lorid

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

commit afe2313c5a3b621d17ab59f875f317c88445d7d2
parent 98628363cde64aef5ce5c14b4a3f92cdf0a5ccc5
Author: nibo <nibo@relim.de>
Date:   Sat,  8 Feb 2025 18:05:19 +0100

Move *.c and *.h into src/ folder

Diffstat:
MMakefile | 2+-
Dchord_diagram.c | 741-------------------------------------------------------------------------------
Dchord_diagram.h | 26--------------------------
Dchordpro.c | 5474-------------------------------------------------------------------------------
Dchordpro.h | 175-------------------------------------------------------------------------------
Dconfig.c | 1065-------------------------------------------------------------------------------
Dconfig.h | 17-----------------
Ddiagrams.h | 4394-------------------------------------------------------------------------------
Dlorid.c | 117-------------------------------------------------------------------------------
Dout_pdf.c | 2819-------------------------------------------------------------------------------
Dout_pdf.h | 103-------------------------------------------------------------------------------
Dtypes.h | 338-------------------------------------------------------------------------------
Dutil.c | 502-------------------------------------------------------------------------------
Dutil.h | 59-----------------------------------------------------------
14 files changed, 1 insertion(+), 15831 deletions(-)

diff --git a/Makefile b/Makefile @@ -6,7 +6,7 @@ VARS = -DVERSION=\"${VERSION}\" -DCOLOR=${COLOR} -DPREFIX=\"${PREFIX}\" CFLAGS = -std=c23 -pedantic -Wall -Wextra LDFLAGS = -lpdfio -ltoml -lfontconfig -lgrapheme -lm STATIC_LDFLAGS = -lpdfio -ltoml -lfontconfig -lgrapheme -lz -L/home/robin/src/libexpat/expat/lib/.libs/ -lexpat -L/home/robin/src/freetype/build/ -lfreetype -L/home/robin/src/libpng-1.6.45/.libs/ -lpng16 -L/home/robin/src/harfbuzz/build/src/ -lharfbuzz -lm -SRC = util.c config.c chordpro.c chord_diagram.c out_pdf.c lorid.c +SRC = src/util.c src/config.c src/chordpro.c src/chord_diagram.c src/out_pdf.c src/lorid.c compile: $(CC) ${CFLAGS} ${VARS} -O2 ${SRC} -o lorid ${LDFLAGS} diff --git a/chord_diagram.c b/chord_diagram.c @@ -1,741 +0,0 @@ -#include <string.h> -#include <ctype.h> -#include <pdfio.h> -#include <pdfio-content.h> -#include "types.h" -#include "out_pdf.h" -#include "util.h" -#include "config.h" -#include "chordpro.h" -#include "chord_diagram.h" -#include "diagrams.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; -} - -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; - for (i = 0; d->frets[i] != -2 && i < 12; i++); - return i; -} - -static void -string_diagram_free(struct StringDiagram *d) -{ - free(d->name); - free(d); -} - -static struct StringDiagram * -string_diagram_copy_all_but_name(struct StringDiagram *diagram) -{ - struct StringDiagram *copy = emalloc(sizeof(struct StringDiagram)); - copy->base_fret = diagram->base_fret; - int i; - for (i = 0; i<12; i++) { - copy->frets[i] = diagram->frets[i]; - } - for (i = 0; i<12; i++) { - copy->fingers[i] = diagram->fingers[i]; - } - return copy; -} - -/* 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 size_t -string_diagram_fret_count(struct StringDiagram *d) -{ - int i; - for (i = 0; d->frets[i] != -2 && i < 12; i++); - return i; -} - -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 -string_diagram_draw( - 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 y_above_diagram = y + height + field_width / 2 + field_width / 2.5; - 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; - } else { - base_pos_x = x - field_width - field_width / 3; - } - if (!pdfioContentSetTextCharacterSpacing(stream, -1.5)) { - fprintf(stderr, "pdfioContentSetTextCharacterSpacing failed.\n"); - return false; - } - 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; - } - } - int8_t fret, finger; - for (i = 0; i<instrument_string_count; i++) { - 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; - } - } - } - pdfio_obj_t *font_obj; - double name_width, centered_x; - font_obj = out_pdf_fnt_obj_get_by_name("chord-diagram-regular-font"); - if (!font_obj) { - LOG_DEBUG("out_pdf_fnt_obj_get_by_name failed."); - return false; - } - name_width = pdfioContentTextMeasure(font_obj, diagram->name, field_width*2.0); - centered_x = (width - name_width) / 2; - if (!pdfioContentSetTextFont(stream, "chord-diagram-regular-font", field_width*2.0)) { - fprintf(stderr, "pdfioContentSetTextFont failed.\n"); - return false; - } - if (!text_show(stream, diagram->name, x+centered_x, y_above_diagram + 7.0)) { - fprintf(stderr, "text_show failed.\n"); - return false; - } - return true; -} - -struct KeyboardDiagram * -keyboard_diagram_new(void) -{ - struct KeyboardDiagram *d = emalloc(sizeof(struct KeyboardDiagram)); - d->name = NULL; - memset(d->keys, -2, sizeof(d->keys)); - return d; -} - -static struct KeyboardDiagram * -keyboard_diagram_copy_all_but_name(struct KeyboardDiagram *diagram) -{ - struct KeyboardDiagram *copy = keyboard_diagram_new(); - memcpy(copy->keys, diagram->keys, 24); - return copy; -} - -static void -keyboard_diagram_free(struct KeyboardDiagram *d) -{ - free(d->name); - free(d); -} - -struct ChordDiagram * -chord_diagram_new() -{ - struct ChordDiagram *d = emalloc(sizeof(struct ChordDiagram)); - d->show = true; - d->color = cho_rgbcolor_new(20, 20, 20); - return d; -} - -void -chord_diagram_free(struct ChordDiagram *d) -{ - free(d->color); - if (d->is_string_instrument) { - string_diagram_free(d->u.sd); - } else { - keyboard_diagram_free(d->u.kd); - } - free(d); -} - -static struct ChordDiagram * -chord_diagram_copy_all_but_name(struct ChordDiagram *diagram) -{ - struct ChordDiagram *copy = emalloc(sizeof(struct ChordDiagram)); - copy->show = diagram->show; - copy->color = cho_color_copy(diagram->color); - copy->is_string_instrument = diagram->is_string_instrument; - if (diagram->is_string_instrument) { - copy->u.sd = string_diagram_copy_all_but_name(diagram->u.sd); - } else { - copy->u.kd = keyboard_diagram_copy_all_but_name(diagram->u.kd); - } - return copy; -} - -enum ChordDiagramContent -chord_diagram_duplicate( - struct ChordDiagram *diagram, - struct ChordDiagram **custom_diagrams, - int custom_diagrams_len, - const char *name, - const char *chord_to_copy, - enum Instrument instrument -) -{ - int d; - for (d = custom_diagrams_len-1; d >= 0; d--) { - if (custom_diagrams[d]->is_string_instrument) { - if (!strcmp(custom_diagrams[d]->u.sd->name, chord_to_copy)) { - diagram->is_string_instrument = true; - diagram->u.sd = string_diagram_copy_all_but_name(custom_diagrams[d]->u.sd); - diagram->u.sd->name = strdup(name); - return CDC_STRING; - } - } else { - if (!strcmp(custom_diagrams[d]->u.kd->name, chord_to_copy)) { - diagram->is_string_instrument = false; - diagram->u.kd = keyboard_diagram_copy_all_but_name(custom_diagrams[d]->u.kd); - diagram->u.kd->name = strdup(name); - return CDC_KEYBOARD; - } - } - } - switch (instrument) { - case INS_GUITAR: { - size_t i; - for (i = 0; i<LENGTH(guitar_diagrams); i++) { - if (!strcmp(guitar_diagrams[i].name, chord_to_copy)) { - diagram->is_string_instrument = true; - diagram->u.sd = string_diagram_copy_all_but_name(&guitar_diagrams[i]); - diagram->u.sd->name = strdup(name); - return CDC_STRING; - } - } - break; - } - case INS_KEYBOARD: { - break; - } - case INS_MANDOLIN: { - break; - } - case INS_UKULELE: { - break; - } - } - return -1; -} - -void -chord_diagrams_free(struct ChordDiagram **diagrams) -{ - if (!diagrams) { - return; - } - struct ChordDiagram **d; - for (d = diagrams; *d; d++) { - chord_diagram_free(*d); - } - free(diagrams); -} - -#ifdef DEBUG -void -debug_chord_diagram_print(struct ChordDiagram *diagram) -{ - int i; - printf("---- CHORD DIAGRAM BEGIN ----\n"); - printf("show: %s\n", diagram->show ? "true" : "false"); - - char str[8]; - str[7] = 0; - sprintf((char *)&str, "#%02X%02X%02X", diagram->color->red, diagram->color->green, diagram->color->blue); - - printf("color: %s\n", str); - - if (diagram->is_string_instrument) { - printf("name: %s\n", diagram->u.sd->name); - printf("base-fret: %d\n", diagram->u.sd->base_fret); - printf("frets: "); - for (i = 0; i<12; i++) { - printf("%d ", diagram->u.sd->frets[i]); - } - printf("\n"); - printf("fingers: "); - for (i = 0; i<12; i++) { - printf("%d ", diagram->u.sd->fingers[i]); - } - printf("\n"); - } else { - printf("name: %s\n", diagram->u.kd->name); - printf("keys: "); - for (i = 0; i<24; i++) { - printf("%d ", diagram->u.kd->keys[i]); - } - printf("\n"); - } - printf("---- CHORD DIAGRAM END ------\n"); -} -#endif /* DEBUG */ - -struct ChordDiagram ** -chord_diagrams_create( - struct Config *config, - struct ChoChord ***chords, - struct ChordDiagram **custom_diagrams -) -{ - struct ChordDiagram **diagrams = NULL; - struct ChordDiagram **cd; - struct ChoChord **c; - int d = 0; - size_t i; - switch (config->output->diagram->instrument) { - case INS_GUITAR: - for (c = *chords; *c; c++) { - for (cd = custom_diagrams; *cd; cd++) { - if ( - (*cd)->is_string_instrument && - string_diagram_fret_count((*cd)->u.sd) == 6 && - !strcmp((*c)->name, (*cd)->u.sd->name) - ) { - diagrams = erealloc(diagrams, (d+1) * sizeof(struct ChordDiagram *)); - diagrams[d] = chord_diagram_copy_all_but_name(*cd); - diagrams[d]->u.sd->name = cho_chord_name_generate(*c); - printf("custom: name '%s'\n", diagrams[d]->u.sd->name); - d++; - goto NEXT_CHORD; - } - } - for (i = 0; i<LENGTH(guitar_diagrams); i++) { - if (!strcmp((*c)->name, guitar_diagrams[i].name)) { - diagrams = erealloc(diagrams, (d+1) * sizeof(struct ChordDiagram *)); - diagrams[d] = chord_diagram_new(); - diagrams[d]->is_string_instrument = true; - diagrams[d]->u.sd = string_diagram_copy_all_but_name(&guitar_diagrams[i]); - diagrams[d]->u.sd->name = cho_chord_name_generate(*c); - printf("predefined: name '%s'\n", diagrams[d]->u.sd->name); - d++; - } - } - NEXT_CHORD: ; - } - break; - case INS_KEYBOARD: - break; - case INS_MANDOLIN: - break; - case INS_UKULELE: - break; - default: - util_log(LOG_ERR, "Invalid Instrument enum value '%d'.", config->output->diagram->instrument); - } - diagrams = erealloc(diagrams, (d+1) * sizeof(struct ChordDiagram *)); - diagrams[d] = NULL; - return diagrams; -} - -bool -chord_diagram_draw( - pdfio_stream_t *stream, - struct ChordDiagram *diagram, - double x, - double y, - double width -) -{ - if (diagram->is_string_instrument) { - if (!string_diagram_draw(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 @@ -1,26 +0,0 @@ -#include <pdfio.h> -#include "types.h" - -enum TextRendering { - FILL, - STROKE, - FILL_AND_STROKE -}; - -enum Direction { - HORIZONTAL, - VERTICAL -}; - -struct ChordDiagram *chord_diagram_new(); -void chord_diagram_free(struct ChordDiagram *d); -bool chord_diagram_draw(pdfio_stream_t *stream, struct ChordDiagram *diagram, double x, double y, double width); -enum ChordDiagramContent chord_diagram_duplicate(struct ChordDiagram *diagram, struct ChordDiagram **custom_diagrams, int custom_diagrams_len, const char *name, const char *chord_to_copy, enum Instrument instrument); - -struct StringDiagram *string_diagram_new(void); -struct KeyboardDiagram *keyboard_diagram_new(void); - -void chord_diagrams_free(struct ChordDiagram **diagrams); -struct ChordDiagram **chord_diagrams_create(struct Config *config, struct ChoChord ***chords, struct ChordDiagram **custom_diagram); - -void debug_chord_diagram_print(struct ChordDiagram *diagram); diff --git a/chordpro.c b/chordpro.c @@ -1,5474 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <stdbool.h> -#include <stdarg.h> -#include <unistd.h> -#include <string.h> -#include <strings.h> -#include <ctype.h> -#include <errno.h> -#include <limits.h> -#include "types.h" -#include "chordpro.h" -#include "util.h" -#include "chord_diagram.h" -#include "config.h" - -static const char *font_families[] = { "normal", "sans", "serif", "monospace" }; -static const char *font_styles[] = { "normal", "oblique", "italic" }; -static const char *font_weights[] = { "normal", "bold" }; -static const char *line_styles[] = { "single", "double", "none" }; -static const char *chord_qualifiers[] = { "m", "", "+", "°" }; -static const char *instruments[] = { "guitar", "keyboard", "mandolin", "ukulele" }; - -static const char *state_enums[] = { - "STATE_LYRICS", - "STATE_BACKSLASH", - "STATE_DIRECTIVE_NAME", - "STATE_DIRECTIVE_VALUE", - "STATE_CHORD", - "STATE_ANNOTATION", - "STATE_TAB", - "STATE_MARKUP_TAG", - "STATE_MARKUP_TAG_START", - "STATE_MARKUP_TAG_END", - "STATE_MARKUP_ATTR_NAME", - "STATE_MARKUP_ATTR_VALUE", - "STATE_COMMENT" -}; - -struct StyleProperty default_style_properties[] = { - // TODO: label and footer are missing - { TT_CHORD, SPT_FONT, { .font_name = NULL } }, - { TT_CHORD, SPT_SIZE, { .font_size = EMPTY_DOUBLE } }, - { TT_CHORD, SPT_COLOR, { .foreground_color = NULL } }, - { TT_CHORUS, SPT_FONT, { .font_name = NULL } }, - { TT_CHORUS, SPT_SIZE, { .font_size = EMPTY_DOUBLE } }, - { TT_CHORUS, SPT_COLOR, { .foreground_color = NULL } }, - { TT_GRID, SPT_FONT, { .font_name = NULL } }, - { TT_GRID, SPT_SIZE, { .font_size = EMPTY_DOUBLE } }, - { TT_GRID, SPT_COLOR, { .foreground_color = NULL } }, - { TT_TAB, SPT_FONT, { .font_name = NULL } }, - { TT_TAB, SPT_SIZE, { .font_size = EMPTY_DOUBLE } }, - { TT_TAB, SPT_COLOR, { .foreground_color = NULL } }, - { TT_TEXT, SPT_FONT, { .font_name = NULL } }, - { TT_TEXT, SPT_SIZE, { .font_size = EMPTY_DOUBLE } }, - { TT_TEXT, SPT_COLOR, { .foreground_color = NULL } }, - { TT_TITLE, SPT_FONT, { .font_name = NULL } }, - { TT_TITLE, SPT_SIZE, { .font_size = EMPTY_DOUBLE } }, - { TT_TITLE, SPT_COLOR, { .foreground_color = NULL } }, - { TT_TOC, SPT_FONT, { .font_name = NULL } }, - { TT_TOC, SPT_SIZE, { .font_size = EMPTY_DOUBLE } }, - { TT_TOC, SPT_COLOR, { .foreground_color = NULL } }, -}; - -static const char *chord_extensions_major[] = { - "2", "3", "4", "5", "6", "69", "7", "7-5", - "7#5", "7#9", "7#9#5", "7#9b5", "7#9#11", - "7b5", "7b9", "7b9#5", "7b9#9", "7b9#11", "7b9b13", "7b9b5", "7b9sus", "7b13", "7b13sus", - "7-9", "7-9#11", "7-9#5", "7-9#9", "7-9-13", "7-9-5", "7-9sus", - "711", - "7#11", - "7-13", "7-13sus", - "7sus", "7susadd3", - "7+", - "7alt", - "9", - "9+", - "9#5", - "9b5", - "9-5", - "9sus", - "9add6", - "maj7", "maj711", "maj7#11", "maj13", "maj7#5", "maj7sus2", "maj7sus4", - "^7", "^711", "^7#11", "^7#5", "^7sus2", "^7sus4", - "maj9", "maj911", - "^9", "^911", - "^13", - "^9#11", - "11", - "911", - "9#11", - "13", - "13#11", - "13#9", - "13b9", - "alt", - "add2", "add4", "add9", - "sus2", "sus4", "sus9", - "6sus2", "6sus4", - "7sus2", "7sus4", - "13sus2", "13sus4", - NULL -}; - -static const char *chord_extensions_minor[] = { - // INFO: https://www.chordpro.org/beta/chordpro-chords/#extensions-for-minor-chords - "#5", - "#5", - "m11", - "-11", - "m6", - "-6", - "m69", - "-69", - "m7b5", - "-7b5", - "m7-5", - "-7-5", - "mmaj7", - "-maj7", - "mmaj9", - "-maj9", - "m9maj7", - "-9maj7", - "m9^7", - "-9^7", - "madd9", - "-add9", - "mb6", - "-b6", - "m#7", - "-#7", - "msus4", "msus9", - "-sus4", "-sus9", - "m7sus4", - "-7sus4", - // INFO: custom - "m7", - "m9", - "m", - NULL -}; - -static bool g_show_info_logs = false; -static enum TextType g_current_ttype = TT_TEXT; -static enum TextType g_prev_ttype = TT_TEXT; -static struct Config *g_config = NULL; -static const char *g_chordpro_filepath = NULL; -static int *g_transpose_history = NULL; -static int *g_transpose = NULL; -static size_t g_line_number = 1; -struct ChoImage **g_image_assets = NULL; -static int g_ia = 0; - -#ifdef DEBUG - -static const char *alignment_enums[] = { - "A_LEFT", - "A_CENTER", - "A_RIGHT" -}; - -static const char *anchor_enums[] = { - "AN_PAPER", - "AN_PAGE", - "AN_COLUMN", - "AN_LINE", - "AN_FLOAT" -}; - -static const char *chord_qualifier_enums[] = { - "CQ_MIN", - "CQ_MAJ", - "CQ_AUG", - "CQ_DIM" -}; - -static const char *directive_type_enums[] = { - "DT_ENVIRONMENT", - "DT_METADATA", - "DT_FORMATTING", - "DT_IMAGE", - "DT_PREAMBLE", - "DT_FONT", - "DT_CHORD", - "DT_OUTPUT", - "DT_EXTENSION", - "DT_CUSTOM" -}; - -static const char *section_type_enums[] = { - "ST_NEWSONG", - "ST_CHORUS", - "ST_VERSE", - "ST_BRIDGE", - "ST_TAB", - "ST_GRID", - "ST_CUSTOM" -}; - -static const char *position_enums[] = { - "POS_START", - "POS_END", - "POS_NO" -}; - -static const char *text_type_enums[] = { - "TT_CHORD", - "TT_ANNOT", - "TT_CHORUS", - "TT_FOOTER", - "TT_GRID", - "TT_TAB", - "TT_TOC", - "TT_TOC_TITLE", - "TT_TEXT", - "TT_TITLE", - "TT_SUBTITLE", - "TT_LABEL", - "TT_COMMENT", - "TT_COMMENT_ITALIC", - "TT_COMMENT_BOX" -}; - -static const char *style_property_type_enums[] = { - "SPT_FONT", - "SPT_SIZE", - "SPT_COLOR" -}; - -static void -cho_debug_chord_print(struct ChoChord *chord) -{ - printf("---- BEGIN CHORD ----\n"); - printf("is_canonical: %d\n", chord->is_canonical); - printf("name: '%s'\n", chord->name); - if (chord->root) { - printf("root: '%s'\n", chord->root); - } else { - printf("root: 'NULL'\n"); - } - printf("qual: '%s'\n", chord_qualifier_enums[chord->qual]); - if (chord->ext) { - printf("ext: '%s'\n", chord->ext); - } else { - printf("ext: 'NULL'\n"); - } - if (chord->bass) { - printf("bass: '%s'\n", chord->bass); - } else { - printf("bass: 'NULL'\n"); - } - printf("---- END CHORD ------\n"); -} - -#endif /* DEBUG */ - -void -cho_log_enable_info_logs(void) -{ - g_show_info_logs = true; -} - -#if COLOR == 1 -static void -cho_log(enum LogLevel level, const char *msg, ...) -{ - if (level == LOG_INFO && !g_show_info_logs) { - return; - } - va_list va; - va_start(va, msg); - const char *log_level; - const char *color; - switch (level) { - case LOG_INFO: - log_level = "INFO"; - color = COLOR_BOLD_WHITE; - break; - case LOG_WARN: - log_level = "WARN"; - color = COLOR_BOLD_ORANGE; - break; - case LOG_ERR: - 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) { - fprintf(stderr, COLOR_BOLD_WHITE"%s:%ld:"COLOR_RESET" %s%s"COLOR_RESET": ", - g_chordpro_filepath, g_line_number, color, log_level); - vfprintf(stderr, msg, va); - fprintf(stderr, "\n"); - } else { - fprintf(stderr, "line "COLOR_BOLD_WHITE"%ld:"COLOR_RESET" %s%s"COLOR_RESET": ", - g_line_number, color, log_level); - vfprintf(stderr, msg, va); - fprintf(stderr, "\n"); - } - } else { - if (g_chordpro_filepath) { - fprintf(stderr, "%s:%ld: %s: ", g_chordpro_filepath, g_line_number, - log_level); - vfprintf(stderr, msg, va); - fprintf(stderr, "\n"); - } else { - fprintf(stderr, "line %ld: %s: ", g_line_number, log_level); - vfprintf(stderr, msg, va); - fprintf(stderr, "\n"); - } - } -} -#else -static void -cho_log(enum LogLevel level, const char *msg, ...) -{ - if (level == LOG_INFO && !g_show_info_logs) { - return; - } - va_list va; - va_start(va, msg); - const char *log_level; - switch (level) { - case LOG_INFO: - log_level = "INFO"; - break; - case LOG_WARN: - log_level = "WARN"; - break; - 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, - log_level); - vfprintf(stderr, msg, va); - fprintf(stderr, "\n"); - } else { - fprintf(stderr, "line %ld: %s: ", g_line_number, log_level); - vfprintf(stderr, msg, va); - fprintf(stderr, "\n"); - } -} -#endif /* COLOR */ - -static inline bool -is_whitespace(char c) -{ - if (c == '\t' || c == ' ') { - return true; - } - return false; -} - -struct RGBColor * -cho_rgbcolor_new(uint8_t red, uint8_t green, uint8_t blue) -{ - struct RGBColor *color = emalloc(sizeof(struct RGBColor)); - color->red = red; - color->green = green; - color->blue = blue; - return color; -} - -static struct RGBColor * -cho_rgbcolor_copy(struct RGBColor *color) -{ - struct RGBColor *copy = emalloc(sizeof(struct RGBColor)); - copy->red = color->red; - copy->green = color->green; - copy->blue = color->blue; - return copy; -} - -static const char * -cho_rgbcolor_to_string(struct RGBColor *color) -{ - static char str[8]; - str[7] = 0; - sprintf((char *)&str, "#%02X%02X%02X", color->red, color->green, color->blue); - return (const char *)&str; -} - -static struct RGBColor * -cho_rgbcolor_parse(const char *str) -{ - struct RGBColor *color = emalloc(sizeof(struct RGBColor)); - size_t len = strlen(str); - char tmp[3]; - long primary_color; - if (len == 7) { - tmp[2] = 0; - if (str[1] == '0' && str[2] == '0') { - color->red = 0; - } else { - tmp[0] = str[1]; - tmp[1] = str[2]; - primary_color = strtol((char *)&tmp, NULL, 16); - if (primary_color == 0) { - cho_log(LOG_ERR, "Invalid primary color in rgb color."); - free(color); - return NULL; - } else { - color->red = primary_color; - } - } - if (str[3] == '0' && str[4] == '0') { - color->green = 0; - } else { - tmp[0] = str[3]; - tmp[1] = str[4]; - primary_color = strtol((char *)&tmp, NULL, 16); - if (primary_color == 0) { - cho_log(LOG_ERR, "Invalid primary color in rgb color."); - free(color); - return NULL; - } else { - color->green = primary_color; - } - } - if (str[5] == '0' && str[6] == '0') { - color->blue = 0; - } else { - tmp[0] = str[5]; - tmp[1] = str[6]; - primary_color = strtol((char *)&tmp, NULL, 16); - if (primary_color == 0) { - cho_log(LOG_ERR, "Invalid primary color in rgb color."); - free(color); - return NULL; - } else { - color->blue = primary_color; - } - } - } else if (len == 4) { - tmp[2] = 0; - if (str[1] == '0') { - color->red = 0; - } else { - tmp[0] = str[1]; - tmp[1] = str[1]; - primary_color = strtol((char *)&tmp, NULL, 16); - if (primary_color == 0) { - cho_log(LOG_ERR, "Invalid primary color in rgb color."); - free(color); - return NULL; - } else { - color->red = primary_color; - } - } - if (str[2] == '0') { - color->green = 0; - } else { - tmp[0] = str[2]; - tmp[1] = str[2]; - primary_color = strtol((char *)&tmp, NULL, 16); - if (primary_color == 0) { - cho_log(LOG_ERR, "Invalid primary color in rgb color."); - free(color); - return NULL; - } else { - color->green = primary_color; - } - } - if (str[3] == '0') { - color->blue = 0; - } else { - tmp[0] = str[3]; - tmp[1] = str[3]; - primary_color = strtol((char *)&tmp, NULL, 16); - if (primary_color == 0) { - cho_log(LOG_ERR, "Invalid primary color in rgb color."); - free(color); - return NULL; - } else { - color->blue = primary_color; - } - } - } else { - cho_log(LOG_ERR, "Invalid rgb color."); - free(color); - return NULL; - } - return color; -} - -struct RGBColor * -cho_color_parse(const char *str) -{ - struct RGBColor *color = emalloc(sizeof(struct RGBColor)); - if (str[0] == '#') { - struct RGBColor *rgb_color = cho_rgbcolor_parse(str); - if (rgb_color) { - free(color); - color = rgb_color; - } else { - free(color); - LOG_DEBUG("cho_rgbcolor_parse failed."); - return NULL; - } - } else if (!strcmp(str, "red")) { - color->red = 255; - color->green = 48; - color->blue = 48; - } else if (!strcmp(str, "green")) { - color->red = 0; - color->green = 126; - color->blue = 0; - } else if (!strcmp(str, "blue")) { - color->red = 0; - color->green = 0; - color->blue = 255; - } else if (!strcmp(str, "yellow")) { - color->red = 255; - color->green = 255; - color->blue = 0; - } else if (!strcmp(str, "magenta")) { - color->red = 255; - color->green = 48; - color->blue = 255; - } else if (!strcmp(str, "cyan")) { - color->red = 82; - color->green = 255; - color->blue = 255; - } else if (!strcmp(str, "white")) { - color->red = 255; - color->green = 255; - color->blue = 255; - } else if (!strcmp(str, "grey")) { - color->red = 191; - color->green = 191; - color->blue = 191; - } else if (!strcmp(str, "black")) { - color->red = 0; - color->green = 0; - color->blue = 0; - } else { - cho_log(LOG_ERR, "Invalid color value '%s'.", str); - free(color); - return NULL; - } - return color; -} - -inline struct RGBColor * -cho_color_copy(struct RGBColor *color) -{ - return cho_rgbcolor_copy(color); -} - -static struct Font * -cho_font_new(void) -{ - struct Font *font = emalloc(sizeof(struct Font)); - font->name = NULL; - font->family = FF_NORMAL; - font->style = FS_ROMAN; - font->weight = FW_REGULAR; - font->size = DEFAULT_FONT_SIZE; - return font; -} - -struct Font * -cho_font_copy(struct Font *font) -{ - struct Font *copy = emalloc(sizeof(struct Font)); - if (font->name) - copy->name = strdup(font->name); - else - copy->name = NULL; - copy->family = font->family; - copy->style = font->style; - copy->weight = font->weight; - copy->size = font->size; - return copy; -} - -void -cho_font_free(struct Font *font) -{ - if (!font) { - return; - } - free(font->name); - free(font); -} - -void -cho_fonts_free(struct Font **fonts) -{ - if (!fonts) { - return; - } - struct Font **f; - for (f = fonts; *f; f++) { - cho_font_free(*f); - } - free(fonts); -} - -enum FontFamily -cho_font_family_parse(const char *str) -{ - if (!strcmp(str, font_families[FF_SANS])) { - return FF_SANS; - } else if (!strcmp(str, font_families[FF_SERIF])) { - return FF_SERIF; - } else if (!strcmp(str, font_families[FF_MONOSPACE])) { - return FF_MONOSPACE; - } else if (!strcmp(str, font_families[FF_NORMAL])) { - return FF_NORMAL; - } else { - return -1; - } -} - -const char * -cho_font_family_to_config_string(enum FontFamily font_family) -{ - return font_families[font_family]; -} - -enum FontStyle -cho_font_style_parse(const char *str) -{ - if (!strcmp(str, font_styles[FS_ITALIC])) { - return FS_ITALIC; - } else if (!strcmp(str, font_styles[FS_OBLIQUE])) { - return FS_OBLIQUE; - } else if (!strcmp(str, font_styles[FS_ROMAN])) { - return FS_ROMAN; - } else { - return -1; - } -} - -const char * -cho_font_style_to_config_string(enum FontStyle style) -{ - return font_styles[style]; -} - -enum FontWeight -cho_font_weight_parse(const char *str) -{ - if (!strcmp(str, "bold")) { - return FW_BOLD; - } else if (!strcmp(str, "normal")) { - return FW_REGULAR; - } else { - return -1; - } -} - -const char * -cho_font_weight_to_config_string(enum FontWeight weight) -{ - return font_weights[weight]; -} - -static void -cho_font_print_as_toml(struct Font *font, const char *section) -{ - printf("[output.styles.%s.font]\n", section); - printf("name = \"%s\"\n", font->name); - printf("family = \"%s\"\n", cho_font_family_to_config_string(font->family)); - printf("style = \"%s\"\n", cho_font_style_to_config_string(font->style)); - printf("weight = \"%s\"\n", cho_font_weight_to_config_string(font->weight)); - printf("size = %.1f\n\n", font->size); -} - -void -cho_font_print(struct Font *font) -{ - printf("---- BEGIN FONT ----\n"); - if (font->name) - printf("font name: %s\n", font->name); - else - printf("font name: NULL\n"); - printf("font family: %s\n", cho_font_family_to_config_string(font->family)); - printf("font style: %s\n", cho_font_style_to_config_string(font->style)); - printf("font weight: %s\n", cho_font_weight_to_config_string(font->weight)); - printf("font size: %f\n", font->size); - printf("---- END FONT ------\n"); -} - -enum LineStyle -cho_linestyle_parse(const char *str) -{ - if (!strcmp(str, "single")) { - return LS_SINGLE; - } else if (!strcmp(str, "double")) { - return LS_DOUBLE; - } else if (!strcmp(str, "none")) { - return LS_NONE; - } else { - return -1; - } -} - -const char * -cho_linestyle_to_config_string(enum LineStyle linestyle) -{ - return line_styles[linestyle]; -} - -#ifdef DEBUG - -static void -cho_debug_the_default_style_properties(void) -{ - unsigned int i; - for (i = 0; i<LENGTH(default_style_properties); i++) { - printf( - "%s %s ", - text_type_enums[default_style_properties[i].ttype], - style_property_type_enums[default_style_properties[i].type] - ); - switch (default_style_properties[i].type) { - case SPT_FONT: - if (default_style_properties[i].u.font_name) - printf("%s\n", default_style_properties[i].u.font_name); - else - printf("NULL\n"); - break; - case SPT_SIZE: - printf("%.1f\n", default_style_properties[i].u.font_size); - break; - case SPT_COLOR: - if (default_style_properties[i].u.foreground_color) - printf("%s\n", cho_rgbcolor_to_string(default_style_properties[i].u.foreground_color)); - else - printf("NULL\n"); - break; - default: - printf("Invalid StylePropertyType value '%d'.\n", default_style_properties[i].type); - } - } -} - -void -cho_debug_style_print(struct ChoStyle *style) -{ - printf("---- BEGIN STYLE ----\n"); - cho_font_print(style->font); - printf("foreground_color: %s\n", cho_rgbcolor_to_string(style->foreground_color)); - printf("background_color: %s\n", cho_rgbcolor_to_string(style->background_color)); - printf("underline_style: %s\n", cho_linestyle_to_config_string(style->underline_style)); - printf("underline_color: %s\n", cho_rgbcolor_to_string(style->underline_color)); - printf("overline_style: %s\n", cho_linestyle_to_config_string(style->overline_style)); - printf("overline_color: %s\n", cho_rgbcolor_to_string(style->overline_color)); - printf("strikethrough: %d\n", style->strikethrough); - printf("strikethrough_color: %s\n", cho_rgbcolor_to_string(style->strikethrough_color)); - printf("boxed: %d\n", style->boxed); - printf("boxed_color: %s\n", cho_rgbcolor_to_string(style->boxed_color)); - printf("rise: %f\n", style->rise); - if (style->href) - printf("href: %s\n", style->href); - else - printf("href: NULL\n"); - printf("---- END STYLE ------\n\n"); -} - -#endif /* DEBUG */ - -static bool -cho_style_property_apply_default(enum TextType current_ttype, enum StylePropertyType ptype, struct ChoStyle *style) -{ - unsigned int i; - for (i = 0; i<LENGTH(default_style_properties); i++) { - if ( - default_style_properties[i].ttype == current_ttype && - default_style_properties[i].type == ptype - ) { - switch (ptype) { - case SPT_FONT: - if (default_style_properties[i].u.font_name) { - free(style->font->name); - style->font->name = strdup(default_style_properties[i].u.font_name); - return true; - } else { - return false; - } - break; - case SPT_SIZE: - if (default_style_properties[i].u.font_size != EMPTY_DOUBLE) { - style->font->size = default_style_properties[i].u.font_size; - return true; - } else { - return false; - } - break; - case SPT_COLOR: - if (default_style_properties[i].u.foreground_color) { - free(style->foreground_color); - style->foreground_color = cho_rgbcolor_copy(default_style_properties[i].u.foreground_color); - return true; - } else { - return false; - } - break; - default: - cho_log(LOG_WARN, "Invalid style property type '%d'.", ptype); - return false; - } - } - } - return false; -} - -static void -cho_style_apply_default(enum TextType current_ttype, struct ChoStyle *style) -{ - if (current_ttype == TT_CHORUS) { - if (!cho_style_property_apply_default(TT_CHORUS, SPT_FONT, style)) { - cho_style_property_apply_default(TT_TEXT, SPT_FONT, style); - } - if (!cho_style_property_apply_default(TT_CHORUS, SPT_SIZE, style)) { - cho_style_property_apply_default(TT_TEXT, SPT_SIZE, style); - } - if (!cho_style_property_apply_default(TT_CHORUS, SPT_COLOR, style)) { - cho_style_property_apply_default(TT_TEXT, SPT_COLOR, style); - } - } else { - cho_style_property_apply_default(current_ttype, SPT_FONT, style); - cho_style_property_apply_default(current_ttype, SPT_SIZE, style); - cho_style_property_apply_default(current_ttype, SPT_COLOR, style); - } -} - -struct ChoStyle * -cho_style_new(void) -{ - struct ChoStyle *style = emalloc(sizeof(struct ChoStyle)); - style->font = cho_font_new(); - style->foreground_color = cho_rgbcolor_new(20, 20, 20); - style->background_color = cho_rgbcolor_new(255, 255, 255); - style->underline_style = LS_NONE; - style->underline_color = cho_rgbcolor_new(20, 20, 20); - style->overline_style = LS_NONE; - style->overline_color = cho_rgbcolor_new(20, 20, 20); - style->strikethrough = false; - style->strikethrough_color = cho_rgbcolor_new(20, 20, 20); - style->boxed = false; - style->boxed_color = cho_rgbcolor_new(20, 20, 20); - style->rise = 0.0; - style->href = NULL; - return style; -} - -struct ChoStyle * -cho_style_copy(struct ChoStyle *style) -{ - struct ChoStyle *copy = emalloc(sizeof(struct ChoStyle)); - copy->font = cho_font_copy(style->font); - copy->foreground_color = cho_rgbcolor_copy(style->foreground_color); - copy->background_color = cho_rgbcolor_copy(style->background_color); - copy->underline_style = style->underline_style; - copy->underline_color = cho_rgbcolor_copy(style->underline_color); - copy->overline_style = style->overline_style; - copy->overline_color = cho_rgbcolor_copy(style->overline_color); - copy->strikethrough = style->strikethrough; - copy->strikethrough_color = cho_rgbcolor_copy(style->strikethrough_color); - copy->boxed = style->boxed; - copy->boxed_color = cho_rgbcolor_copy(style->boxed_color); - copy->rise = style->rise; - copy->href = style->href ? strdup(style->href) : NULL; - return copy; -} - -static void -cho_style_complement( - struct ChoStyle *old, - struct ChoStyle *new, - struct ChoStylePresence *presence -) -{ - if (presence->font.name) { - free(old->font->name); - old->font->name = strdup(new->font->name); - } - if (presence->font.family) { - old->font->family = new->font->family; - } - if (presence->font.style) { - old->font->style = new->font->style; - } - if (presence->font.weight) { - old->font->weight = new->font->weight; - } - if (presence->font.size) { - old->font->size = new->font->size; - } - if (presence->foreground_color) { - free(old->foreground_color); - old->foreground_color = cho_rgbcolor_copy(new->foreground_color); - } - if (presence->background_color) { - free(old->background_color); - old->background_color = cho_rgbcolor_copy(new->background_color); - } - if (presence->underline_style) { - old->underline_style = new->underline_style; - } - if (presence->underline_color) { - free(old->underline_color); - old->underline_color = cho_rgbcolor_copy(new->underline_color); - } - if (presence->overline_style) { - old->overline_style = new->overline_style; - } - if (presence->overline_color) { - free(old->overline_color); - old->overline_color = cho_rgbcolor_copy(new->overline_color); - } - if (presence->strikethrough) { - old->strikethrough = new->strikethrough; - } - if (presence->strikethrough_color) { - free(old->strikethrough_color); - old->strikethrough_color = cho_rgbcolor_copy(new->strikethrough_color); - } - if (presence->boxed) { - old->boxed = new->boxed; - } - if (presence->boxed_color) { - free(old->boxed_color); - old->boxed_color = cho_rgbcolor_copy(new->boxed_color); - } - if (presence->rise) { - old->rise = new->rise; - } - if (presence->href) { - free(old->href); - old->href = strdup(new->href); - } -} - -static struct ChoStyle * -cho_style_new_from_config(enum TextType ttype) -{ - return cho_style_copy(g_config->output->styles[ttype]); -} - -static struct ChoStyle * -cho_style_new_default(void) -{ - struct ChoStyle *style = cho_style_new_from_config(g_current_ttype); - if (!style) { - LOG_DEBUG("cho_style_new_from_config failed."); - return NULL; - } - cho_style_apply_default(g_current_ttype, style); - return style; -} - -void -cho_style_free(struct ChoStyle *style) -{ - if (!style) { - return; - } - cho_font_free(style->font); - free(style->foreground_color); - free(style->background_color); - free(style->underline_color); - free(style->overline_color); - free(style->strikethrough_color); - free(style->boxed_color); - free(style->href); - free(style); -} - -static struct Font * -cho_style_font_desc_parse(const char *str, struct ChoStylePresence *presence) -{ - if (strlen(str) == 0) { - return NULL; - } - struct Font *font = cho_font_new(); - double size = -1.0; - char **words = emalloc(sizeof(char *)); - int w = 0; - words[w] = NULL; - int k = 0; - int i; - for (i = 0; str[i]; i++) { - if (str[i] == ' ') { - words[w] = erealloc(words[w], (k+1) * sizeof(char)); - words[w][k] = 0; - if (strlen(words[w]) == 0) { - free(words[w]); - } else { - w++; - } - k = 0; - words = erealloc(words, (w+1) * sizeof(char *)); - words[w] = NULL; - } else { - words[w] = erealloc(words[w], (k+1) * sizeof(char)); - words[w][k] = str[i]; - k++; - } - } - words[w] = erealloc(words[w], (k+1) * sizeof(char)); - words[w][k] = 0; - w++; - words = erealloc(words, (w+1) * sizeof(char *)); - words[w] = NULL; - int stop_at = EMPTY_INT; - for (w = 0; words[w]; w++) { - if (strcasecmp(words[w], "italic") == 0) { - font->style = FS_ITALIC; - presence->font.style = true; - if (stop_at == EMPTY_INT) { - stop_at = w; - } - } else if (strcasecmp(words[w], "bold") == 0) { - font->weight = FW_BOLD; - presence->font.weight = true; - if (stop_at == EMPTY_INT) { - stop_at = w; - } - } else if (strcasecmp(words[w], "oblique") == 0) { - font->style = FS_OBLIQUE; - presence->font.style = true; - if (stop_at == EMPTY_INT) { - stop_at = w; - } - // TODO: Is that smart? - } else if (strcasecmp(words[w], "regular") == 0) { - font->weight = FW_REGULAR; - presence->font.weight = true; - if (stop_at == EMPTY_INT) { - stop_at = w; - } - // TODO: Is that smart? - } else if (strcasecmp(words[w], "normal") == 0) { - font->style = FS_ROMAN; - presence->font.style = true; - if (stop_at == EMPTY_INT) { - stop_at = w; - } - /* Commented because the family name sometimes contains 'sans' or 'serif' */ - /* } else if (strcasecmp(words[w], "sans") == 0) { - font->family = FF_SANS; - if (stop_at == EMPTY_INT) - stop_at = w; - } else if (strcasecmp(words[w], "serif") == 0) { - font->family = FF_SERIF; - if (stop_at == EMPTY_INT) - stop_at = w; */ - } else if (strcasecmp(words[w], "monospace") == 0) { - font->family = FF_MONOSPACE; - presence->font.family = true; - if (stop_at == EMPTY_INT) { - stop_at = w; - } - } else { - size = strtod(words[w], NULL); - if (size == 0.0) { - continue; - } - font->size = size; - presence->font.size = true; - if (stop_at == EMPTY_INT) { - stop_at = w; - } - } - } - if (stop_at == EMPTY_INT) { - stop_at = w; - } - if (stop_at > 0) { - int n = 0; - for (i = 0; i<stop_at; i++) { - for (k = 0; words[i][k]; n++, k++) { - font->name = erealloc(font->name, (n+1) * sizeof(char)); - font->name[n] = words[i][k]; - } - font->name = erealloc(font->name, (n+1) * sizeof(char)); - font->name[n] = ' '; - n++; - } - n--; - font->name[n] = 0; - presence->font.name = true; - } - for (w = 0; words[w]; w++) { - free(words[w]); - } - free(words); - return font; -} - -static struct ChoStyle * -cho_style_parse( - const char *tag_name, - struct Attr **attrs, - struct ChoStyle *inherited_style, - struct ChoStylePresence *presence -) -{ - size_t value_len, last_char; - struct RGBColor *rgb_color; - struct ChoStyle *style; - struct Font *font; - if (inherited_style) { - style = cho_style_copy(inherited_style); - } else { - style = cho_style_new_default(); - } - if (!strcmp(tag_name, "span")) { - int a; - for (a = 0; attrs[a]; a++) { - if (!strcmp(attrs[a]->name, "font_desc")) { - font = cho_style_font_desc_parse(attrs[a]->value, presence); - if (font) { - if (!font->name) { - font->name = strdup(style->font->name); - } - cho_font_free(style->font); - style->font = font; - } - } else if ( - !strcmp(attrs[a]->name, "font_family") || - !strcmp(attrs[a]->name, "face") - ) { - if (!strcmp(attrs[a]->value, "normal")) { - style->font->family = FF_NORMAL; - presence->font.family = true; - } else if (!strcmp(attrs[a]->value, "sans")) { - style->font->family = FF_SANS; - presence->font.family = true; - } else if (!strcmp(attrs[a]->value, "serif")) { - style->font->family = FF_SERIF; - presence->font.family = true; - } else if (!strcmp(attrs[a]->value, "monospace")) { - style->font->family = FF_MONOSPACE; - presence->font.family = true; - } else { - cho_log(LOG_ERR, "Invalid value in attribute 'font_family/face'."); - return NULL; - } - } else if (!strcmp(attrs[a]->name, "size")) { - value_len = strlen(attrs[a]->value); - last_char = value_len - 1; - if (attrs[a]->value[last_char] == '%') { - if (value_len < 5) { - attrs[a]->value[last_char] = 0; - int percentage = atoi(attrs[a]->value); - if (percentage != 0 && percentage <= 100) { - style->font->size *= percentage / 100.0; - presence->font.size = true; - } else { - cho_log(LOG_ERR, "Invalid percentage in attribute 'size'."); - return NULL; - } - } else { - cho_log(LOG_ERR, "Invalid percentage in attribute 'size'."); - return NULL; - } - } else if (isdigit(attrs[a]->value[0]) != 0) { - double size = strtod(attrs[a]->value, NULL); - if (size != 0.0) { - style->font->size = size; - presence->font.size = true; - } else { - cho_log(LOG_ERR, "Invalid number in attribute 'size'."); - return NULL; - } - } else if (!strcmp(attrs[a]->value, "xx-small")) { - style->font->size *= 0.8; - style->font->size *= 0.8; - style->font->size *= 0.8; - presence->font.size = true; - } else if (!strcmp(attrs[a]->value, "x-small")) { - style->font->size *= 0.8; - style->font->size *= 0.8; - presence->font.size = true; - } else if (!strcmp(attrs[a]->value, "small")) { - style->font->size *= 0.8; - presence->font.size = true; - // } else if (!strcmp(attrs[a]->value, "medium")) { - } else if (!strcmp(attrs[a]->value, "large")) { - style->font->size *= 1.8; - presence->font.size = true; - } else if (!strcmp(attrs[a]->value, "x-large")) { - style->font->size *= 1.8; - style->font->size *= 1.8; - presence->font.size = true; - } else if (!strcmp(attrs[a]->value, "xx-large")) { - style->font->size *= 1.8; - style->font->size *= 1.8; - style->font->size *= 1.8; - presence->font.size = true; - } else if (!strcmp(attrs[a]->value, "larger")) { - style->font->size *= 1.8; - presence->font.size = true; - } else if (!strcmp(attrs[a]->value, "smaller")) { - style->font->size *= 0.8; - presence->font.size = true; - } else { - cho_log(LOG_ERR, "Invalid value '%s' for the attribute 'size'.", attrs[a]->value); - return NULL; - } - } else if (!strcmp(attrs[a]->name, "style")) { - if (!strcmp(attrs[a]->value, "normal")) { - style->font->style = FS_ROMAN; - presence->font.style = true; - } else if (!strcmp(attrs[a]->value, "oblique")) { - style->font->style = FS_OBLIQUE; - presence->font.style = true; - } else if (!strcmp(attrs[a]->value, "italic")) { - style->font->style = FS_ITALIC; - presence->font.style = true; - } else { - cho_log(LOG_ERR, "Invalid value in attribute 'style'."); - return NULL; - } - } else if (!strcmp(attrs[a]->name, "weight")) { - if (!strcmp(attrs[a]->value, "normal")) { - style->font->weight = FW_REGULAR; - presence->font.weight = true; - } else if (!strcmp(attrs[a]->value, "bold")) { - style->font->weight = FW_BOLD; - presence->font.weight = true; - } else { - cho_log(LOG_ERR, "Invalid value in attribute 'weight'."); - return NULL; - } - } else if (!strcmp(attrs[a]->name, "foreground")) { - rgb_color = cho_color_parse(attrs[a]->value); - if (!rgb_color) { - LOG_DEBUG("cho_color_parse failed."); - return NULL; - } else { - free(style->foreground_color); - style->foreground_color = rgb_color; - presence->foreground_color = true; - } - } else if (!strcmp(attrs[a]->name, "background")) { - rgb_color = cho_color_parse(attrs[a]->value); - if (!rgb_color) { - LOG_DEBUG("cho_color_parse failed."); - return NULL; - } else { - free(style->background_color); - style->background_color = rgb_color; - presence->background_color = true; - } - } else if (!strcmp(attrs[a]->name, "underline")) { - if (!strcmp(attrs[a]->value, "single")) { - style->underline_style = LS_SINGLE; - presence->underline_style = true; - } else if (!strcmp(attrs[a]->value, "double")) { - style->underline_style = LS_DOUBLE; - presence->underline_style = true; - } else if (!strcmp(attrs[a]->value, "none")) { - style->underline_style = LS_NONE; - presence->underline_style = true; - } else { - cho_log(LOG_ERR, "Invalid value in attribute 'underline'."); - return NULL; - } - } else if (!strcmp(attrs[a]->name, "underline_colour")) { - rgb_color = cho_color_parse(attrs[a]->value); - if (!rgb_color) { - LOG_DEBUG("cho_color_parse failed."); - return NULL; - } else { - free(style->underline_color); - style->underline_color = rgb_color; - presence->underline_color = true; - } - } else if (!strcmp(attrs[a]->name, "overline")) { - if (!strcmp(attrs[a]->value, "single")) { - style->overline_style = LS_SINGLE; - presence->overline_style = true; - } else if (!strcmp(attrs[a]->value, "double")) { - style->overline_style = LS_DOUBLE; - presence->overline_style = true; - } else if (!strcmp(attrs[a]->value, "none")) { - style->overline_style = LS_NONE; - presence->overline_style = true; - } else { - cho_log(LOG_ERR, "Invalid value in attribute 'overline'."); - return NULL; - } - } else if (!strcmp(attrs[a]->name, "overline_colour")) { - rgb_color = cho_color_parse(attrs[a]->value); - if (!rgb_color) { - LOG_DEBUG("cho_color_parse failed."); - return NULL; - } else { - free(style->overline_color); - style->overline_color = rgb_color; - presence->overline_color = true; - } - } else if (!strcmp(attrs[a]->name, "rise")) { - value_len = strlen(attrs[a]->value); - last_char = value_len - 1; - if (attrs[a]->value[0] == '-') { - if (attrs[a]->value[last_char] == '%') { - if (value_len < 6) { - attrs[a]->value[last_char] = 0; - int percentage = atoi(&attrs[a]->value[1]); - if (percentage != 0 && percentage <= 100) { - style->rise = (style->font->size / 2) * percentage / 100.0; - presence->rise = true; - } else { - cho_log(LOG_ERR, "Invalid percentage in attribute 'rise'."); - return NULL; - } - } - } else if (isdigit(attrs[a]->value[1]) != 0) { - double rise = strtod(attrs[a]->value, NULL); - if (rise != 0.0) { - style->rise = rise; - presence->rise = true; - } else { - cho_log(LOG_ERR, "Invalid number in attribute 'rise'."); - return NULL; - } - } else { - cho_log(LOG_ERR, "Invalid value '%s' for the attribute 'rise'.", attrs[a]->value); - return NULL; - } - } else { - if (attrs[a]->value[last_char] == '%') { - if (value_len < 5) { - attrs[a]->value[last_char] = 0; - int percentage = atoi(attrs[a]->value); - if (percentage != 0 && percentage <= 100) { - // TODO: Test if that's right - double more = style->font->size / 2.0 * percentage / 100.0; - style->rise += more; - presence->rise = true; - } else { - cho_log(LOG_ERR, "Invalid percentage in attribute 'rise'."); - return NULL; - } - } - } else if (isdigit(attrs[a]->value[1]) != 0) { - double rise = strtod(attrs[a]->value, NULL); - if (rise != 0.0) { - style->rise = rise; - presence->rise = true; - } else { - cho_log(LOG_ERR, "Invalid number in attribute 'rise'."); - return NULL; - } - } else { - cho_log(LOG_ERR, "Invalid value '%s' for the attribute 'rise'.", attrs[a]->value); - return NULL; - } - } - } else if (!strcmp(attrs[a]->name, "strikethrough")) { - if (!strcmp(attrs[a]->value, "true")) { - style->strikethrough = true; - presence->strikethrough = true; - } else if (!strcmp(attrs[a]->value, "false")) { - style->strikethrough = false; - presence->strikethrough = true; - } else { - cho_log(LOG_ERR, "Invalid value '%s' in attribute 'strikethrough'.", attrs[a]->value); - return NULL; - } - } else if (!strcmp(attrs[a]->name, "strikethrough_colour")) { - rgb_color = cho_color_parse(attrs[a]->value); - if (!rgb_color) { - LOG_DEBUG("cho_color_parse failed."); - return NULL; - } else { - free(style->strikethrough_color); - style->strikethrough_color = rgb_color; - presence->strikethrough_color = true; - } - } else if (!strcmp(attrs[a]->name, "href")) { - style->href = strdup(attrs[a]->value); - presence->href = true; - } else { - cho_log(LOG_ERR, "Invalid attribute '%s'.", attrs[a]->name); - return NULL; - } - } - } else if (!strcmp(tag_name, "b")) { - style->font->weight = FW_BOLD; - presence->font.weight = true; - } else if (!strcmp(tag_name, "big")) { - style->font->size *= 0.8; - presence->font.size = true; - } else if (!strcmp(tag_name, "i")) { - style->font->style = FS_ITALIC; - presence->font.style = true; - } else if (!strcmp(tag_name, "s")) { - style->strikethrough = true; - presence->strikethrough = true; - } else if (!strcmp(tag_name, "sub")) { - style->font->size *= 0.8; - style->rise = -style->font->size * 0.3; - presence->font.size = true; - presence->rise = true; - } else if (!strcmp(tag_name, "sup")) { - style->font->size *= 0.8; - style->rise = style->font->size * 0.3; - presence->font.size = true; - presence->rise = true; - } else if (!strcmp(tag_name, "small")) { - style->font->size *= 0.8; - presence->font.size = true; - } else if (!strcmp(tag_name, "tt")) { - style->font->family = FF_MONOSPACE; - presence->font.family = true; - } else if (!strcmp(tag_name, "u")) { - style->underline_style = LS_SINGLE; - presence->underline_style = true; - } else { - cho_log(LOG_ERR, "Invalid tag name '%s'.", tag_name); - cho_style_free(style); - return NULL; - } - return style; -} - -void -cho_style_print_as_toml(struct ChoStyle *style, const char *section) -{ - printf("foreground_color = \"%s\"\n", cho_rgbcolor_to_string(style->foreground_color)); - printf("background_color = \"%s\"\n", cho_rgbcolor_to_string(style->background_color)); - printf("underline_style = \"%s\"\n", cho_linestyle_to_config_string(style->underline_style)); - printf("underline_color = \"%s\"\n", cho_rgbcolor_to_string(style->underline_color)); - printf("overline_style = \"%s\"\n", cho_linestyle_to_config_string(style->overline_style)); - printf("overline_color = \"%s\"\n", cho_rgbcolor_to_string(style->overline_color)); - printf("strikethrough = %s\n", style->strikethrough ? "true" : "false"); - printf("strikethrough_color = \"%s\"\n", cho_rgbcolor_to_string(style->strikethrough_color)); - printf("boxed = %s\n", style->boxed ? "true" : "false"); - printf("boxed_color = \"%s\"\n", cho_rgbcolor_to_string(style->boxed_color)); - printf("rise = %.1f\n", style->rise); - printf("href = \"%s\"\n\n", style->href ? style->href : ""); - cho_font_print_as_toml(style->font, section); -} - -static bool -cho_style_change_default(struct StyleProperty sprop) -{ - if (sprop.type == -1) - return false; - unsigned int i; - for (i = 0; i<LENGTH(default_style_properties); i++) { - if ( - default_style_properties[i].ttype == sprop.ttype && - default_style_properties[i].type == sprop.type - ) { - switch (sprop.type) { - case SPT_FONT: - free(default_style_properties[i].u.font_name); - if (sprop.u.font_name) { - default_style_properties[i].u.font_name = strdup(sprop.u.font_name); - } else { - default_style_properties[i].u.font_name = NULL; - } - return true; - case SPT_SIZE: - default_style_properties[i].u.font_size = sprop.u.font_size; - return true; - case SPT_COLOR: - free(default_style_properties[i].u.foreground_color); - if (sprop.u.foreground_color) { - default_style_properties[i].u.foreground_color = cho_rgbcolor_copy(sprop.u.foreground_color); - } else { - default_style_properties[i].u.foreground_color = NULL; - } - return true; - default: - cho_log(LOG_ERR, "Invalid style property type '%d'.", sprop.type); - return false; - } - } - } - return false; -} - -static bool -cho_style_reset_default(void) -{ - unsigned int i; - for (i = 0; i<LENGTH(default_style_properties); i++) { - switch (default_style_properties[i].type) { - case SPT_FONT: - free(default_style_properties[i].u.font_name); - default_style_properties[i].u.font_name = NULL; - return true; - case SPT_SIZE: - default_style_properties[i].u.font_size = EMPTY_DOUBLE; - return true; - case SPT_COLOR: - free(default_style_properties[i].u.foreground_color); - default_style_properties[i].u.foreground_color = NULL; - return true; - default: - cho_log(LOG_ERR, "Invalid style property type '%d'.", default_style_properties[i].type); - return false; - } - } - return false; -} - -static struct Attr * -cho_tag_attr_new(void) -{ - struct Attr *attr = emalloc(sizeof(struct Attr)); - attr->name = NULL; - attr->value = NULL; - return attr; -} - -static void -cho_tag_attr_free(struct Attr *attr) -{ - if (!attr) { - return; - } - free(attr->name); - free(attr->value); - free(attr); -} - -static void -cho_tag_attrs_free(struct Attr **attrs) -{ - if (!attrs) { - return; - } - struct Attr **a; - for (a = attrs; *a; a++) { - cho_tag_attr_free(*a); - } - free(attrs); -} - -static struct Tag * -cho_tag_new(void) -{ - struct Tag *tag = emalloc(sizeof(struct Tag)); - tag->name = NULL; - tag->style = NULL; - tag->attrs = NULL; - tag->is_closed = false; - memset(&tag->style_presence, 0, sizeof(tag->style_presence)); - return tag; -} - -static void -cho_tag_free(struct Tag *tag) -{ - if (!tag) { - return; - } - free(tag->name); - if (tag->style) { - cho_style_free(tag->style); - } - if (tag->attrs) { - cho_tag_attrs_free(tag->attrs); - } - free(tag); -} - -static bool -cho_tag_close_last_unclosed(const char *tag_name, struct Tag **tags, int last_index) -{ - int i; - for (i = last_index; i >= 0; i--) { - if (!strcmp(tags[i]->name, tag_name) && !tags[i]->is_closed) { - tags[i]->is_closed = true; - return true; - } - } - cho_log(LOG_ERR, "Didn't find a start tag for the end tag '%s'.", tag_name); - return false; -} - -static struct ChoStyle * -cho_tag_style_inherit(struct Tag **tags, int prev_index) -{ - int i; - for (i = prev_index; i >= 0; i--) { - if (!tags[i]->is_closed) { - return tags[i]->style; - } - } - /* - Doesn't mean there is an error. - If no style can be inherited the - default style should be used. - */ - return NULL; -} - -static struct ChoMetadata * -cho_metadata_new(void) -{ - struct ChoMetadata *meta = emalloc(sizeof(struct ChoMetadata)); - meta->name = NULL; - meta->value = NULL; - meta->style = cho_style_new_default(); - return meta; -} - -static void -cho_metadata_free(struct ChoMetadata *meta) -{ - if (!meta) { - return; - } - free(meta->name); - free(meta->value); - if (meta->style) { - cho_style_free(meta->style); - } - free(meta); -} - -const char * -cho_metadata_get(struct ChoMetadata **metadata, const char *name) -{ - int m; - for (m = 0; metadata[m]; m++) { - if (!strcmp(metadata[m]->name, name)) { - return metadata[m]->value; - } - } - return NULL; -} - -static struct ChoMetadata * -cho_metadata_split(const char *directive_value) -{ - struct ChoMetadata *meta = cho_metadata_new(); - bool is_name = true; - char *value = str_remove_leading_whitespace(directive_value); - int n = 0; - int v = 0; - int i; - for (i = 0; value[i]; i++) { - if (value[i] == ' ') { - meta->name = erealloc(meta->name, (n+1) * sizeof(char)); - meta->name[n] = 0; - is_name = false; - } else { - if (is_name) { - meta->name = erealloc(meta->name, (n+1) * sizeof(char)); - meta->name[n] = value[i]; - n++; - } else { - meta->value = erealloc(meta->value, (v+1) * sizeof(char)); - meta->value[v] = value[i]; - v++; - } - } - } - meta->value = erealloc(meta->value, (v+1) * sizeof(char)); - meta->value[v] = 0; - free(value); - if (v > 1) { - return meta; - } else { - free(meta->name); - free(meta->value); - free(meta); - cho_log(LOG_ERR, "Failed to parse directive 'meta'."); - return NULL; - } -} - -static bool -transposition_parse(const char *str, int *transpose) -{ - long i; - char *endptr; - i = strtol(str, &endptr, 10); - if (str == endptr) { - return false; - } - if (i == LONG_MIN || i == LONG_MAX) { - return false; - } - if (i > INT_MAX) { - return false; - } - *transpose = (int)i; - return true; -} - -static char * -transposition_calc_chord_root(int index, enum NoteType type) -{ - int transpose = *g_transpose; - if (transpose == 0) { - switch (type) { - case NT_NOTE: - return strdup(g_config->output->notes[index]->note); - case NT_SHARP: - return strdup(g_config->output->notes[index]->sharp); - case NT_FLAT: - return strdup(g_config->output->notes[index]->flat); - } - } - int new_index = index; - enum NoteType note_type = type; - int i; - if (transpose > 0) { - for (i = 0; i<transpose; i++) { - switch (note_type) { - case NT_NOTE: - switch (new_index) { - case 2: - new_index++; - break; - case 6: - new_index = 0; - break; - default: - note_type = NT_SHARP; - } - break; - case NT_SHARP: - new_index++; - note_type = NT_NOTE; - break; - case NT_FLAT: - note_type = NT_NOTE; - break; - } - } - } else { - for (i = transpose; i<0; i++) { - switch (note_type) { - case NT_NOTE: - switch (new_index) { - case 0: - new_index = 6; - break; - case 3: - new_index--; - break; - default: - note_type = NT_FLAT; - } - break; - case NT_SHARP: - note_type = NT_NOTE; - break; - case NT_FLAT: - new_index--; - note_type = NT_NOTE; - break; - } - } - } - switch (note_type) { - case NT_NOTE: - return strdup(g_config->output->notes[new_index]->note); - case NT_SHARP: - return strdup(g_config->output->notes[new_index]->sharp); - case NT_FLAT: - return strdup(g_config->output->notes[new_index]->flat); - } - cho_log(LOG_ERR, "Invalid NoteType '%d'.", note_type); - return NULL; -} - -static struct ChoChord * -cho_chord_new(void) -{ - struct ChoChord *chord = emalloc(sizeof(struct ChoChord)); - chord->style = cho_style_new_default(); - chord->name = NULL; - chord->is_canonical = false; - chord->root = NULL; - chord->qual = -1; - chord->ext = NULL; - chord->bass = NULL; - return chord; -} - -/* INFO: copy every field except for 'style' */ -static void -cho_chord_complete(struct ChoChord *first, struct ChoChord *second) -{ - first->name = strdup(second->name); - first->is_canonical = second->is_canonical; - first->qual = second->qual; - if (second->root) { - first->root = strdup(second->root); - } - if (second->ext) { - first->ext = strdup(second->ext); - } - if (second->bass) { - first->bass = strdup(second->bass); - } -} - - -static void -cho_chord_free(struct ChoChord *chord) -{ - if (!chord) { - return; - } - cho_style_free(chord->style); - free(chord->name); - free(chord->root); - free(chord->ext); - free(chord->bass); - free(chord); -} - -static struct ChoChord * -cho_chord_copy(struct ChoChord *chord) -{ - struct ChoChord *copy = emalloc(sizeof(struct ChoChord)); - copy->style = cho_style_copy(chord->style); - copy->is_canonical = chord->is_canonical; - copy->qual = chord->qual; - copy->name = strdup(chord->name); - copy->root = chord->root ? strdup(chord->root) : NULL; - copy->ext = chord->ext ? strdup(chord->ext) : NULL; - copy->bass = chord->bass ? strdup(chord->bass) : NULL; - return copy; -} - -bool -cho_chords_has(struct ChoChord **chords, struct ChoChord *chord) -{ - if (!chords) { - return false; - } - struct ChoChord **c; - for (c = chords; *c; c++) { - if ( - !str_compare((*c)->name, chord->name) && - !str_compare((*c)->root, chord->root) && - !str_compare((*c)->ext, chord->ext) && - !str_compare((*c)->bass, chord->bass) && - (*c)->qual == chord->qual - ) { - return true; - } - } - return false; -} - -int -cho_chord_compare(const void *a, const void *b) -{ - struct ChoChord **aa = (struct ChoChord **)a; - struct ChoChord **bb = (struct ChoChord **)b; - return str_compare((*aa)->root, (*bb)->root); -} - -size_t -cho_chord_count(struct ChoChord **chords) -{ - if (!chords) { - return 0; - } - int i; - for (i = 0; chords[i]; i++); - return i; -} - -void -cho_chords_add(struct ChoChord ***chords, struct ChoChord *chord) -{ - int i = 0; - if (*chords) { - struct ChoChord **c; - for (c = *chords; *c; c++, i++); - } - *chords = erealloc(*chords, (i+2) * sizeof(struct ChoChord *)); - (*chords)[i] = cho_chord_copy(chord); - (*chords)[i+1] = NULL; -} - -void -cho_chords_free(struct ChoChord **chords) -{ - if (!chords) { - return; - } - struct ChoChord **c; - for (c = chords; *c; c++) { - cho_chord_free(*c); - } - free(chords); -} - -/* returns how many bytes make up the root; returns 0 if no root was found */ -static int -cho_chord_root_parse(const char *str, struct ChoChord *chord) -{ - const char *note = NULL; - const char *sharp = NULL; - const char *flat = NULL; - char *transposed_root; - int i; - for (i = 0; i<7; i++) { - sharp = g_config->parser->notes[i]->sharp; - if (sharp && str_starts_with(str, sharp)) { - transposed_root = transposition_calc_chord_root(i, NT_SHARP); - if (!transposed_root) { - LOG_DEBUG("transposition_calc_chord_root failed."); - return 0; - } - chord->root = transposed_root; - return strlen(sharp); - } - flat = g_config->parser->notes[i]->flat; - if (flat && str_starts_with(str, flat)) { - transposed_root = transposition_calc_chord_root(i, NT_FLAT); - if (!transposed_root) { - LOG_DEBUG("transposition_calc_chord_root failed."); - return 0; - } - chord->root = transposed_root; - return strlen(flat); - } - note = g_config->parser->notes[i]->note; - if (str_starts_with(str, note)) { - transposed_root = transposition_calc_chord_root(i, NT_NOTE); - if (!transposed_root) { - LOG_DEBUG("transposition_calc_chord_root failed."); - return 0; - } - chord->root = transposed_root; - return strlen(note); - } - } - return 0; -} - -static char * -cho_chord_qualifier_strip(const char *str) -{ - if (str_starts_with(str, "m") || str_starts_with(str, "-")) { - return strdup((char *)&str[1]); - } - return strdup(str); -} - -static int -cho_chord_qualifier_and_extension_parse(const char *str, struct ChoChord *chord) -{ - int i; - for (i = 0; chord_extensions_major[i]; i++) { - if (str_starts_with(str, chord_extensions_major[i])) { - chord->ext = strdup(chord_extensions_major[i]); - chord->qual = CQ_MAJ; - return strlen(chord_extensions_major[i]); - } - } - for (i = 0; chord_extensions_minor[i]; i++) { - if (str_starts_with(str, chord_extensions_minor[i])) { - chord->ext = cho_chord_qualifier_strip(chord_extensions_minor[i]); - chord->qual = CQ_MIN; - return strlen(chord_extensions_minor[i]); - } - } - if (str_starts_with(str, "aug")) { - chord->qual = CQ_AUG; - return 3; - } - if (str_starts_with(str, "+")) { - chord->qual = CQ_AUG; - return 1; - } - if (str_starts_with(str, "dim")) { - chord->qual = CQ_DIM; - return 3; - } - if (str_starts_with(str, "0")) { - chord->qual = CQ_DIM; - return 1; - } - if (str_starts_with(str, "ø")) { - chord->qual = CQ_DIM; - return 1; - } - // TODO: What about 'ø', 'h', 'h7' and 'h9'? - // TODO: What about extensions after 'aug', '+', 'dim', '0'? - return 0; -} - -static int -cho_chord_bass_parse(const char *str, struct ChoChord *chord) -{ - if (str[0] == '/') { - const char *note = NULL; - const char *sharp = NULL; - const char *flat = NULL; - const char *out_note = NULL; - const char *out_sharp = NULL; - const char *out_flat = NULL; - int i; - for (i = 0; i<7; i++) { - sharp = g_config->parser->notes[i]->sharp; - out_sharp = g_config->output->notes[i]->sharp; - if (sharp && !strcmp((char *)&str[1], sharp)) { - chord->bass = strdup(out_sharp); - return strlen(sharp) + 1; - } - flat = g_config->parser->notes[i]->flat; - out_flat = g_config->output->notes[i]->flat; - if (flat && !strcmp((char *)&str[1], flat)) { - chord->bass = strdup(out_flat); - return strlen(flat) + 1; - } - note = g_config->parser->notes[i]->note; - out_note = g_config->parser->notes[i]->note; - if (!strcmp((char *)&str[1], note)) { - chord->bass = strdup(out_note); - return strlen(note) + 1; - } - } - } - return 0; -} - -static struct ChoChord * -cho_chord_parse(const char *str) -{ - struct ChoChord *chord = cho_chord_new(); - size_t str_len = strlen(str); - size_t bytes_parsed = 0; - int ret; - chord->name = strdup(str); - ret = cho_chord_root_parse(str, chord); - if (ret == 0) { - return chord; - } - chord->qual = CQ_MAJ; - bytes_parsed += ret; - if (bytes_parsed == str_len) { - chord->is_canonical = true; - return chord; - } - ret = cho_chord_qualifier_and_extension_parse((const char *)&str[bytes_parsed], chord); - bytes_parsed += ret; - if (bytes_parsed == str_len) { - chord->is_canonical = true; - return chord; - } - ret = cho_chord_bass_parse((const char *)&str[bytes_parsed], chord); - bytes_parsed += ret; - if (bytes_parsed == str_len) { - chord->is_canonical = true; - return chord; - } - return chord; -} - -char * -cho_chord_name_generate(struct ChoChord *chord) -{ - if (chord->is_canonical) { - int n = 0; - int i; - char *name = NULL; - size_t name_len = 0; - name_len += strlen(chord->root); - name = erealloc(name, name_len * sizeof(char)); - for (i = 0; chord->root[i]; i++) { - name[n] = chord->root[i]; - n++; - } - const char *qual = chord_qualifiers[chord->qual]; - name_len += strlen(qual); - name = erealloc(name, name_len * sizeof(char)); - for (i = 0; qual[i]; i++) { - name[n] = qual[i]; - n++; - } - if (chord->ext) { - name_len += strlen(chord->ext); - name = erealloc(name, name_len * sizeof(char)); - for (i = 0; chord->ext[i]; i++) { - name[n] = chord->ext[i]; - n++; - } - } - if (chord->bass) { - name_len++; - name_len += strlen(chord->bass); - name = erealloc(name, name_len * sizeof(char)); - name[n] = '/'; - n++; - for (i = 0; chord->bass[i]; i++) { - name[n] = chord->bass[i]; - n++; - } - } - name_len++; - name = erealloc(name, name_len * sizeof(char)); - name[n] = 0; - return name; - } - return strdup(chord->name); -} - -static struct ChoImage * -cho_image_new(void) -{ - struct ChoImage *image = emalloc(sizeof(struct ChoImage)); - image->is_asset = false; - image->id = NULL; - image->src = NULL; - image->width = NULL; - image->height = NULL; - image->width_scale = NULL; - image->height_scale = NULL; - image->align = A_CENTER; - image->border = EMPTY_DOUBLE; - image->spread_space = NULL; - image->href = NULL; - image->x = NULL; - image->y = NULL; - image->anchor = AN_FLOAT; - image->dx = NULL; - image->dy = NULL; - image->w = NULL; - image->h = NULL; - image->bbox = false; - return image; -} - -static void -cho_image_free(struct ChoImage *image) -{ - if (!image) { - return; - } - free(image->id); - free(image->src); - free(image->width); - free(image->height); - free(image->width_scale); - free(image->height_scale); - free(image->spread_space); - free(image->href); - free(image->x); - free(image->y); - free(image->dx); - free(image->dy); - free(image->w); - free(image->h); - free(image); -} - -static struct ChoImage * -cho_image_copy(struct ChoImage *image) -{ - struct ChoImage *copy = emalloc(sizeof(struct ChoImage)); - copy->is_asset = image->is_asset; - copy->id = strdup(image->id); - copy->src = strdup(image->src); - copy->width = size_copy(image->width); - copy->height = size_copy(image->height); - 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); - copy->href = strdup(image->href); - copy->x = size_copy(image->x); - copy->y = size_copy(image->y); - copy->anchor = image->anchor; - copy->dx = size_copy(image->dx); - copy->dy = size_copy(image->dy); - copy->w = size_copy(image->w); - copy->h = size_copy(image->h); - copy->bbox = image->bbox; - return copy; -} - -static struct ChoImage * -cho_image_find_asset(const char *id) -{ - int i; - for (i = 0; i<g_ia; i++) { - if (!strcmp(g_image_assets[i]->id, id)) { - return g_image_assets[i]; - } - } - return NULL; -} - -#ifdef DEBUG -void -cho_debug_image_print(struct ChoImage *image) -{ - printf("---- BEGIN IMAGE ----\n"); - printf("is_asset: %d\n", image->is_asset); - if (image->id) { - printf("id: %s\n", image->id); - } else { - printf("id: NULL\n"); - } - if (image->src) { - printf("src: %s\n", image->src); - } else { - printf("src: NULL\n"); - } - if (image->width) { - printf("width: %s\n", size_to_string(image->width)); - } else { - printf("width: NULL\n"); - } - if (image->height) { - printf("height: %s\n", size_to_string(image->height)); - } else { - printf("height: NULL\n"); - } - if (image->width_scale) { - printf("width_scale: %s\n", size_to_string(image->width_scale)); - } else { - 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); - if (image->spread_space) { - printf("spread_space: %s\n", size_to_string(image->spread_space)); - } else { - printf("spread_space: NULL\n"); - } - if (image->href) { - printf("href: %s\n", image->href); - } else { - printf("href: NULL\n"); - } - if (image->x) { - printf("x: %s\n", size_to_string(image->x)); - } else { - printf("x: NULL\n"); - } - if (image->y) { - printf("y: %s\n", size_to_string(image->y)); - } else { - printf("y: NULL\n"); - } - printf("anchor: %s\n", anchor_enums[image->anchor]); - if (image->dx) { - printf("dx: %s\n", size_to_string(image->dx)); - } else { - printf("dx: NULL\n"); - } - if (image->dy) { - printf("dy: %s\n", size_to_string(image->dy)); - } else { - printf("dy: NULL\n"); - } - if (image->w) { - printf("w: %s\n", size_to_string(image->w)); - } else { - printf("w: NULL\n"); - } - if (image->h) { - printf("h: %s\n", size_to_string(image->h)); - } else { - printf("h: NULL\n"); - } - printf("bbox: %d\n", image->bbox); - printf("---- END IMAGE ------\n"); -} -#endif /* DEBUG */ - -static bool -cho_image_option_parse(struct ChoImage *image, const char *name, const char *value) -{ - char *endptr; - struct Size *size; - if (!strcmp(name, "id")) { - image->id = strdup(value); - } else - if (!strcmp(name, "src")) { - image->src = filepath_resolve_tilde(value); - if (!image->src) { - LOG_DEBUG("filepath_resolve_tilde failed."); - return false; - } - } else - if (!strcmp(name, "width")) { - size = size_create(value); - if (!size) { - cho_log(LOG_ERR, "Invalid value in option 'width' in image directive."); - return false; - } - if (size->type == ST_EM || size->type == ST_EX) { - cho_log(LOG_ERR, "Invalid type of value in option 'width' in image directive. Allowed types are: points, percentage."); - return false; - } - image->width = size; - } else - if (!strcmp(name, "height")) { - size = size_create(value); - if (!size) { - cho_log(LOG_ERR, "Invalid value in option 'height' in image directive."); - return false; - } - if (size->type == ST_EM || size->type == ST_EX) { - cho_log(LOG_ERR, "Invalid type of value in option 'height' in image directive. Allowed types are: point, percentage."); - return false; - } - image->height = size; - } else - if (!strcmp(name, "scale")) { - 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); - } - } else - if (!strcmp(name, "align")) { - if (!strcmp(value, "left")) { - image->align = A_LEFT; - } else - if (!strcmp(value, "right")) { - image->align = A_RIGHT; - } else - if (!strcmp(value, "center")) { - image->align = A_CENTER; - } else { - cho_log(LOG_ERR, "Invalid value in option 'align' in image directive."); - return false; - } - } else - if (!strcmp(name, "border")) { - image->border = strtod(value, &endptr); - if (value == endptr || errno == ERANGE) { - LOG_DEBUG("strtod failed."); - return false; - } - } else - if (!strcmp(name, "spread")) { - size = size_create(value); - if (!size) { - cho_log(LOG_ERR, "Invalid value in option 'spread' in image directive."); - return false; - } - if (size->type == ST_EM || size->type == ST_EX) { - cho_log(LOG_ERR, "Invalid type of value in option 'spread' in image directive. Allowed types are: point, percentage."); - return false; - } - image->spread_space = size; - } else - if (!strcmp(name, "href")) { - image->href = strdup(value); - } else - if (!strcmp(name, "x")) { - size = size_create(value); - if (!size) { - cho_log(LOG_ERR, "Invalid value in option 'x' in image directive."); - return false; - } - if (size->type == ST_EM || size->type == ST_EX) { - cho_log(LOG_ERR, "Invalid type of value in option 'x' in image directive. Allowed types are: point, percentage."); - return false; - } - image->x = size; - } else - if (!strcmp(name, "y")) { - size = size_create(value); - if (!size) { - cho_log(LOG_ERR, "Invalid value in option 'y' in image directive."); - return false; - } - if (size->type == ST_EM || size->type == ST_EX) { - cho_log(LOG_ERR, "Invalid type of value in option 'y' in image directive. Allowed types are: point, percentage."); - return false; - } - image->y = size; - } else - if (!strcmp(name, "anchor")) { - if (!strcmp(value, "paper")) { - image->anchor = AN_PAPER; - } else - if (!strcmp(value, "page")) { - image->anchor = AN_PAGE; - } else - if (!strcmp(value, "column")) { - image->anchor = AN_COLUMN; - } else - if (!strcmp(value, "line")) { - image->anchor = AN_LINE; - } else - if (!strcmp(value, "float")) { - image->anchor = AN_FLOAT; - } else { - cho_log(LOG_ERR, "Invalid value in option 'anchor' in image directive."); - return false; - } - } - return true; -} - -static struct ChoImage * -cho_image_directive_parse(const char *str) -{ - struct ChoImage *image = cho_image_new(); - struct ChoImage *asset; - char c; - enum OptionState state = OS_NAME; - enum AttrValueSyntax avs = -1; - char name[6+1]; - char value[URL_MAX_LEN+1]; - int n = 0; - int v = 0; - memset(name, 0, sizeof(name)); - memset(value, 0, sizeof(value)); - int option_count = 0; - int i; - for (i = 0; str[i]; i++) { - c = str[i]; - switch (state) { - case OS_NAME: - if (is_whitespace(c)) { - if (n == 0) { - break; - } else { - name[n] = 0; - cho_log(LOG_ERR, "Option with name '%s' in image directive has no value.", name); - return NULL; - } - } - if (c == '=') { - name[n] = 0; - state = OS_VALUE; - break; - } - if (n > 5) { - cho_log(LOG_ERR, "Option name in image directive is too long."); - return NULL; - } - name[n] = c; - n++; - break; - case OS_VALUE: - if (avs == -1) { - if (is_whitespace(c)) { - cho_log(LOG_ERR, "Whitespace character after equals sign in image directive is invalid."); - return NULL; - } - if (c == '\'') { - avs = AVS_APOSTROPHE; - } else if (c == '"') { - avs = AVS_QUOTATION_MARK; - } else { - avs = AVS_UNQUOTED; - value[v] = c; - v++; - } - break; - } - if (c == '\n') { - cho_log(LOG_ERR, "Newline character inside an option value in image directive is invalid."); - return NULL; - } - if ( - (avs == AVS_APOSTROPHE && c == '\'') || - (avs == AVS_QUOTATION_MARK && c == '"') || - (avs == AVS_UNQUOTED && (c == ' ' || c == '\t')) - ) { - value[v] = 0; - if (!cho_image_option_parse(image, name, value)) { - LOG_DEBUG("cho_image_option_check failed."); - return NULL; - } - option_count++; - memset(name, 0, n); - memset(value, 0, v); - n = 0; - v = 0; - avs = -1; - state = OS_NAME; - break; - } - value[v] = c; - v++; - break; - } - } - if (avs == AVS_UNQUOTED) { - value[v] = 0; - if (!cho_image_option_parse(image, name, value)) { - LOG_DEBUG("cho_image_option_check failed."); - return NULL; - } - option_count++; - } - if (image->id) { - if (image->src) { - if (option_count > 2) { - cho_log(LOG_ERR, "Defining an image asset disallows any options other than 'id' and 'src'."); - return NULL; - } - image->is_asset = true; - } else { - asset = cho_image_find_asset(image->id); - if (!asset) { - cho_log(LOG_ERR, "There is no image asset with the id '%s'.", image->id); - return NULL; - } - image->src = strdup(asset->src); - } - } - return image; -} - -static struct ChoImage * -cho_image_tag_parse(struct Attr **attrs) -{ - struct ChoImage *image = cho_image_new(); - struct ChoImage *asset; - struct Size *size; - int a; - for (a = 0; attrs[a]; a++) { - if (!strcmp(attrs[a]->name, "src")) { - image->src = filepath_resolve_tilde(attrs[a]->value); - if (!image->src) { - LOG_DEBUG("filepath_resolve_tilde failed."); - return NULL; - } - } else - if (!strcmp(attrs[a]->name, "id")) { - image->id = strdup(attrs[a]->value); - } else - if (!strcmp(attrs[a]->name, "width")) { - size = size_create(attrs[a]->value); - if (!size) { - cho_log(LOG_ERR, "Invalid value in attribute 'width' in 'img' tag."); - return NULL; - } - if (size->type == ST_PERCENT) { - cho_log(LOG_ERR, "Invalid type of value in attribute 'width' in 'img' tag. Allowed types are: point, em, ex."); - return NULL; - } - image->width = size; - } else - if (!strcmp(attrs[a]->name, "height")) { - size = size_create(attrs[a]->value); - if (!size) { - cho_log(LOG_ERR, "Invalid value in attribute 'height' in 'img' tag."); - return NULL; - } - if (size->type == ST_PERCENT) { - cho_log(LOG_ERR, "Invalid type of value in attribute 'height' in 'img' tag. Allowed types are: point, em, ex."); - return NULL; - } - image->height = size; - } else - if (!strcmp(attrs[a]->name, "dx")) { - size = size_create(attrs[a]->value); - if (!size) { - cho_log(LOG_ERR, "Invalid value in attribute 'dx' in 'img' tag."); - return NULL; - } - if (size->type == ST_PERCENT) { - cho_log(LOG_ERR, "Invalid type of value in attribute 'dx' in 'img' tag. Allowed types are: point, em, ex."); - return NULL; - } - image->dx = size; - } else - if (!strcmp(attrs[a]->name, "dy")) { - size = size_create(attrs[a]->value); - if (!size) { - cho_log(LOG_ERR, "Invalid value in attribute 'dy' in 'img' tag."); - return NULL; - } - if (size->type == ST_PERCENT) { - cho_log(LOG_ERR, "Invalid type of value in attribute 'dy' in 'img' tag. Allowed types are: point, em, ex."); - return NULL; - } - image->dy = size; - } else - if (!strcmp(attrs[a]->name, "scale")) { - 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 NULL; - } - 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 NULL; - } - image->width_scale = size; - size = size_create(++comma); - if (!size) { - cho_log(LOG_ERR, "Invalid value in attribute 'scale' in 'img' tag."); - return NULL; - } - 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 NULL; - } - 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 NULL; - } - 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 NULL; - } - image->width_scale = size; - image->height_scale = size_copy(size); - } - } else - if (!strcmp(attrs[a]->name, "align")) { - if (!strcmp(attrs[a]->value, "left")) { - image->align = A_LEFT; - } else - if (!strcmp(attrs[a]->value, "right")) { - image->align = A_RIGHT; - } else - if (!strcmp(attrs[a]->value, "center")) { - image->align = A_CENTER; - } else { - cho_log(LOG_ERR, "Invalid value in attribute 'align' in 'img' tag."); - return NULL; - } - } else - if (!strcmp(attrs[a]->name, "bbox")) { - if (!strcmp(attrs[a]->value, "1")) { - image->bbox = true; - } else - if (!strcmp(attrs[a]->value, "0")) { - image->bbox = false; - } else { - cho_log(LOG_ERR, "Invalid value in attribute 'bbox' in 'img' tag."); - return NULL; - } - } else - if (!strcmp(attrs[a]->name, "w")) { - size = size_create(attrs[a]->value); - if (!size) { - cho_log(LOG_ERR, "Invalid value in attribute 'w' in 'img' tag."); - return NULL; - } - if (size->type != ST_POINT) { - cho_log(LOG_ERR, "Invalid type of value in attribute 'w' in 'img' tag. Allowed type is: point"); - return NULL; - } - image->w = size; - } else - if (!strcmp(attrs[a]->name, "h")) { - size = size_create(attrs[a]->value); - if (!size) { - cho_log(LOG_ERR, "Invalid value in attribute 'h' in 'img' tag."); - return NULL; - } - if (size->type != ST_POINT) { - cho_log(LOG_ERR, "Invalid type of value in attribute 'h' in 'img' tag. Allowed type is: point"); - return NULL; - } - image->h = size; - } else { - cho_log(LOG_ERR, "Invalid attribute '%s' in 'img' tag.", attrs[a]->name); - return NULL; - } - } - if (image->id) { - if (image->src) { - cho_log(LOG_ERR, "'img' tag can't have both attributes 'id' and 'src' at the same time."); - return NULL; - } else { - asset = cho_image_find_asset(image->id); - if (!asset) { - cho_log(LOG_ERR, "There is no image asset with the id '%s'.", image->id); - return NULL; - } - image->src = strdup(asset->src); - } - } else { - if (!image->src) { - cho_log(LOG_ERR, "'img' tag has to have at least either the attribute 'id' or 'src'."); - return NULL; - } - } - return image; -} - -static int8_t -char_to_positive_int(char c) -{ - if (c >= '0' && c <= '9') { - return c - 48; - } - return -1; -} - -static int8_t -finger_to_int(char c) -{ - if (c == '-' || c == 'x' || c == 'X' || c == 'N') { - return -1; - } - if (c >= '0' && c <= '9') { - return c - 48; - } - if (c >= 'A' && c <= 'Z') { - return c - 64; - } - return -2; -} - -static struct ChordDiagram * -cho_chord_diagram_parse( - const char *str, - struct ChordDiagram **custom_diagrams, - int custom_diagrams_len -) -{ - struct ChordDiagram *diagram = chord_diagram_new(); - enum ChordDiagramState state = CDS_NAME; - enum ChordDiagramContent current_content = -1; - enum ChordDiagramContent future_content = -1; - bool is_maybe_minus_one = false; - char name[20]; - char option[10]; - char key[3]; - char base_fret[3]; - char diagram_value[7+1]; - char chord_to_copy[20]; - int i = 0; - int f = 0; - int fret_count = 0; - int finger_count = 0; - int8_t number = -2; - long l; - const char *c; - for (c = str; *c; c++) { - // printf("c '%c' state '%d'\n", c, state); - switch (state) { - case CDS_NAME: { - if (is_whitespace(*c)) { - if (i == 0) { - break; - } - name[i] = 0; - i = 0; - state = CDS_OPTION_NAME; - break; - } - if (i > 18) { - cho_log(LOG_ERR, "Chord name in chord diagram is too long."); - return NULL; - } - name[i] = *c; - i++; - break; - } - case CDS_OPTION_NAME: { - if (is_whitespace(*c)) { - if (i == 0) { - break; - } - option[i] = 0; - future_content = -1; - if (!strcmp(option, "base-fret")) { - state = CDS_BASE_FRET; - future_content = CDC_STRING; - } else - if (!strcmp(option, "frets")) { - state = CDS_FRETS; - future_content = CDC_STRING; - } else - if (!strcmp(option, "fingers")) { - state = CDS_FINGERS; - future_content = CDC_STRING; - } else - if (!strcmp(option, "keys")) { - state = CDS_KEYS; - future_content = CDC_KEYBOARD; - } else - if (!strcmp(option, "diagram")) { - state = CDS_DIAGRAM; - } else - if (!strcmp(option, "copy")) { - state = CDS_COPY; - } else { - cho_log(LOG_ERR, "Invalid option '%s' in define directive.", option); - return NULL; - } - memset(option, 0, i); - i = 0; - if (current_content == -1 && future_content != -1) { - current_content = future_content; - switch (future_content) { - case CDC_STRING: - diagram->is_string_instrument = true; - diagram->u.sd = string_diagram_new(); - diagram->u.sd->name = strdup(name); - break; - case CDC_KEYBOARD: - diagram->is_string_instrument = false; - diagram->u.kd = keyboard_diagram_new(); - diagram->u.kd->name = strdup(name); - break; - default: - cho_log(LOG_ERR, "'future_content' cannot be empty at this point.\n"); - } - } - break; - } - if (i > 8) { - cho_log(LOG_ERR, "Option in chord diagram is too long."); - return NULL; - } - option[i] = *c; - i++; - break; - } - case CDS_BASE_FRET: { - if (is_whitespace(*c)) { - if (i == 0) { - break; - } - base_fret[i] = 0; - i = 0; - l = str_to_number(base_fret); - if (l == -1) { - LOG_DEBUG("str_to_number failed."); - cho_log(LOG_ERR, "Invalid base-fret value '%s' in chord diagram.", base_fret); - return NULL; - } - if (l == 0) { - cho_log(LOG_ERR, "Invalid base-fret value '%c' in chord diagram.", *c); - return NULL; - } - printf("base-fret %d\n", (int8_t)l); - diagram->u.sd->base_fret = (int8_t)l; - state = CDS_OPTION_NAME; - break; - } - if (i > 1) { - cho_log(LOG_ERR, "base-fret value is too long."); - printf("c: %c\n", *c); - return NULL; - } - printf("parsing base-fret '%c'\n", *c); - base_fret[i] = *c; - i++; - break; - } - case CDS_FRETS: { - number = -2; - if (is_whitespace(*c)) { - break; - } - if (*c == '-') { - is_maybe_minus_one = true; - break; - } - if (is_maybe_minus_one) { - if (*c == '1') { - number = -1; - is_maybe_minus_one = false; - } else { - cho_log(LOG_ERR, "Invalid frets value '-%c' in chord diagram.", *c); - return NULL; - } - } - if (*c == 'N' || *c == 'x') { - number = -1; - } else - if (isalpha(*c)) { - f = 0; - state = CDS_OPTION_NAME; - c--; - break; - } - if (number == -2) { - number = char_to_positive_int(*c); - if (number == -1) { - LOG_DEBUG("char_to_positive_int failed."); - cho_log(LOG_ERR, "Invalid frets value '%c' in chord diagram.", *c); - return NULL; - } - } - if (f > 11) { - cho_log(LOG_ERR, "Too many fret values in chord diagram."); - return NULL; - } - diagram->u.sd->frets[f] = number; - f++; - fret_count++; - break; - } - case CDS_FINGERS: { - if (is_whitespace(*c)) { - break; - } - if (*c == 'b' || *c == 'f') { - f = 0; - state = CDS_OPTION_NAME; - c--; - break; - } - number = finger_to_int(*c); - if (number == -2) { - cho_log(LOG_ERR, "Invalid fingers value '%c' in chord diagram.", *c); - return NULL; - } - if (f > 11) { - cho_log(LOG_ERR, "Too many finger values in chord diagram."); - return NULL; - } - diagram->u.sd->fingers[f] = number; - f++; - finger_count++; - break; - } - case CDS_KEYS: { - if (is_whitespace(*c)) { - if (i == 0) { - break; - } - key[i] = 0; - i = 0; - l = str_to_number(key); - if (l == -1) { - LOG_DEBUG("str_to_number failed."); - cho_log(LOG_ERR, "Invalid number in keys in chord diagram."); - return NULL; - } - if (f > 23) { - cho_log(LOG_ERR, "Too many key values in chord diagram."); - return NULL; - } - diagram->u.kd->keys[f] = (int8_t)l; - f++; - break; - } - if (isalpha(*c)) { - state = CDS_OPTION_NAME; - c--; - break; - } - if (i > 1) { - cho_log(LOG_ERR, "Too high key value in chord diagram. '%d'", i); - printf("key '%s'\n", key); - return NULL; - } - key[i] = *c; - i++; - break; - } - case CDS_DIAGRAM: { - if (is_whitespace(*c)) { - if (i == 0) { - break; - } - diagram_value[i] = 0; - i = 0; - if (!strcmp(diagram_value, "off")) { - diagram->show = false; - } else - if (!strcmp(diagram_value, "on")) { - // INFO: but this is already the default - diagram->show = true; - } else { - diagram->color = cho_color_parse(diagram_value); - if (!diagram->color) { - LOG_DEBUG("cho_color_parse failed."); - return NULL; - } - } - state = CDS_OPTION_NAME; - break; - } - diagram_value[i] = *c; - i++; - break; - } - case CDS_COPY: { - if (is_whitespace(*c)) { - if (i == 0) { - break; - } - chord_to_copy[i] = 0; - if (current_content != -1) { - cho_log(LOG_ERR, "The define options 'base-fret', 'frets', 'fingers' and 'keys' are not allowed before the 'copy' option."); - return NULL; - } - enum Instrument ins = g_config->output->diagram->instrument; - current_content = chord_diagram_duplicate(diagram, custom_diagrams, custom_diagrams_len, name, chord_to_copy, ins); - if (current_content == -1) { - cho_log(LOG_ERR, "Can't copy the diagram for the chord '%s'" - "because no previous definition was found and also" - "no predefined chord diagram for the instrument '%s'" - "was found.", chord_to_copy, instruments[ins]); - return NULL; - } - i = 0; - state = CDS_OPTION_NAME; - break; - } - chord_to_copy[i] = *c; - i++; - break; - } - } - } - switch (state) { - case CDS_BASE_FRET: { - base_fret[i] = 0; - i = 0; - l = str_to_number(base_fret); - if (l == -1) { - LOG_DEBUG("str_to_number failed."); - cho_log(LOG_ERR, "Invalid base-fret value '%s' in chord diagram.", base_fret); - return NULL; - } - if (l == 0) { - cho_log(LOG_ERR, "Invalid base-fret value '%c' in chord diagram.", *c); - return NULL; - } - diagram->u.sd->base_fret = (int8_t)l; - break; - } - case CDS_KEYS: { - key[i] = 0; - if (strlen(key) > 0) { - l = str_to_number(key); - if (l == -1) { - LOG_DEBUG("str_to_number failed."); - cho_log(LOG_ERR, "Invalid number in keys in chord diagram."); - return NULL; - } - if (f > 23) { - cho_log(LOG_ERR, "Too many key values in chord diagram."); - return NULL; - } - diagram->u.kd->keys[f] = (int8_t)l; - } - break; - } - case CDS_DIAGRAM: { - diagram_value[i] = 0; - if (!strcmp(diagram_value, "off")) { - diagram->show = false; - } else - if (!strcmp(diagram_value, "on")) { - // INFO: but this is already the default - diagram->show = true; - } else { - free(diagram->color); - diagram->color = cho_color_parse(diagram_value); - if (!diagram->color) { - LOG_DEBUG("cho_color_parse failed."); - return NULL; - } - } - break; - } - case CDS_COPY: { - chord_to_copy[i] = 0; - if (current_content != -1) { - cho_log(LOG_ERR, "The define options 'base-fret', 'frets'," - "'fingers' and 'keys' are not allowed before the 'copy'" - "option."); - return NULL; - } - enum Instrument ins = g_config->output->diagram->instrument; - current_content = chord_diagram_duplicate(diagram, custom_diagrams, custom_diagrams_len, name, chord_to_copy, ins); - if (current_content == -1) { - cho_log(LOG_ERR, "Can't copy the diagram for the chord '%s' because" - "no previous definition was found and also no predefined" - "chord diagram for the instrument '%s' was found.", - chord_to_copy, instruments[ins]); - return NULL; - } - break; - } - default: - } - if ( - current_content == CDC_STRING && - fret_count > 0 && - finger_count > 0 && - fret_count != finger_count - ) { - cho_log(LOG_ERR, "The number of frets (%d) and fingers (%d) in the chord diagram must be equal.", fret_count, finger_count); - return NULL; - } - return diagram; -} - -static struct ChoText * -cho_text_new(void) -{ - struct ChoText *text = emalloc(sizeof(struct ChoText)); - text->style = cho_style_new_default(); - text->text = NULL; - return text; -} - -static void -cho_text_free(struct ChoText *text) -{ - if (!text) { - return; - } - cho_style_free(text->style); - free(text->text); - free(text); -} - -static struct ChoText * -cho_text_copy(struct ChoText *text) -{ - struct ChoText *copy = emalloc(sizeof(struct ChoText)); - copy->style = cho_style_copy(text->style); - copy->text = strdup(text->text); - return copy; -} - -static void -cho_text_above_free(struct ChoLineItemAbove *text_above) -{ - if (!text_above) { - return; - } - if (text_above->is_chord) { - cho_chord_free(text_above->u.chord); - } else { - cho_text_free(text_above->u.annot); - } - free(text_above); -} - -static struct ChoLineItemAbove * -cho_text_above_copy(struct ChoLineItemAbove *text_above) -{ - struct ChoLineItemAbove *copy = emalloc(sizeof(struct ChoLineItemAbove)); - copy->position = text_above->position; - copy->is_chord = text_above->is_chord; - if (copy->is_chord) { - copy->u.chord = cho_chord_copy(text_above->u.chord); - } else { - copy->u.annot = cho_text_copy(text_above->u.annot); - } - return copy; -} - -static struct ChoLineItem * -cho_line_item_new(void) -{ - struct ChoLineItem *item = emalloc(sizeof(struct ChoLineItem)); - item->is_text = true; - item->u.text = cho_text_new(); - return item; -} - -static void -cho_line_item_free(struct ChoLineItem *item) -{ - if (!item) { - return; - } - if (item->is_text) { - cho_text_free(item->u.text); - } else { - if (item->u.image) { - cho_image_free(item->u.image); - } - } - free(item); -} - -static struct ChoLineItem * -cho_line_item_copy(struct ChoLineItem *item) -{ - struct ChoLineItem *copy = emalloc(sizeof(struct ChoLineItem)); - if (item->is_text) { - copy->is_text = true; - copy->u.text = cho_text_copy(item->u.text); - } else { - copy->is_text = false; - copy->u.image = cho_image_copy(item->u.image); - } - return copy; -} - -static struct ChoLine * -cho_line_new(void) -{ - struct ChoLine *line = emalloc(sizeof(struct ChoLine)); - line->text_above = NULL; - line->items = NULL; - line->btype = BT_LINE; - return line; -} - -static void -cho_line_free(struct ChoLine *line) -{ - if (!line) { - return; - } - struct ChoLineItem **it; - for (it = line->items; *it; it++) { - cho_line_item_free(*it); - } - struct ChoLineItemAbove **above; - for (above = line->text_above; *above; above++) { - cho_text_above_free(*above); - } - free(line->items); - free(line->text_above); - free(line); -} - -static int -cho_line_compute_text_above_position(struct ChoLine *line, int ly, int te) -{ - if (ly == 0) { - return te; - } - size_t lyrics_len = 0; - int i; - for (i = ly-1; i >= 0; i--) { - if (line->items[i]->is_text) { - lyrics_len += strlen(line->items[i]->u.text->text); - } - } - return lyrics_len + te; -} - -static struct ChoSection * -cho_section_new(void) -{ - struct ChoSection *section = emalloc(sizeof(struct ChoSection)); - section->type = -1; - section->label = NULL; - section->lines = NULL; - return section; -} - -static void -cho_section_free(struct ChoSection *section) -{ - if (!section) { - return; - } - if (section->label) { - cho_text_free(section->label); - } - struct ChoLine **li; - for (li = section->lines; *li; li++) { - cho_line_free(*li); - } - free(section->lines); - free(section); -} - -static struct ChoSection * -cho_section_copy(struct ChoSection *section) -{ - struct ChoSection *copy = emalloc(sizeof(struct ChoSection)); - copy->type = section->type; - if (section->label) { - copy->label = cho_text_copy(section->label); - } else { - copy->label = NULL; - } - copy->lines = NULL; - int li, c, ly; - for (li = 0; section->lines[li]; li++) { - copy->lines = erealloc(copy->lines, (li+1) * sizeof(struct ChoLine *)); - copy->lines[li] = cho_line_new(); - copy->lines[li]->btype = section->lines[li]->btype; - for (c = 0; section->lines[li]->text_above[c]; c++) { - copy->lines[li]->text_above = erealloc(copy->lines[li]->text_above, (c+1) * sizeof(struct ChoLineItemAbove *)); - copy->lines[li]->text_above[c] = cho_text_above_copy(section->lines[li]->text_above[c]); - } - copy->lines[li]->text_above = erealloc(copy->lines[li]->text_above, (c+1) * sizeof(struct ChoLineItemAbove *)); - copy->lines[li]->text_above[c] = NULL; - for (ly = 0; section->lines[li]->items[ly]; ly++) { - copy->lines[li]->items = erealloc(copy->lines[li]->items, (ly+1) * sizeof(struct ChoLineItem *)); - copy->lines[li]->items[ly] = cho_line_item_copy(section->lines[li]->items[ly]); - } - copy->lines[li]->items = erealloc(copy->lines[li]->items, (ly+1) * sizeof(struct ChoLineItem *)); - copy->lines[li]->items[ly] = NULL; - } - copy->lines = erealloc(copy->lines, (li+1) * sizeof(struct ChoLine *)); - copy->lines[li] = NULL; - return copy; -} - -static struct ChoSong * -cho_song_new(void) -{ - struct ChoSong *song = emalloc(sizeof(struct ChoSong)); - song->metadata = NULL; - song->sections = NULL; - song->diagrams = NULL; - memset(song->present_text_types, 0, TT_LENGTH); - return song; -} - -int -cho_song_count(struct ChoSong **songs) -{ - int i; - for (i = 0; songs[i]; i++); - return i; -} - -static const char * -cho_song_get_title(struct ChoSong *song) -{ - struct ChoMetadata **m; - for (m = song->metadata; *m; m++) { - if (!strcmp((*m)->name, "sorttitle")) { - return (*m)->value; - } - } - for (m = song->metadata; *m; m++) { - if (!strcmp((*m)->name, "title")) { - return (*m)->value; - } - } - return NULL; -} - -int -cho_song_compare(const void *a, const void *b) -{ - struct ChoSong *song; - const char *a_title, *b_title; - song = *(struct ChoSong **)a; - a_title = cho_song_get_title(song); - song = *(struct ChoSong **)b; - b_title = cho_song_get_title(song); - return str_compare(a_title, b_title); -} - -static void -cho_song_free(struct ChoSong *song) -{ - if (!song) { - return; - } - struct ChoMetadata **m; - struct ChoSection **se; - struct ChordDiagram **dia; - for (m = song->metadata; *m; m++) { - cho_metadata_free(*m); - } - for (se = song->sections; *se; se++) { - cho_section_free(*se); - } - for (dia = song->diagrams; *dia; dia++) { - chord_diagram_free(*dia); - } - free(song->metadata); - free(song->sections); - free(song->diagrams); - free(song); -} - -void -cho_songs_free(struct ChoSong **songs) -{ - if (!songs) { - return; - } - struct ChoSong **so; - for (so = songs; *so; so++) { - cho_song_free(*so); - } - free(songs); -} - -static struct ChoSection * -cho_find_previous_chorus(struct ChoSection **sections, int se) -{ - int i; - for (i = se; i>=0; i--) { - if (sections[i]->type == ST_CHORUS) { - return sections[i]; - } - } - return NULL; -} - -static struct ChoDirective * -cho_directive_new(void) -{ - struct ChoDirective *directive = emalloc(sizeof(struct ChoDirective)); - directive->dtype = -1; - directive->stype = -1; - directive->position = -1; - directive->sprop = -1; - directive->ttype = -1; - directive->btype = -1; - directive->meta = -1; - directive->ctype = -1; - directive->style = cho_style_new_default(); - return directive; -} - -static void -cho_directive_free(struct ChoDirective *directive) -{ - if (!directive) { - return; - } - cho_style_free(directive->style); - free(directive); -} - -static struct ChoDirective * -cho_directive_parse(const char *name) -{ - struct ChoDirective *directive = cho_directive_new(); - if ( - !strcmp(name, "start_of_chorus") || - !strcmp(name, "soc") - ) { - directive->dtype = DT_ENVIRONMENT; - directive->position = POS_START; - directive->stype = ST_CHORUS; - directive->ttype = TT_CHORUS; - return directive; - } else if ( - !strcmp(name, "end_of_chorus") || - !strcmp(name, "eoc") - ) { - directive->dtype = DT_ENVIRONMENT; - directive->position = POS_END; - directive->stype = ST_CHORUS; - directive->ttype = TT_TEXT; - return directive; - } else if (!strcmp(name, "chorus")) { - directive->dtype = DT_ENVIRONMENT; - directive->position = POS_NO; - directive->ttype = TT_LABEL; - return directive; - } else if ( - !strcmp(name, "start_of_verse") || - !strcmp(name, "sov") - ) { - directive->dtype = DT_ENVIRONMENT; - directive->position = POS_START; - directive->stype = ST_VERSE; - directive->ttype = TT_TEXT; - return directive; - } else if ( - !strcmp(name, "end_of_verse") || - !strcmp(name, "eov") - ) { - directive->dtype = DT_ENVIRONMENT; - directive->position = POS_END; - directive->stype = ST_VERSE; - directive->ttype = TT_TEXT; - return directive; - } else if ( - !strcmp(name, "start_of_bridge") || - !strcmp(name, "sob") - ) { - directive->dtype = DT_ENVIRONMENT; - directive->position = POS_START; - directive->stype = ST_BRIDGE; - directive->ttype = TT_TEXT; - return directive; - } else if ( - !strcmp(name, "end_of_bridge") || - !strcmp(name, "eob") - ) { - directive->dtype = DT_ENVIRONMENT; - directive->position = POS_END; - directive->stype = ST_BRIDGE; - directive->ttype = TT_TEXT; - return directive; - } else if ( - !strcmp(name, "start_of_tab") || - !strcmp(name, "sot") - ) { - directive->dtype = DT_ENVIRONMENT; - directive->position = POS_START; - directive->stype = ST_TAB; - directive->ttype = TT_TAB; - return directive; - } else if ( - !strcmp(name, "end_of_tab") || - !strcmp(name, "eot") - ) { - directive->dtype = DT_ENVIRONMENT; - directive->position = POS_END; - directive->stype = ST_TAB; - directive->ttype = TT_TEXT; - return directive; - } else if ( - !strcmp(name, "start_of_grid") || - !strcmp(name, "sog") - ) { - directive->dtype = DT_ENVIRONMENT; - directive->position = POS_START; - directive->stype = ST_GRID; - directive->ttype = TT_GRID; - return directive; - } else if ( - !strcmp(name, "end_of_grid") || - !strcmp(name, "eog") - ) { - directive->dtype = DT_ENVIRONMENT; - directive->position = POS_END; - directive->stype = ST_GRID; - directive->ttype = TT_TEXT; - return directive; - } - if ( - !strcmp(name, "title") || - !strcmp(name, "t") - ) { - directive->dtype = DT_METADATA; - directive->meta = TITLE; - cho_style_free(directive->style); - g_prev_ttype = g_current_ttype; - g_current_ttype = TT_TITLE; - directive->style = cho_style_new_default(); - g_current_ttype = g_prev_ttype; - return directive; - - } else if ( - !strcmp(name, "subtitle") || - !strcmp(name, "st") - ) { - directive->dtype = DT_METADATA; - directive->meta = SUBTITLE; - cho_style_free(directive->style); - g_prev_ttype = g_current_ttype; - g_current_ttype = TT_SUBTITLE; - directive->style = cho_style_new_default(); - g_current_ttype = g_prev_ttype; - return directive; - } - if ( - !strcmp(name, "sorttitle") || - !strcmp(name, "artist") || - !strcmp(name, "composer") || - !strcmp(name, "lyricist") || - !strcmp(name, "copyright") || - !strcmp(name, "album") || - !strcmp(name, "year") || - !strcmp(name, "key") || - !strcmp(name, "time") || - !strcmp(name, "tempo") || - !strcmp(name, "duration") || - !strcmp(name, "capo") || - !strcmp(name, "meta") || - !strcmp(name, "arranger") - ) { - directive->dtype = DT_METADATA; - return directive; - } - if ( - !strcmp(name, "comment") || - !strcmp(name, "c") || - !strcmp(name, "highlight") - ) { - directive->dtype = DT_FORMATTING; - g_prev_ttype = g_current_ttype; - g_current_ttype = TT_COMMENT; - cho_style_free(directive->style); - directive->style = cho_style_new_default(); - g_current_ttype = g_prev_ttype; - directive->ttype = TT_COMMENT; - return directive; - } else if ( - !strcmp(name, "comment_italic") || - !strcmp(name, "ci") - ) { - directive->dtype = DT_FORMATTING; - g_prev_ttype = g_current_ttype; - g_current_ttype = TT_COMMENT_ITALIC; - cho_style_free(directive->style); - directive->style = cho_style_new_default(); - g_current_ttype = g_prev_ttype; - directive->ttype = TT_COMMENT_ITALIC; - return directive; - } else if ( - !strcmp(name, "comment_box") || - !strcmp(name, "cb") - ) { - directive->dtype = DT_FORMATTING; - g_prev_ttype = g_current_ttype; - g_current_ttype = TT_COMMENT_BOX; - cho_style_free(directive->style); - directive->style = cho_style_new_default(); - g_current_ttype = g_prev_ttype; - directive->ttype = TT_COMMENT_BOX; - return directive; - } - if (!strcmp(name, "image")) { - directive->dtype = DT_IMAGE; - return directive; - } - if ( - !strcmp(name, "new_song") || - !strcmp(name, "ns") - ) { - directive->dtype = DT_PREAMBLE; - directive->stype = ST_NEWSONG; - return directive; - } - if ( - !strcmp(name, "chordfont") || - !strcmp(name, "cf") - ) { - directive->dtype = DT_FONT; - directive->sprop = SPT_FONT; - directive->ttype = TT_CHORD; - return directive; - } else if ( - !strcmp(name, "chordsize") || - !strcmp(name, "cs") - ) { - directive->dtype = DT_FONT; - directive->sprop = SPT_SIZE; - directive->ttype = TT_CHORD; - return directive; - } else if (!strcmp(name, "chordcolour")) { - directive->dtype = DT_FONT; - directive->sprop = SPT_COLOR; - directive->ttype = TT_CHORD; - return directive; - } else if (!strcmp(name, "chorusfont")) { - directive->dtype = DT_FONT; - directive->sprop = SPT_FONT; - directive->ttype = TT_CHORUS; - return directive; - } else if (!strcmp(name, "chorussize")) { - directive->dtype = DT_FONT; - directive->sprop = SPT_SIZE; - directive->ttype = TT_CHORUS; - return directive; - } else if (!strcmp(name, "choruscolour")) { - directive->dtype = DT_FONT; - directive->sprop = SPT_COLOR; - directive->ttype = TT_CHORUS; - return directive; - } else if (!strcmp(name, "gridfont")) { - directive->dtype = DT_FONT; - directive->sprop = SPT_FONT; - directive->ttype = TT_GRID; - return directive; - } else if (!strcmp(name, "gridsize")) { - directive->dtype = DT_FONT; - directive->sprop = SPT_SIZE; - directive->ttype = TT_GRID; - return directive; - } else if (!strcmp(name, "gridcolour")) { - directive->dtype = DT_FONT; - directive->sprop = SPT_COLOR; - directive->ttype = TT_GRID; - return directive; - } else if (!strcmp(name, "tabfont")) { - directive->dtype = DT_FONT; - directive->sprop = SPT_FONT; - directive->ttype = TT_TAB; - return directive; - } else if (!strcmp(name, "tabsize")) { - directive->dtype = DT_FONT; - directive->sprop = SPT_SIZE; - directive->ttype = TT_TAB; - return directive; - } else if (!strcmp(name, "tabcolour")) { - directive->dtype = DT_FONT; - directive->sprop = SPT_COLOR; - directive->ttype = TT_TAB; - return directive; - } else if ( - !strcmp(name, "textfont") || - !strcmp(name, "tf") - ) { - directive->dtype = DT_FONT; - directive->sprop = SPT_FONT; - directive->ttype = TT_TEXT; - return directive; - } else if ( - !strcmp(name, "textsize") || - !strcmp(name, "ts") - ) { - directive->dtype = DT_FONT; - directive->sprop = SPT_SIZE; - directive->ttype = TT_TEXT; - return directive; - } else if (!strcmp(name, "textcolour")) { - directive->dtype = DT_FONT; - directive->sprop = SPT_COLOR; - directive->ttype = TT_TEXT; - return directive; - } else if (!strcmp(name, "titlefont")) { - directive->dtype = DT_FONT; - directive->sprop = SPT_FONT; - directive->ttype = TT_TITLE; - return directive; - } else if (!strcmp(name, "titlesize")) { - directive->dtype = DT_FONT; - directive->sprop = SPT_SIZE; - directive->ttype = TT_TITLE; - return directive; - } else if (!strcmp(name, "titlecolour")) { - directive->dtype = DT_FONT; - directive->sprop = SPT_COLOR; - directive->ttype = TT_TITLE; - return directive; - } else if (!strcmp(name, "tocfont")) { - directive->dtype = DT_FONT; - directive->sprop = SPT_FONT; - directive->ttype = TT_TOC; - } else if (!strcmp(name, "tocsize")) { - directive->dtype = DT_FONT; - directive->sprop = SPT_SIZE; - directive->ttype = TT_TOC; - } else if (!strcmp(name, "toccolour")) { - directive->dtype = DT_FONT; - directive->sprop = SPT_COLOR; - directive->ttype = TT_TOC; - } /* else if (!strcmp(name, "footerfont")) { - } else if (!strcmp(name, "footersize")) { - } else if (!strcmp(name, "footercolour")) { - } else if (!strcmp(name, "labelfont")) { - } else if (!strcmp(name, "labelsize")) { - } else if (!strcmp(name, "labelcolour")) { - } */ - if (!strcmp(name, "transpose")) { - directive->dtype = DT_CHORD; - directive->ctype = TRANSPOSE; - return directive; - } else if (!strcmp(name, "define")) { - directive->dtype = DT_CHORD; - directive->ctype = DEFINE; - return directive; - } /* else if (!strcmp(name, "chord")) { - } */ - if ( - !strcmp(name, "new_page") || - !strcmp(name, "np") - ) { - directive->dtype = DT_OUTPUT; - directive->btype = BT_PAGE; - return directive; - } else if ( - !strcmp(name, "column_break") || - !strcmp(name, "colb") - ) { - directive->dtype = DT_OUTPUT; - directive->btype = BT_COLUMN; - return directive; - } - if (str_starts_with(name, "start_of_")) { - directive->dtype = DT_ENVIRONMENT; - directive->position = POS_START; - directive->stype = ST_CUSTOM; - directive->ttype = TT_TEXT; - return directive; - } else if (str_starts_with(name, "end_of_")) { - directive->dtype = DT_ENVIRONMENT; - directive->position = POS_END; - directive->stype = ST_CUSTOM; - directive->ttype = TT_TEXT; - return directive; - } - if (str_starts_with(name, "x_")) { - directive->dtype = DT_EXTENSION; - return directive; - } - directive->dtype = DT_CUSTOM; - return directive; -} - -static char * -cho_directive_label_parse(const char *directive_name, const char *str) -{ - char *label_name = NULL; - char c; - enum OptionState state = OS_NAME; - enum AttrValueSyntax avs = -1; - char name[5+1]; - char value[URL_MAX_LEN+1]; - int n = 0; - int v = 0; - memset(name, 0, sizeof(name)); - memset(value, 0, sizeof(value)); - int i; - for (i = 0; str[i]; i++) { - c = str[i]; - switch (state) { - case OS_NAME: - if (is_whitespace(c)) { - if (n == 0) { - break; - } else { - name[n] = 0; - cho_log(LOG_ERR, "Option with name '%s' in environment directive '%s' has no value.", name, directive_name); - return NULL; - } - } - if (c == '=') { - name[n] = 0; - if (strcmp(name, "label") != 0) { - cho_log(LOG_ERR, "Invalid option name '%s' in environment directive '%s'.", name, directive_name); - } - memset(name, 0, n); - n = 0; - state = OS_VALUE; - break; - } - if (n > 4) { - cho_log(LOG_ERR, "Option name in environment directive '%s' is too long.", directive_name); - return NULL; - } - name[n] = c; - n++; - break; - case OS_VALUE: - if (avs == -1) { - if (is_whitespace(c)) { - cho_log(LOG_ERR, "Whitespace character after equals sign in environment directive '%s' is invalid.", directive_name); - return NULL; - } - if (c == '\'') { - avs = AVS_APOSTROPHE; - } else if (c == '"') { - avs = AVS_QUOTATION_MARK; - } else { - avs = AVS_UNQUOTED; - value[v] = c; - v++; - } - break; - } - if (c == '\n') { - cho_log(LOG_ERR, "Newline character inside an option value in environment directive '%s' is invalid.", directive_name); - return NULL; - } - if ( - (avs == AVS_APOSTROPHE && c == '\'') || - (avs == AVS_QUOTATION_MARK && c == '"') || - (avs == AVS_UNQUOTED && (c == ' ' || c == '\t')) - ) { - value[v] = 0; - label_name = strdup(value); - memset(value, 0, v); - v = 0; - avs = -1; - state = OS_NAME; - break; - } - value[v] = c; - v++; - break; - } - } - if (avs == AVS_UNQUOTED) { - value[v] = 0; - label_name = strdup(value); - } - return label_name; -} - -struct ChoSong ** -cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) -{ - g_config = config; - g_chordpro_filepath = chordpro_filepath; - bool is_chord_already_initialized = false; - bool is_maybe_end_of_tab_directive = false; - bool directive_has_tag = false; - char buf = 0; - char prev_buf = '\n'; - char directive_name[128]; - char directive_value[4096]; - char chord[15]; - char tag_start[6]; - char tag_end[6]; - char custom_directive[64]; - char *label, *metadata_value, *stripped_directive_value; - enum State state = STATE_LYRICS; - enum State state_before_comment = STATE_LYRICS; - enum State state_before_tag = STATE_LYRICS; - enum State state_before_backslash = STATE_LYRICS; - int dn = 0; - int dv = 0; - int ch = 0; - int c = 0; - int m = 0; - int ly = 0; - int t = 0; - int so = 0; - int se = 0; - int li = 0; - int ta = -1; - int te = 0; - int at = 0; - int atn = 0; - int atv = 0; - int ann = 0; - int text_above_pos; - int transpose; - int th = 0; - int dia = 0; - size_t read; - g_transpose_history = erealloc(g_transpose_history, (th+1) * sizeof(int *)); - g_transpose_history[th] = 0; - g_transpose = &g_transpose_history[th]; - th++; - enum AttrValueSyntax avs = -1; - struct ChoDirective *directive = NULL; - struct ChoMetadata *metadata = NULL; - struct ChoLine ***lines; - struct ChoSong **songs = emalloc(sizeof(struct ChoSong *)); - songs[so] = cho_song_new(); - songs[so]->sections = emalloc((se+1) * sizeof(struct ChoSection *)); - songs[so]->sections[se] = cho_section_new(); - songs[so]->sections[se]->lines = emalloc(sizeof(struct ChoLine *)); - songs[so]->sections[se]->lines[li] = cho_line_new(); - lines = &songs[so]->sections[se]->lines; - (*lines)[li]->items = emalloc(sizeof(struct ChoLineItem *)); - (*lines)[li]->items[ly] = cho_line_item_new(); - songs[so]->present_text_types[TT_TOC] = config->output->toc->show; - struct Tag **tags = NULL; - struct ChoStyle *tag_style; - struct StyleProperty sprop; - struct ChoChord *tmp_chord; - struct ChoSection *chorus; - struct ChoImage *image; - struct ChordDiagram *diagram; - while (feof(fp) == 0) { - read = fread(&buf, 1, 1, fp); - if (read == 1) { - if (buf == '\n') { - g_line_number++; - } - // printf("state: %s, buf: %c\n", state_enums[state], buf); - if (buf == '\r') { - continue; - } - switch (state) { - case STATE_LYRICS: { - if (prev_buf == '\n' && buf == '#') { - state_before_comment = STATE_LYRICS; - state = STATE_COMMENT; - break; - } - if (prev_buf == '\n' && buf == '{') { - state = STATE_DIRECTIVE_NAME; - break; - } - if (buf == '[') { - state = STATE_CHORD; - break; - } - if (buf == '<') { - if ((*lines)[li]->items[ly]->is_text) { - (*lines)[li]->items[ly]->u.text->text = erealloc((*lines)[li]->items[ly]->u.text->text, (te+1) * sizeof(char)); - (*lines)[li]->items[ly]->u.text->text[te] = 0; - if (strlen((*lines)[li]->items[ly]->u.text->text) == 0) { - cho_line_item_free((*lines)[li]->items[ly]); - } else { - ly++; - } - } else { - ly++; - } - te = 0; - (*lines)[li]->items = erealloc((*lines)[li]->items, (ly+1) * sizeof(struct ChoLineItem *)); - (*lines)[li]->items[ly] = cho_line_item_new(); - state_before_tag = STATE_LYRICS; - state = STATE_MARKUP_TAG; - break; - } - if (buf == '\n') { - if (prev_buf == '\\') { - state_before_backslash = STATE_LYRICS; - state = STATE_BACKSLASH; - te--; // INFO: This will later overwrite the backslash - break; - } - if (ta > -1 && !tags[ta]->is_closed && strcmp(tags[ta]->name, "img")) { - cho_log(LOG_ERR, "Tag has to be closed on same line."); - return NULL; - } - if ((*lines)[li]->items[ly]->is_text) { - (*lines)[li]->items[ly]->u.text->text = erealloc((*lines)[li]->items[ly]->u.text->text, (te+1) * sizeof(char)); - (*lines)[li]->items[ly]->u.text->text[te] = 0; - if (strlen((*lines)[li]->items[ly]->u.text->text) == 0) { - cho_line_item_free((*lines)[li]->items[ly]); - if (ly == 0) { - if ( - !(*lines)[li]->text_above && - (*lines)[li]->btype == BT_LINE - ) { - free((*lines)[li]->items); - free((*lines)[li]); - *lines = erealloc(*lines, (li+1) * sizeof(struct ChoLine *)); - (*lines)[li] = cho_line_new(); - (*lines)[li]->items = erealloc((*lines)[li]->items, (ly+1) * sizeof(struct ChoLineItem *)); - (*lines)[li]->items[ly] = cho_line_item_new(); - break; - } - } - } else { - ly++; - } - } else { - ly++; - } - (*lines)[li]->items = erealloc((*lines)[li]->items, (ly+1) * sizeof(struct ChoLineItem *)); - (*lines)[li]->items[ly] = NULL; - ly = 0; - te = 0; - (*lines)[li]->text_above = erealloc((*lines)[li]->text_above, (c+1) * sizeof(struct ChoLineItemAbove *)); - (*lines)[li]->text_above[c] = NULL; - c = 0; - li++; - *lines = erealloc(*lines, (li+1) * sizeof(struct ChoLine *)); - (*lines)[li] = cho_line_new(); - (*lines)[li]->items = erealloc((*lines)[li]->items, (ly+1) * sizeof(struct ChoLineItem *)); - (*lines)[li]->items[ly] = cho_line_item_new(); - break; - } - (*lines)[li]->items[ly]->u.text->text = erealloc((*lines)[li]->items[ly]->u.text->text, (te+1) * sizeof(char)); - (*lines)[li]->items[ly]->u.text->text[te] = buf; - te++; - break; - } - case STATE_BACKSLASH: { - if (!is_whitespace(buf)) { - if (fseek(fp, -1, SEEK_CUR)) { - LOG_DEBUG("fseek failed."); - return NULL; - } - printf("Hopefully not losing byte 0x%02X|%c\n", buf, buf); - state = state_before_backslash; - break; - } - break; - } - case STATE_DIRECTIVE_NAME: { - if (buf == '}') { - directive_name[dn] = 0; - dn = 0; - directive = cho_directive_parse(directive_name); - /* printf( - "directive: '%s'\ndtype: %s, stype: %s, position: %s\n", - directive_name, cho_debug_dtype(directive->dtype), cho_debug_the_stype(directive->stype), cho_debug_the_pos(directive->position) - ); */ - switch (directive->dtype) { - case DT_ENVIRONMENT: { - g_current_ttype = directive->ttype; - switch (directive->position) { - case POS_START: { - if (directive->stype == ST_CUSTOM) { - memset(custom_directive, 0, sizeof(custom_directive)); - strcpy(custom_directive, &directive_name[9]); - } - cho_line_item_free((*lines)[li]->items[ly]); - free((*lines)[li]->items); - ly = 0; - free((*lines)[li]); - (*lines)[li] = NULL; - se++; - songs[so]->sections = erealloc(songs[so]->sections, (se+1) * sizeof(struct ChoSection *)); - songs[so]->sections[se] = cho_section_new(); - songs[so]->sections[se]->type = directive->stype; - li = 0; - lines = &songs[so]->sections[se]->lines; - *lines = emalloc(sizeof(struct ChoLine *)); - (*lines)[li] = cho_line_new(); - (*lines)[li]->items = emalloc(sizeof(struct ChoLineItem *)); - (*lines)[li]->items[ly] = cho_line_item_new(); - songs[so]->present_text_types[directive->ttype] = true; - break; - } - case POS_END: { - if (directive->stype == songs[so]->sections[se]->type) { - if (directive->stype == ST_CUSTOM) { - if (strcmp(custom_directive, &directive_name[7]) != 0) { - break; - } - } - cho_line_item_free((*lines)[li]->items[ly]); - free((*lines)[li]->items); - ly = 0; - free((*lines)[li]); - (*lines)[li] = NULL; - se++; - songs[so]->sections = erealloc(songs[so]->sections, (se+1) * sizeof(struct ChoSection *)); - songs[so]->sections[se] = cho_section_new(); - li = 0; - lines = &songs[so]->sections[se]->lines; - *lines = emalloc(sizeof(struct ChoLine *)); - (*lines)[li] = cho_line_new(); - (*lines)[li]->items = emalloc(sizeof(struct ChoLineItem *)); - (*lines)[li]->items[ly] = cho_line_item_new(); - } - break; - } - case POS_NO: { - /* INFO: {chorus} */ - chorus = cho_find_previous_chorus(songs[so]->sections, se); - if (chorus) { - if (config->output->chorus->quote) { - if ((*lines)[li]->items[ly]->is_text) { - (*lines)[li]->items[ly]->u.text->text = erealloc((*lines)[li]->items[ly]->u.text->text, (te+1) * sizeof(char)); - (*lines)[li]->items[ly]->u.text->text[te] = 0; - } - ly++; - (*lines)[li]->items = erealloc((*lines)[li]->items, (ly+1) * sizeof(struct ChoLineItem *)); - (*lines)[li]->items[ly] = NULL; - ly = 0; - te = 0; - (*lines)[li]->text_above = erealloc((*lines)[li]->text_above, (c+1) * sizeof(struct ChoLineItemAbove *)); - (*lines)[li]->text_above[c] = NULL; - c = 0; - cho_line_free((*lines)[li]); - (*lines)[li] = NULL; - li = 0; - se++; - songs[so]->sections = erealloc(songs[so]->sections, (se+1) * sizeof(struct ChoSection *)); - songs[so]->sections[se] = cho_section_copy(chorus); - se++; - songs[so]->sections = erealloc(songs[so]->sections, (se+1) * sizeof(struct ChoSection *)); - songs[so]->sections[se] = cho_section_new(); - lines = &songs[so]->sections[se]->lines; - *lines = emalloc(sizeof(struct ChoLine *)); - (*lines)[li] = cho_line_new(); - (*lines)[li]->items = emalloc(sizeof(struct ChoLineItem *)); - (*lines)[li]->items[ly] = cho_line_item_new(); - } else { - if (chorus->label) { - label = strdup(chorus->label->text); - } else { - label = strdup(config->output->chorus->label); - } - if ((*lines)[li]->items[ly]->is_text) { - (*lines)[li]->items[ly]->u.text->text = erealloc((*lines)[li]->items[ly]->u.text->text, (te+1) * sizeof(char)); - (*lines)[li]->items[ly]->u.text->text[te] = 0; - if (strlen((*lines)[li]->items[ly]->u.text->text) == 0) { - cho_line_item_free((*lines)[li]->items[ly]); - } else { - ly++; - } - } else { - ly++; - } - (*lines)[li]->items = erealloc((*lines)[li]->items, (ly+1) * sizeof(struct ChoLineItem *)); - (*lines)[li]->items[ly] = cho_line_item_new(); - cho_style_free((*lines)[li]->items[ly]->u.text->style); - g_current_ttype = TT_LABEL; - (*lines)[li]->items[ly]->u.text->style = cho_style_new_default(); - (*lines)[li]->items[ly]->u.text->text = label; - te += strlen(label); - } - } else { - if (config->output->chorus->quote) { - cho_log(LOG_WARN, "Can't quote chorus because it's not defined previously."); - } - label = strdup(config->output->chorus->label); - if ((*lines)[li]->items[ly]->is_text) { - (*lines)[li]->items[ly]->u.text->text = erealloc((*lines)[li]->items[ly]->u.text->text, (te+1) * sizeof(char)); - (*lines)[li]->items[ly]->u.text->text[te] = 0; - if (strlen((*lines)[li]->items[ly]->u.text->text) == 0) { - cho_line_item_free((*lines)[li]->items[ly]); - } else { - ly++; - } - } else { - ly++; - } - (*lines)[li]->items = erealloc((*lines)[li]->items, (ly+1) * sizeof(struct ChoLineItem *)); - (*lines)[li]->items[ly] = cho_line_item_new(); - cho_style_free((*lines)[li]->items[ly]->u.text->style); - g_current_ttype = TT_LABEL; - (*lines)[li]->items[ly]->u.text->style = cho_style_new_default(); - (*lines)[li]->items[ly]->u.text->text = label; - te += strlen(label); - } - break; - } - default: - cho_log(LOG_ERR, "Invalid position value '%d'.", directive->position); - return NULL; - } - break; - } - case DT_METADATA: - cho_log(LOG_WARN, "Ignoring metadata directive '%s' because it has no value.", directive_name); - break; - case DT_FORMATTING: - cho_log(LOG_WARN, "Formatting directive '%s' has no value.", directive_name); - break; - case DT_IMAGE: - cho_log(LOG_ERR, "Directive 'image' has no value."); - return NULL; - case DT_PREAMBLE: { - // INFO: The only preamble directive is 'new_song' - cho_line_item_free((*lines)[li]->items[ly]); - free((*lines)[li]->items); - free((*lines)[li]); - (*lines)[li] = NULL; - songs[so]->metadata = erealloc(songs[so]->metadata, (m+1) * sizeof(struct ChoMetadata *)); - songs[so]->metadata[m] = NULL; - se++; - songs[so]->sections = erealloc(songs[so]->sections, (se+1) * sizeof(struct ChoSection *)); - songs[so]->sections[se] = NULL; - songs[so]->diagrams = erealloc(songs[so]->diagrams, (dia+1) * sizeof(struct ChordDiagram *)); - songs[so]->diagrams[dia] = NULL; - if (!cho_style_reset_default()) { - LOG_DEBUG("cho_style_reset_default failed."); - return NULL; - } - so++; - songs = erealloc(songs, (so+1) * sizeof(struct ChoSong *)); - songs[so] = cho_song_new(); - se = 0; - li = 0; - ly = 0; - m = 0; - dia = 0; - songs[so]->sections = emalloc((se+1) * sizeof(struct ChoSection *)); - songs[so]->sections[se] = cho_section_new(); - lines = &songs[so]->sections[se]->lines; - *lines = erealloc(*lines, (li+1) * sizeof(struct ChoLine *)); - (*lines)[li] = cho_line_new(); - (*lines)[li]->items = emalloc(sizeof(struct ChoLineItem *)); - (*lines)[li]->items[ly] = cho_line_item_new(); - break; - } - case DT_FONT: { - sprop.ttype = directive->ttype; - sprop.type = directive->sprop; - switch (directive->sprop) { - case SPT_FONT: - sprop.u.font_name = NULL; - break; - case SPT_SIZE: - sprop.u.font_size = EMPTY_DOUBLE; - break; - case SPT_COLOR: - sprop.u.foreground_color = NULL; - break; - default: - cho_log(LOG_ERR, "Invalid style property type '%d'.", directive->sprop); - return NULL; - } - if (!cho_style_change_default(sprop)) { - LOG_DEBUG("cho_style_change_default failed."); - return NULL; - } - break; - } - case DT_CHORD: { - switch (directive->ctype) { - case TRANSPOSE: - g_transpose--; - th--; - break; - case DEFINE: - cho_log(LOG_WARN, "Ignoring chord directive '%s' because it has no value.", directive_name); - break; - } - break; - } - case DT_OUTPUT: - if (directive->btype != -1) { - (*lines)[li]->btype = directive->btype; - } - break; - case DT_EXTENSION: - // INFO: Such a directive should not be logged. - break; - case DT_CUSTOM: - cho_log(LOG_INFO, "Ignoring custom directive '%s'.", directive_name); - break; - default: - cho_log(LOG_ERR, "Invalid directive '%s'.", directive_name); - return NULL; - } - if (directive->stype == ST_TAB) { - state = STATE_TAB; - } else { - state = STATE_LYRICS; - } - cho_directive_free(directive); - directive = NULL; - break; - } - if (buf == '{') { - cho_log(LOG_ERR, "Can't start a new directive if the previous one is not yet closed."); - return NULL; - } - if (buf == '\n') { - if (prev_buf == '\\') { - state_before_backslash = STATE_DIRECTIVE_NAME; - state = STATE_BACKSLASH; - dn--; - break; - } - cho_log(LOG_ERR, "Can't have a newline in a directive name."); - return NULL; - } - if (buf == ':' || buf == ' ') { - directive_name[dn] = 0; - dn = 0; - state = STATE_DIRECTIVE_VALUE; - break; - } - directive_name[dn] = buf; - dn++; - break; - } - case STATE_DIRECTIVE_VALUE: { - if (buf == '}') { - directive_value[dv] = 0; - dv = 0; - stripped_directive_value = str_remove_leading_whitespace(directive_value); - directive = cho_directive_parse(directive_name); - /* printf( - "directive: '%s'\ndtype: %s, stype: %s, position: %s\n", - directive_name, dtype(directive->dtype), the_stype(directive->stype), pos(directive->position) - ); */ - switch (directive->dtype) { - case DT_ENVIRONMENT: { - if (strlen(stripped_directive_value) > 0) { - songs[so]->present_text_types[TT_LABEL] = true; - } - g_current_ttype = directive->ttype; - switch (directive->position) { - case POS_START: { - if (directive->stype == ST_CUSTOM) { - memset(custom_directive, 0, sizeof(custom_directive)); - strcpy(custom_directive, &directive_name[9]); - } - cho_line_item_free((*lines)[li]->items[ly]); - free((*lines)[li]->items); - ly = 0; - free((*lines)[li]); - (*lines)[li] = NULL; - se++; - songs[so]->sections = erealloc(songs[so]->sections, (se+1) * sizeof(struct ChoSection *)); - songs[so]->sections[se] = cho_section_new(); - songs[so]->sections[se]->type = directive->stype; - songs[so]->sections[se]->label = emalloc(sizeof(struct ChoText)); - if (strstr(directive_value, "=")) { - label = cho_directive_label_parse(directive_name, directive_value); - if (!label) { - LOG_DEBUG("cho_directive_label_parse failed."); - cho_log(LOG_ERR, "Failed to parse the section label. You have to ways of specifying a label:\n\t\t\t1. {start_of_*: label=\"Label name\"}\n\t\t\t2. {start_of*: Label name}"); - return NULL; - } - songs[so]->sections[se]->label->text = label; - } else { - songs[so]->sections[se]->label->text = strdup(stripped_directive_value); - } - songs[so]->sections[se]->label->style = cho_style_new_from_config(TT_LABEL); - if (directive_has_tag) { - cho_style_complement(songs[so]->sections[se]->label->style, tags[ta]->style, &tags[ta]->style_presence); - directive_has_tag = false; - } - li = 0; - lines = &songs[so]->sections[se]->lines; - *lines = emalloc(sizeof(struct ChoLine *)); - (*lines)[li] = cho_line_new(); - (*lines)[li]->items = emalloc(sizeof(struct ChoLineItem *)); - (*lines)[li]->items[ly] = cho_line_item_new(); - songs[so]->present_text_types[directive->ttype] = true; - break; - } - case POS_END: { - if (directive->stype == songs[so]->sections[se]->type) { - cho_line_item_free((*lines)[li]->items[ly]); - free((*lines)[li]->items); - ly = 0; - free((*lines)[li]); - (*lines)[li] = NULL; - se++; - songs[so]->sections = erealloc(songs[so]->sections, (se+1) * sizeof(struct ChoSection *)); - songs[so]->sections[se] = cho_section_new(); - li = 0; - lines = &songs[so]->sections[se]->lines; - *lines = emalloc(sizeof(struct ChoLine *)); - (*lines)[li] = cho_line_new(); - (*lines)[li]->items = emalloc(sizeof(struct ChoLineItem *)); - (*lines)[li]->items[ly] = cho_line_item_new(); - } - break; - } - case POS_NO: { - /* INFO: {chorus: ...} */ - label = strdup(stripped_directive_value); - chorus = cho_find_previous_chorus(songs[so]->sections, se); - if (chorus) { - if (config->output->chorus->quote) { - if ((*lines)[li]->items[ly]->is_text) { - (*lines)[li]->items[ly]->u.text->text = erealloc((*lines)[li]->items[ly]->u.text->text, (te+1) * sizeof(char)); - (*lines)[li]->items[ly]->u.text->text[te] = 0; - } - ly++; - (*lines)[li]->items = erealloc((*lines)[li]->items, (ly+1) * sizeof(struct ChoLineItem *)); - (*lines)[li]->items[ly] = NULL; - ly = 0; - te = 0; - (*lines)[li]->text_above = erealloc((*lines)[li]->text_above, (c+1) * sizeof(struct ChoLineItemAbove *)); - (*lines)[li]->text_above[c] = NULL; - c = 0; - cho_line_free((*lines)[li]); - // songs[so]->sections[se]->lines = erealloc(songs[so]->sections[se]->lines, (li+1) * sizeof(struct ChoLine *)); - (*lines)[li] = NULL; - li = 0; - se++; - songs[so]->sections = erealloc(songs[so]->sections, (se+1) * sizeof(struct ChoSection *)); - songs[so]->sections[se] = cho_section_copy(chorus); - if (songs[so]->sections[se]->label) { - free(songs[so]->sections[se]->label->text); - songs[so]->sections[se]->label->text = label; - } else { - g_current_ttype = TT_LABEL; - songs[so]->sections[se]->label = cho_text_new(); - songs[so]->sections[se]->label->text = label; - } - if (directive_has_tag) { - cho_style_complement(songs[so]->sections[se]->label->style, tags[ta]->style, &tags[ta]->style_presence); - directive_has_tag = false; - } - se++; - songs[so]->sections = erealloc(songs[so]->sections, (se+1) * sizeof(struct ChoSection *)); - songs[so]->sections[se] = cho_section_new(); - lines = &songs[so]->sections[se]->lines; - *lines = emalloc(sizeof(struct ChoLine *)); - (*lines)[li] = cho_line_new(); - (*lines)[li]->items = emalloc(sizeof(struct ChoLineItem *)); - (*lines)[li]->items[ly] = cho_line_item_new(); - } else { - if ((*lines)[li]->items[ly]->is_text) { - (*lines)[li]->items[ly]->u.text->text = erealloc((*lines)[li]->items[ly]->u.text->text, (te+1) * sizeof(char)); - (*lines)[li]->items[ly]->u.text->text[te] = 0; - if (strlen((*lines)[li]->items[ly]->u.text->text) == 0) { - cho_line_item_free((*lines)[li]->items[ly]); - } else { - ly++; - } - } else { - ly++; - } - (*lines)[li]->items = erealloc((*lines)[li]->items, (ly+1) * sizeof(struct ChoLineItem *)); - (*lines)[li]->items[ly] = cho_line_item_new(); - cho_style_free((*lines)[li]->items[ly]->u.text->style); - g_current_ttype = TT_LABEL; - (*lines)[li]->items[ly]->u.text->style = cho_style_new_default(); - (*lines)[li]->items[ly]->u.text->text = label; - if (directive_has_tag) { - cho_style_complement((*lines)[li]->items[ly]->u.text->style, tags[ta]->style, &tags[ta]->style_presence); - directive_has_tag = false; - } - te += strlen(label); - } - } else { - if (config->output->chorus->quote) { - cho_log(LOG_WARN, "Can't quote chorus because it's not defined previously."); - } - if ((*lines)[li]->items[ly]->is_text) { - (*lines)[li]->items[ly]->u.text->text = erealloc((*lines)[li]->items[ly]->u.text->text, (te+1) * sizeof(char)); - (*lines)[li]->items[ly]->u.text->text[te] = 0; - if (strlen((*lines)[li]->items[ly]->u.text->text) == 0) { - cho_line_item_free((*lines)[li]->items[ly]); - } else { - ly++; - } - } else { - ly++; - } - (*lines)[li]->items = erealloc((*lines)[li]->items, (ly+1) * sizeof(struct ChoLineItem *)); - (*lines)[li]->items[ly] = cho_line_item_new(); - cho_style_free((*lines)[li]->items[ly]->u.text->style); - g_current_ttype = TT_LABEL; - (*lines)[li]->items[ly]->u.text->style = cho_style_new_default(); - (*lines)[li]->items[ly]->u.text->text = label; - if (directive_has_tag) { - cho_style_complement((*lines)[li]->items[ly]->u.text->style, tags[ta]->style, &tags[ta]->style_presence); - } - te += strlen(label); - } - break; - } - default: - cho_log(LOG_ERR, "Invalid position value '%d'.", directive->position); - return NULL; - } - break; - } - case DT_METADATA: { - metadata_value = strdup(stripped_directive_value); - if (strlen(metadata_value) == 0) { - cho_log(LOG_WARN, "Ignoring metadata directive '%s' because it has no value.", directive_name); - free(metadata_value); - break; - } - switch (directive->meta) { - case TITLE: - songs[so]->metadata = erealloc(songs[so]->metadata, (m+1) * sizeof(struct ChoMetadata *)); - songs[so]->metadata[m] = cho_metadata_new(); - songs[so]->metadata[m]->name = strdup("title"); - songs[so]->metadata[m]->value = metadata_value; - cho_style_free(songs[so]->metadata[m]->style); - songs[so]->metadata[m]->style = cho_style_copy(directive->style); - songs[so]->present_text_types[TT_TITLE] = true; - break; - case SUBTITLE: - songs[so]->metadata = erealloc(songs[so]->metadata, (m+1) * sizeof(struct ChoMetadata *)); - songs[so]->metadata[m] = cho_metadata_new(); - songs[so]->metadata[m]->name = strdup("subtitle"); - songs[so]->metadata[m]->value = metadata_value; - cho_style_free(songs[so]->metadata[m]->style); - songs[so]->metadata[m]->style = cho_style_copy(directive->style); - songs[so]->present_text_types[TT_SUBTITLE] = true; - break; - default: - if (!strcmp(directive_name, "meta")) { - metadata = cho_metadata_split(directive_value); - if (!metadata) { - LOG_DEBUG("cho_metadata_split failed."); - return NULL; - } - songs[so]->metadata = erealloc(songs[so]->metadata, (m+1) * sizeof(struct ChoMetadata *)); - songs[so]->metadata[m] = metadata; - free(metadata_value); - } else { - songs[so]->metadata = erealloc(songs[so]->metadata, (m+1) * sizeof(struct ChoMetadata *)); - songs[so]->metadata[m] = cho_metadata_new(); - songs[so]->metadata[m]->name = strdup(directive_name); - songs[so]->metadata[m]->value = metadata_value; - } - } - if (directive_has_tag) { - cho_style_complement(songs[so]->metadata[m]->style, tags[ta]->style, &tags[ta]->style_presence); - directive_has_tag = false; - } - m++; - break; - } - case DT_FORMATTING: { - if ((*lines)[li]->items[ly]->is_text) { - (*lines)[li]->items[ly]->u.text->text = erealloc((*lines)[li]->items[ly]->u.text->text, (te+1) * sizeof(char)); - (*lines)[li]->items[ly]->u.text->text[te] = 0; - if (strlen((*lines)[li]->items[ly]->u.text->text) == 0) { - cho_line_item_free((*lines)[li]->items[ly]); - } else { - ly++; - } - } else { - ly++; - } - (*lines)[li]->items = erealloc((*lines)[li]->items, (ly+1) * sizeof(struct ChoLineItem *)); - (*lines)[li]->items[ly] = cho_line_item_new(); - cho_style_free((*lines)[li]->items[ly]->u.text->style); - (*lines)[li]->items[ly]->u.text->style = cho_style_copy(directive->style); - (*lines)[li]->items[ly]->u.text->text = strdup(stripped_directive_value); - if (directive_has_tag) { - cho_style_complement((*lines)[li]->items[ly]->u.text->style, tags[ta]->style, &tags[ta]->style_presence); - directive_has_tag = false; - } - te += strlen(stripped_directive_value); - songs[so]->present_text_types[directive->ttype] = true; - break; - } - case DT_IMAGE: { - if (strstr(directive_value, "=")) { - image = cho_image_directive_parse(directive_value); - if (!image) { - LOG_DEBUG("cho_image_directive_parse failed."); - return NULL; - } - } else { - image = cho_image_new(); - image->src = strdup(stripped_directive_value); - } - if (image->is_asset) { - g_image_assets = erealloc(g_image_assets, (g_ia+1) * sizeof(struct ChoImage *)); - g_image_assets[g_ia] = image; - g_ia++; - } else { - if ((*lines)[li]->items[ly]->is_text) { - (*lines)[li]->items[ly]->u.text->text = erealloc((*lines)[li]->items[ly]->u.text->text, (te+1) * sizeof(char)); - (*lines)[li]->items[ly]->u.text->text[te] = 0; - te = 0; - } - ly++; - (*lines)[li]->items = erealloc((*lines)[li]->items, (ly+1) * sizeof(struct ChoLineItem *)); - (*lines)[li]->items[ly] = cho_line_item_new(); - cho_text_free((*lines)[li]->items[ly]->u.text); - (*lines)[li]->items[ly]->is_text = false; - (*lines)[li]->items[ly]->u.image = image; - } - break; - } - case DT_PREAMBLE: - cho_log(LOG_ERR, "Preamble directive '%s' can't have a value.", directive_name); - return NULL; - case DT_FONT: { - sprop.ttype = directive->ttype; - char *dir_value = strdup(stripped_directive_value); - switch (directive->sprop) { - case SPT_FONT: - sprop.u.font_name = emalloc((strlen(dir_value)+1) * sizeof(char)); - strcpy(sprop.u.font_name, dir_value); - sprop.type = SPT_FONT; - break; - case SPT_SIZE: - sprop.u.font_size = strtod(dir_value, NULL); - if (sprop.u.font_size == 0.0) { - cho_log(LOG_ERR, "Font directive '%s' has an invalid value.", directive_name); - return NULL; - } - sprop.type = SPT_SIZE; - break; - case SPT_COLOR: - sprop.u.foreground_color = cho_color_parse(dir_value); - if (sprop.u.foreground_color == NULL) { - cho_log(LOG_ERR, "Font directive '%s' has an invalid value.", directive_name); - return NULL; - } - sprop.type = SPT_COLOR; - break; - default: - cho_log(LOG_ERR, "Invalid style property type '%d'.", directive->sprop); - return NULL; - } - if (!cho_style_change_default(sprop)) { - LOG_DEBUG("cho_style_change_default failed."); - return NULL; - } - if (sprop.type == SPT_FONT) { - free(sprop.u.font_name); - } else if (sprop.type == SPT_COLOR) { - free(sprop.u.foreground_color); - } - free(dir_value); - break; - } - case DT_CHORD: { - switch (directive->ctype) { - case TRANSPOSE: - if (!transposition_parse(directive_value, &transpose)) { - LOG_DEBUG("transposition_parse failed."); - cho_log(LOG_ERR, "Directive 'transpose' has an invalid value."); - return NULL; - } - g_transpose_history = erealloc(g_transpose_history, (th+1) * sizeof(int *)); - g_transpose_history[th] = g_transpose_history[th-1] + transpose; - g_transpose = &g_transpose_history[th]; - th++; - break; - case DEFINE: - diagram = cho_chord_diagram_parse(directive_value, songs[so]->diagrams, dia); - if (!diagram) { - LOG_DEBUG("cho_chord_diagram_parse failed."); - return NULL; - } - songs[so]->diagrams = erealloc(songs[so]->diagrams, (dia+1) * sizeof(struct ChordDiagram *)); - songs[so]->diagrams[dia] = diagram; - dia++; - // debug_chord_diagram_print(diagram); - break; - } - break; - } - case DT_OUTPUT: - cho_log(LOG_ERR, "Directive '%s' can't have a value.", directive_name); - return NULL; - case DT_EXTENSION: - // INFO: Such a directive should not be logged. - break; - case DT_CUSTOM: - cho_log(LOG_INFO, "Ignoring custom directive '%s'.", directive_name); - break; - default: - cho_log(LOG_ERR, "Invalid directive type '%d'.", directive->dtype); - return NULL; - } - memset(directive_value, 0, strlen(directive_value)); - free(stripped_directive_value); - cho_directive_free(directive); - directive = NULL; - state = STATE_LYRICS; - break; - } - if (buf == '<') { - state_before_tag = STATE_DIRECTIVE_VALUE; - state = STATE_MARKUP_TAG; - break; - } - if (buf == '{') { - cho_log(LOG_ERR, "Can't start a new directive if the previous one is not yet closed."); - return NULL; - } - if (buf == '\n') { - if (prev_buf == '\\') { - state_before_backslash = STATE_DIRECTIVE_VALUE; - state = STATE_BACKSLASH; - dv--; - break; - } - 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 greater than 4095 bytes."); - return NULL; - } - directive_value[dv] = buf; - dv++; - break; - } - case STATE_CHORD: { - if (buf == ']') { - chord[ch] = 0; - ch = 0; - g_prev_ttype = g_current_ttype; - g_current_ttype = TT_CHORD; - if (is_chord_already_initialized) { - text_above_pos = cho_line_compute_text_above_position(songs[so]->sections[se]->lines[li], ly, te); - (*lines)[li]->text_above[c]->position = text_above_pos; - tmp_chord = cho_chord_parse(chord); - cho_chord_complete((*lines)[li]->text_above[c]->u.chord, tmp_chord); - if (!(*lines)[li]->text_above[c]->u.chord->is_canonical) { - cho_log(LOG_INFO, "Didn't recognize the chord '%s'.", (*lines)[li]->text_above[c]->u.chord->name); - } - cho_chord_free(tmp_chord); - is_chord_already_initialized = false; - } else { - (*lines)[li]->text_above = erealloc((*lines)[li]->text_above, (c+1) * sizeof(struct ChoLineItemAbove *)); - (*lines)[li]->text_above[c] = emalloc(sizeof(struct ChoLineItemAbove)); - (*lines)[li]->text_above[c]->is_chord = true; - text_above_pos = cho_line_compute_text_above_position((*lines)[li], ly, te); - (*lines)[li]->text_above[c]->position = text_above_pos; - (*lines)[li]->text_above[c]->u.chord = cho_chord_parse(chord); - if (!(*lines)[li]->text_above[c]->u.chord->is_canonical) { - cho_log(LOG_INFO, "Didn't recognize the chord '%s'.", (*lines)[li]->text_above[c]->u.chord->name); - } - } - songs[so]->present_text_types[TT_CHORD] = true; - memset(chord, 0, strlen(chord)); - c++; - g_current_ttype = g_prev_ttype; - state = STATE_LYRICS; - break; - } - if (prev_buf == '[' && buf == '*') { - g_prev_ttype = g_current_ttype; - g_current_ttype = TT_ANNOT; - (*lines)[li]->text_above = erealloc((*lines)[li]->text_above, (c+1) * sizeof(struct ChoLineItemAbove *)); - (*lines)[li]->text_above[c] = emalloc(sizeof(struct ChoLineItemAbove)); - (*lines)[li]->text_above[c]->is_chord = false; - text_above_pos = cho_line_compute_text_above_position((*lines)[li], ly, te); - (*lines)[li]->text_above[c]->position = text_above_pos; - (*lines)[li]->text_above[c]->u.annot = cho_text_new(); - state = STATE_ANNOTATION; - break; - } - if (buf == '\n') { - if (prev_buf == '\\') { - state_before_backslash = STATE_CHORD; - state = STATE_BACKSLASH; - ch--; - break; - } - cho_log(LOG_ERR, "Newline character inside a chord is invalid."); - return NULL; - } - if (buf == '[') { - cho_log(LOG_ERR, "Can't start a new chord/annotation if the previous one is not yet closed."); - return NULL; - } - if (buf == '<') { - if (prev_buf == '[') { - (*lines)[li]->text_above = erealloc((*lines)[li]->text_above, (c+1) * sizeof(struct ChoLineItemAbove *)); - (*lines)[li]->text_above[c] = emalloc(sizeof(struct ChoLineItemAbove)); - (*lines)[li]->text_above[c]->is_chord = true; - (*lines)[li]->text_above[c]->u.chord = cho_chord_new(); - is_chord_already_initialized = true; - } - state_before_tag = STATE_CHORD; - state = STATE_MARKUP_TAG; - break; - } - chord[ch] = buf; - ch++; - break; - } - case STATE_ANNOTATION: { - if (buf == ']') { - (*lines)[li]->text_above[c]->u.annot->text = erealloc((*lines)[li]->text_above[c]->u.annot->text, (ann+1) * sizeof(char)); - (*lines)[li]->text_above[c]->u.annot->text[ann] = 0; - songs[so]->present_text_types[TT_ANNOT] = true; - ann = 0; - c++; - g_current_ttype = g_prev_ttype; - state = STATE_LYRICS; - break; - } - if (buf == '<') { - state_before_tag = STATE_ANNOTATION; - state = STATE_MARKUP_TAG; - break; - } - if (buf == '\n') { - if (prev_buf == '\\') { - state_before_backslash = STATE_ANNOTATION; - state = STATE_BACKSLASH; - ann--; - break; - } - cho_log(LOG_ERR, "Newline character inside an annotation is invalid."); - return NULL; - } - (*lines)[li]->text_above[c]->u.annot->text = erealloc((*lines)[li]->text_above[c]->u.annot->text, (ann+1) * sizeof(char)); - (*lines)[li]->text_above[c]->u.annot->text[ann] = buf; - ann++; - break; - } - case STATE_TAB: { - // INFO: similar to STATE_LYRICS but without markup and directives - if (prev_buf == '\n' && buf == '#') { - state_before_comment = STATE_TAB; - state = STATE_COMMENT; - break; - } - if (is_maybe_end_of_tab_directive) { - if (buf == '}') { - directive_name[dn] = 0; - dn = 0; - is_maybe_end_of_tab_directive = false; - if (!strcmp(directive_name, "end_of_tab") || !strcmp(directive_name, "eot")) { - cho_line_item_free((*lines)[li]->items[ly]); - free((*lines)[li]->items); - ly = 0; - free((*lines)[li]); - (*lines)[li] = NULL; - se++; - songs[so]->sections = erealloc(songs[so]->sections, (se+1) * sizeof(struct ChoSection *)); - songs[so]->sections[se] = cho_section_new(); - li = 0; - lines = &songs[so]->sections[se]->lines; - *lines = emalloc(sizeof(struct ChoLine *)); - (*lines)[li] = cho_line_new(); - (*lines)[li]->items = emalloc(sizeof(struct ChoLineItem *)); - (*lines)[li]->items[ly] = cho_line_item_new(); - g_current_ttype = TT_TEXT; - state = STATE_LYRICS; - break; - } else { - (*lines)[li]->items[ly]->u.text->text = erealloc((*lines)[li]->items[ly]->u.text->text, (te+1) * sizeof(char)); - (*lines)[li]->items[ly]->u.text->text[te] = '{'; - te++; - char *k; - for (k = (char *)&directive_name; *k; k++, te++) { - (*lines)[li]->items[ly]->u.text->text = erealloc((*lines)[li]->items[ly]->u.text->text, (te+1) * sizeof(char)); - (*lines)[li]->items[ly]->u.text->text[te] = *k; - } - (*lines)[li]->items[ly]->u.text->text = erealloc((*lines)[li]->items[ly]->u.text->text, (te+1) * sizeof(char)); - (*lines)[li]->items[ly]->u.text->text[te] = '}'; - te++; - } - break; - } - if (buf == ' ' || buf == ':') { - directive_name[dn] = 0; - dn = 0; - is_maybe_end_of_tab_directive = false; - (*lines)[li]->items[ly]->u.text->text = erealloc((*lines)[li]->items[ly]->u.text->text, (te+1) * sizeof(char)); - (*lines)[li]->items[ly]->u.text->text[te] = '{'; - te++; - char *k; - for (k = (char *)&directive_name; *k; k++, te++) { - (*lines)[li]->items[ly]->u.text->text = erealloc((*lines)[li]->items[ly]->u.text->text, (te+1) * sizeof(char)); - (*lines)[li]->items[ly]->u.text->text[te] = *k; - } - (*lines)[li]->items[ly]->u.text->text = erealloc((*lines)[li]->items[ly]->u.text->text, (te+1) * sizeof(char)); - (*lines)[li]->items[ly]->u.text->text[te] = buf; - te++; - break; - } - directive_name[dn] = buf; - dn++; - break; - } - if (prev_buf == '\n' && buf == '{') { - is_maybe_end_of_tab_directive = true; - break; - } - if (buf == '\n') { - if (prev_buf == '\\') { - state_before_backslash = STATE_TAB; - state = STATE_BACKSLASH; - // INFO: This will later overwrite the backslash - te--; - break; - } - if (ta > -1 && !tags[ta]->is_closed && strcmp(tags[ta]->name, "img")) { - cho_log(LOG_ERR, "Tag has to be closed on same line."); - return NULL; - } - if ((*lines)[li]->items[ly]->is_text) { - (*lines)[li]->items[ly]->u.text->text = erealloc((*lines)[li]->items[ly]->u.text->text, (te+1) * sizeof(char)); - (*lines)[li]->items[ly]->u.text->text[te] = 0; - if (strlen((*lines)[li]->items[ly]->u.text->text) == 0) { - cho_line_item_free((*lines)[li]->items[ly]); - if (ly == 0) { - if ( - !(*lines)[li]->text_above && - (*lines)[li]->btype == BT_LINE - ) { - free((*lines)[li]->items); - free((*lines)[li]); - *lines = erealloc(*lines, (li+1) * sizeof(struct ChoLine *)); - (*lines)[li] = cho_line_new(); - (*lines)[li]->items = erealloc((*lines)[li]->items, (ly+1) * sizeof(struct ChoLineItem *)); - (*lines)[li]->items[ly] = cho_line_item_new(); - break; - } - } - } else { - ly++; - } - } else { - ly++; - } - (*lines)[li]->items = erealloc((*lines)[li]->items, (ly+1) * sizeof(struct ChoLineItem *)); - (*lines)[li]->items[ly] = NULL; - ly = 0; - te = 0; - (*lines)[li]->text_above = erealloc((*lines)[li]->text_above, (c+1) * sizeof(struct ChoLineItemAbove *)); - (*lines)[li]->text_above[c] = NULL; - c = 0; - li++; - *lines = erealloc(*lines, (li+1) * sizeof(struct ChoLine *)); - (*lines)[li] = cho_line_new(); - (*lines)[li]->items = erealloc((*lines)[li]->items, (ly+1) * sizeof(struct ChoLineItem *)); - (*lines)[li]->items[ly] = cho_line_item_new(); - break; - } - (*lines)[li]->items[ly]->u.text->text = erealloc((*lines)[li]->items[ly]->u.text->text, (te+1) * sizeof(char)); - (*lines)[li]->items[ly]->u.text->text[te] = buf; - te++; - break; - } - case STATE_MARKUP_TAG: { - if (buf == '/') { - state = STATE_MARKUP_TAG_END; - break; - } - ta++; - tags = erealloc(tags, (ta+1) * sizeof(struct Tag *)); - tags[ta] = cho_tag_new(); - state = STATE_MARKUP_TAG_START; - if (fseek(fp, -1, SEEK_CUR)) { - LOG_DEBUG("fseek failed."); - return NULL; - } - break; - } - case STATE_MARKUP_TAG_START: { - if (buf == '>') { - tag_start[t] = 0; - t = 0; - if (!strcmp(tag_start, "img")) { - cho_log(LOG_ERR, "'img' tag has to have at least the 'src' attribute."); - return NULL; - } - tags[ta]->name = strdup(tag_start); - tag_style = cho_style_parse(tag_start, NULL, cho_tag_style_inherit(tags, ta-1), &tags[ta]->style_presence); - if (!tag_style) { - LOG_DEBUG("cho_style_parse failed."); - return NULL; - } - tags[ta]->style = tag_style; - switch (state_before_tag) { - case STATE_LYRICS: - cho_style_free((*lines)[li]->items[ly]->u.text->style); - (*lines)[li]->items[ly]->u.text->style = cho_style_copy(tag_style); - break; - case STATE_CHORD: - cho_style_free((*lines)[li]->text_above[c]->u.chord->style); - (*lines)[li]->text_above[c]->u.chord->style = cho_style_copy(tag_style); - break; - case STATE_ANNOTATION: - if (ann > 0) { - (*lines)[li]->text_above[c]->u.annot->text = erealloc((*lines)[li]->text_above[c]->u.annot->text, (ann+1) * sizeof(char)); - (*lines)[li]->text_above[c]->u.annot->text[ann] = 0; - ann = 0; - c++; - (*lines)[li]->text_above = erealloc((*lines)[li]->text_above, (c+1) * sizeof(struct ChoLineItemAbove *)); - (*lines)[li]->text_above[c] = emalloc(sizeof(struct ChoLineItemAbove)); - (*lines)[li]->text_above[c]->is_chord = false; - (*lines)[li]->text_above[c]->position = text_above_pos; - (*lines)[li]->text_above[c]->u.annot = cho_text_new(); - } - cho_style_free((*lines)[li]->text_above[c]->u.annot->style); - (*lines)[li]->text_above[c]->u.annot->style = cho_style_copy(tag_style); - break; - case STATE_DIRECTIVE_VALUE: - directive_has_tag = true; - break; - default: - cho_log(LOG_ERR, "Invalid state_before_tag '%s'.", state_enums[state_before_tag]); - return NULL; - } - memset(tag_start, 0, strlen(tag_start)); - state = state_before_tag; - break; - } - if (is_whitespace(buf)) { - tag_start[t] = 0; - t = 0; - tags[ta]->name = strdup(tag_start); - tags[ta]->attrs = erealloc(tags[ta]->attrs, (at+1) * sizeof(struct Attr *)); - tags[ta]->attrs[at] = cho_tag_attr_new(); - state = STATE_MARKUP_ATTR_NAME; - break; - } - if (buf == '\n') { - if (prev_buf == '\\') { - state_before_backslash = STATE_MARKUP_TAG_START; - state = STATE_BACKSLASH; - t--; - break; - } - cho_log(LOG_ERR, "Newline character inside a tag name is invalid."); - return NULL; - } - if (t == 5) { - cho_log(LOG_ERR, "Start tag name is too long."); - return NULL; - } - tag_start[t] = buf; - t++; - break; - } - case STATE_MARKUP_TAG_END: { - if (buf == '>') { - tag_end[t] = 0; - t = 0; - if (!cho_tag_close_last_unclosed(tag_end, tags, ta)) { - LOG_DEBUG("cho_tag_close_last_unclosed failed."); - return NULL; - } - memset(tag_end, 0, strlen(tag_end)); - state = state_before_tag; - break; - } - if (buf == '\n') { - if (prev_buf == '\\') { - state_before_backslash = STATE_MARKUP_TAG_END; - state = STATE_BACKSLASH; - t--; - break; - } - cho_log(LOG_ERR, "Newline character inside a tag name is invalid."); - return NULL; - } - if (t == 5) { - cho_log(LOG_ERR, "End tag name is too long."); - return NULL; - } - tag_end[t] = buf; - t++; - break; - } - case STATE_MARKUP_ATTR_NAME: { - if (buf == '=') { - tags[ta]->attrs[at]->name = erealloc(tags[ta]->attrs[at]->name, (atn+1) * sizeof(char)); - tags[ta]->attrs[at]->name[atn] = 0; - atn = 0; - state = STATE_MARKUP_ATTR_VALUE; - break; - } - if (is_whitespace(buf)) { - if (at == 0) { - if (!tags[ta]->attrs[at]->name) { - break; - } else { - tags[ta]->attrs[at]->name = erealloc(tags[ta]->attrs[at]->name, (atn+1) * sizeof(char)); - tags[ta]->attrs[at]->name[atn] = 0; - cho_log(LOG_ERR, "Attribute with name '%s' of tag '%s' has no value.", tags[ta]->attrs[at]->name, tag_start); - return NULL; - } - } - if (tags[ta]->attrs[at-1]->name && tags[ta]->attrs[at-1]->value) { - break; - } - if (!tags[ta]->attrs[at-1]->name && !tags[ta]->attrs[at-1]->value) { - break; - } - tags[ta]->attrs[at]->name = erealloc(tags[ta]->attrs[at]->name, (atn+1) * sizeof(char)); - tags[ta]->attrs[at]->name[atn] = 0; - cho_log(LOG_ERR, "Attribute with name '%s' of tag '%s' has no value.", tags[ta]->attrs[at]->name, tag_start); - return NULL; - } - if (buf == '>') { - if (tags[ta]->attrs[at-1]->value) { - cho_tag_attr_free(tags[ta]->attrs[at]); - tags[ta]->attrs[at] = NULL; - atn = 0; - if (!strcmp(tags[ta]->name, "img")) { - cho_text_free((*lines)[li]->items[ly]->u.text); - (*lines)[li]->items[ly]->is_text = false; - image = cho_image_tag_parse(tags[ta]->attrs); - if (!image) { - LOG_DEBUG("cho_image_tag_parse failed."); - return NULL; - } - (*lines)[li]->items[ly]->u.image = image; - ly++; - (*lines)[li]->items = erealloc((*lines)[li]->items, (ly+1) * sizeof(struct ChoLineItem *)); - (*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), &tags[ta]->style_presence); - if (!tag_style) { - LOG_DEBUG("cho_style_parse failed."); - return NULL; - } - tags[ta]->style = tag_style; - switch (state_before_tag) { - case STATE_LYRICS: - cho_style_free((*lines)[li]->items[ly]->u.text->style); - (*lines)[li]->items[ly]->u.text->style = cho_style_copy(tag_style); - break; - case STATE_CHORD: - cho_style_free((*lines)[li]->text_above[c]->u.chord->style); - (*lines)[li]->text_above[c]->u.chord->style = cho_style_copy(tag_style); - break; - case STATE_ANNOTATION: - cho_style_free((*lines)[li]->text_above[c]->u.annot->style); - (*lines)[li]->text_above[c]->u.annot->style = cho_style_copy(tag_style); - break; - case STATE_DIRECTIVE_VALUE: - directive_has_tag = true; - break; - default: - cho_log(LOG_ERR, "Invalid state_before_tag '%s'.", state_enums[state_before_tag]); - return NULL; - } - } - at = 0; - memset(tag_start, 0, strlen(tag_start)); - state = state_before_tag; - break; - } else { - tags[ta]->attrs[at]->name = erealloc(tags[ta]->attrs[at]->name, (atn+1) * sizeof(char)); - tags[ta]->attrs[at]->name[atn] = 0; - atn = 0; - cho_log(LOG_ERR, "Attribute with name '%s' of tag '%s' has no value.", tags[ta]->attrs[at]->name, tag_start); - return NULL; - } - } - if (buf == '\n') { - if (prev_buf == '\\') { - state_before_backslash = STATE_MARKUP_ATTR_NAME; - state = STATE_BACKSLASH; - atn--; - break; - } - cho_log(LOG_ERR, "Newline character inside an tag attribute name is invalid."); - return NULL; - } - tags[ta]->attrs[at]->name = erealloc(tags[ta]->attrs[at]->name, (atn+1) * sizeof(char)); - tags[ta]->attrs[at]->name[atn] = buf; - atn++; - break; - } - case STATE_MARKUP_ATTR_VALUE: { - if (buf == '\n') { - if (prev_buf == '\\') { - state_before_backslash = STATE_MARKUP_ATTR_VALUE; - state = STATE_BACKSLASH; - atv--; - break; - } - cho_log(LOG_ERR, "Newline character inside an attribute value is invalid."); - return NULL; - } - if (avs == -1) { - if (is_whitespace(buf)) { - cho_log(LOG_ERR, "Whitespace character after equals sign is invalid."); - return NULL; - } - if (buf == '>') { - cho_log(LOG_ERR, "Attribute with name '%s' of tag '%s' has no value.", tags[ta]->attrs[at]->name, tag_start); - return NULL; - } - if (buf == '\'') { - avs = AVS_APOSTROPHE; - } else if (buf == '"') { - avs = AVS_QUOTATION_MARK; - } else { - avs = AVS_UNQUOTED; - tags[ta]->attrs[at]->value = erealloc(tags[ta]->attrs[at]->value, (atv+1) * sizeof(char)); - tags[ta]->attrs[at]->value[atv] = buf; - atv++; - } - break; - } - if (avs == AVS_UNQUOTED && buf == '>') { - tags[ta]->attrs[at]->value = erealloc(tags[ta]->attrs[at]->value, (atv+1) * sizeof(char)); - tags[ta]->attrs[at]->value[atv] = 0; - atv = 0; - at++; - tags[ta]->attrs = erealloc(tags[ta]->attrs, (at+1) * sizeof(struct Attr *)); - tags[ta]->attrs[at] = NULL; - if (!strcmp(tags[ta]->name, "img")) { - cho_text_free((*lines)[li]->items[ly]->u.text); - (*lines)[li]->items[ly]->is_text = false; - image = cho_image_tag_parse(tags[ta]->attrs); - if (!image) { - LOG_DEBUG("cho_image_tag_parse failed."); - return NULL; - } - (*lines)[li]->items[ly]->u.image = image; - ly++; - (*lines)[li]->items = erealloc((*lines)[li]->items, (ly+1) * sizeof(struct ChoLineItem *)); - (*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), &tags[ta]->style_presence); - if (!tag_style) { - LOG_DEBUG("cho_style_parse failed."); - return NULL; - } - tags[ta]->style = tag_style; - switch (state_before_tag) { - case STATE_LYRICS: - cho_style_free((*lines)[li]->items[ly]->u.text->style); - (*lines)[li]->items[ly]->u.text->style = cho_style_copy(tag_style); - break; - case STATE_CHORD: - cho_style_free((*lines)[li]->text_above[c]->u.chord->style); - (*lines)[li]->text_above[c]->u.chord->style = cho_style_copy(tag_style); - break; - case STATE_ANNOTATION: - cho_style_free((*lines)[li]->text_above[c]->u.annot->style); - (*lines)[li]->text_above[c]->u.annot->style = cho_style_copy(tag_style); - break; - case STATE_DIRECTIVE_VALUE: - directive_has_tag = true; - break; - default: - cho_log(LOG_ERR, "Invalid state_before_tag '%s'.", state_enums[state_before_tag]); - return NULL; - } - } - at = 0; - avs = -1; - memset(tag_start, 0, strlen(tag_start)); - state = state_before_tag; - break; - } - if ( - (avs == AVS_APOSTROPHE && buf == '\'') || - (avs == AVS_QUOTATION_MARK && buf == '"') || - (avs == AVS_UNQUOTED && (buf == ' ' || buf == '\t')) - ) { - tags[ta]->attrs[at]->value = erealloc(tags[ta]->attrs[at]->value, (atv+1) * sizeof(char)); - tags[ta]->attrs[at]->value[atv] = 0; - atv = 0; - at++; - tags[ta]->attrs = erealloc(tags[ta]->attrs, (at+1) * sizeof(struct Attr *)); - tags[ta]->attrs[at] = cho_tag_attr_new(); - avs = -1; - state = STATE_MARKUP_ATTR_NAME; - break; - } - tags[ta]->attrs[at]->value = erealloc(tags[ta]->attrs[at]->value, (atv+1) * sizeof(char)); - tags[ta]->attrs[at]->value[atv] = buf; - atv++; - break; - } - case STATE_COMMENT: { - if (buf == '\n') { - state = state_before_comment; - break; - } - break; - } - } - prev_buf = buf; - } else { - break; - } - } - int e = 0; - while (e <= ta) { - cho_tag_free(tags[e]); - e++; - } - free(tags); - if ((*lines)[li]->items[ly]->is_text) { - if ((*lines)[li]->items[ly]->u.text->text) { - (*lines)[li]->items[ly]->u.text->text = erealloc((*lines)[li]->items[ly]->u.text->text, (te+1) * sizeof(char)); - (*lines)[li]->items[ly]->u.text->text[te] = 0; - ly++; - (*lines)[li]->items = erealloc((*lines)[li]->items, (ly+1) * sizeof(struct ChoLineItem *)); - (*lines)[li]->items[ly] = NULL; - (*lines)[li]->text_above = erealloc((*lines)[li]->text_above, (c+1) * sizeof(struct ChoLineItemAbove *)); - (*lines)[li]->text_above[c] = NULL; - li++; - *lines = erealloc(*lines, (li+1) * sizeof(struct ChoLine *)); - (*lines)[li] = NULL; - } else { - cho_line_item_free((*lines)[li]->items[ly]); - free((*lines)[li]->items); - free((*lines)[li]->text_above); - free((*lines)[li]); - (*lines)[li] = NULL; - } - } - if (!cho_style_reset_default()) { - LOG_DEBUG("cho_style_reset_default failed."); - return NULL; - } - songs[so]->metadata = erealloc(songs[so]->metadata, (m+1) * sizeof(struct ChoMetadata *)); - songs[so]->metadata[m] = NULL; - se++; - songs[so]->sections = erealloc(songs[so]->sections, (se+1) * sizeof(struct ChoSection *)); - songs[so]->sections[se] = NULL; - songs[so]->diagrams = erealloc(songs[so]->diagrams, (dia+1) * sizeof(struct ChordDiagram *)); - songs[so]->diagrams[dia] = NULL; - so++; - songs = erealloc(songs, (so+1) * sizeof(struct ChoSong *)); - songs[so] = NULL; - g_current_ttype = TT_TEXT; - g_prev_ttype = TT_TEXT; - g_config = NULL; - g_chordpro_filepath = NULL; - free(g_transpose_history); - g_transpose_history = NULL; - g_transpose = NULL; - g_line_number = 1; - for (e = 0; e<g_ia; e++) { - cho_image_free(g_image_assets[e]); - } - free(g_image_assets); - g_image_assets = NULL; - g_ia = 0; - bool exist_title = false; - for (so = 0; songs[so]; so++) { - for (m = 0; songs[so]->metadata[m]; m++) { - if ( - !strcmp(songs[so]->metadata[m]->name, "title") && - songs[so]->metadata[m]->value && - strcmp(songs[so]->metadata[m]->value, "") != 0 - ) { - exist_title = true; - } - } - if (!exist_title) { - g_line_number = 0; - /* INFO: This cho_log() is not line specific. It's a workaround. */ - cho_log(LOG_ERR, "Song has no title."); - return NULL; - } - exist_title = false; - } - return songs; -} - -#ifdef DEBUG -void -cho_debug_songs_print(struct ChoSong **songs) -{ - struct ChoSong **s; - struct ChoSection **se; - struct ChoLine **li; - struct ChoLineItem **it; - struct ChoLineItemAbove **above; - char *name; - for (s = songs; *s; s++) { - for (se = (*s)->sections; *se; se++) { - printf("## Section"); - if ((*se)->label) { - printf(": %s\n", (*se)->label->text); - } else { - printf("\n"); - } - for (li = (*se)->lines; *li; li++) { - printf("## Line\n"); - it = (*li)->items; - for (above = (*li)->text_above; *above; 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); - } - } - for (it = (*li)->items; *it; it++) { - if ((*it)->is_text) { - printf("text: %s\n", (*it)->u.text->text); - } else { - printf("image: %s\n", (*it)->u.image->src); - } - } - } - } - } -} -#endif /* DEBUG */ diff --git a/chordpro.h b/chordpro.h @@ -1,175 +0,0 @@ -#include <stdint.h> -#include "types.h" - -#ifndef _CHORDPRO_H_ -#define _CHORDPRO_H_ - -#define ERROR -1.0 -#define EMPTY_DOUBLE -1.0 -#define EMPTY_INT -1 -#define DEFAULT_FONT_SIZE 14.0 -#define DEFAULT_TITLE_FONT_SIZE 18.0 -// INFO: Based on https://stackoverflow.com/a/417184 -#define URL_MAX_LEN 2000 -#define FONT_NAME_MAX 100 - -enum AttrValueSyntax : int8_t { - AVS_QUOTATION_MARK, - AVS_APOSTROPHE, - AVS_UNQUOTED -}; - -enum ChordDiagramState { - CDS_NAME, - CDS_OPTION_NAME, - CDS_BASE_FRET, - CDS_FRETS, - CDS_FINGERS, - CDS_KEYS, - CDS_DIAGRAM, - CDS_COPY -}; - -enum ChordDirective { - TRANSPOSE, DEFINE /* , CHORD */ -}; - -enum DirectiveType { - DT_ENVIRONMENT, - DT_METADATA, - DT_FORMATTING, - DT_IMAGE, - DT_PREAMBLE, - DT_FONT, - DT_CHORD, - DT_OUTPUT, - DT_EXTENSION, - DT_CUSTOM -}; - -enum MetadataDirective { - TITLE, - SUBTITLE -}; - -enum OptionState { - OS_NAME, - OS_VALUE -}; - -enum Position { - POS_START, - POS_END, - POS_NO -}; - -enum State { - STATE_LYRICS, - STATE_BACKSLASH, - STATE_DIRECTIVE_NAME, - STATE_DIRECTIVE_VALUE, - STATE_CHORD, - STATE_ANNOTATION, - STATE_TAB, - STATE_MARKUP_TAG, - STATE_MARKUP_TAG_START, - STATE_MARKUP_TAG_END, - STATE_MARKUP_ATTR_NAME, - STATE_MARKUP_ATTR_VALUE, - STATE_COMMENT -}; - -enum StylePropertyType : int8_t { - SPT_FONT, - SPT_SIZE, - SPT_COLOR -}; - -struct Attr { - char *name; - char *value; -}; - -/* - INFO: Depending on the 'dtype' the other - fields have a meaningful value or not. -*/ - -struct ChoDirective { - enum DirectiveType dtype; - enum SectionType stype; - enum Position position; - enum StylePropertyType sprop; - enum TextType ttype; - enum BreakType btype; - enum MetadataDirective meta; - enum ChordDirective ctype; - struct ChoStyle *style; -}; - -union StylePropertyValue { - char *font_name; - double font_size; - struct RGBColor *foreground_color; -}; - -struct StyleProperty { - enum TextType ttype; - enum StylePropertyType type; - union StylePropertyValue u; -}; - -struct Tag { - char *name; - struct ChoStyle *style; - struct ChoStylePresence style_presence; - struct Attr **attrs; - bool is_closed; -}; - -void cho_log_enable_info_logs(void); - -struct ChoSong **cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config); -int cho_song_count(struct ChoSong **songs); -int cho_song_compare(const void *a, const void *b); -void cho_songs_free(struct ChoSong **song); - -int cho_line_item_count(struct ChoLineItem **items); -char *cho_chord_name_generate(struct ChoChord *chord); - -void cho_chords_add(struct ChoChord ***chords, struct ChoChord *chord); -bool cho_chords_has(struct ChoChord **chords, struct ChoChord *chord); -size_t cho_chord_count(struct ChoChord **chords); -int cho_chord_compare(const void *a, const void *b); -void cho_chords_free(struct ChoChord **chords); - -const char *cho_metadata_get(struct ChoMetadata **metadata, const char *name); - -struct ChoStyle *cho_style_new(void); -void cho_style_free(struct ChoStyle *style); -struct ChoStyle *cho_style_copy(struct ChoStyle *style); -void cho_style_print_as_toml(struct ChoStyle *style, const char *section); - -struct RGBColor *cho_rgbcolor_new(uint8_t red, uint8_t green, uint8_t blue); -struct RGBColor *cho_color_parse(const char *str); -struct RGBColor *cho_color_copy(struct RGBColor *color); -enum LineStyle cho_linestyle_parse(const char *str); - -// const char *cho_image_name_create(struct ChoImage *image, const char *dirname); - -void cho_font_free(struct Font *font); -void cho_font_print(struct Font *font); -struct Font *cho_font_copy(struct Font *font); -void cho_fonts_free(struct Font **fonts); -char *cho_font_name_normalize(const char *name); -enum FontFamily cho_font_family_parse(const char *str); -const char *cho_font_family_to_config_string(enum FontFamily font_family); -enum FontStyle cho_font_style_parse(const char *str); -const char *cho_font_style_to_config_string(enum FontStyle style); -enum FontWeight cho_font_weight_parse(const char *str); -const char *cho_font_weight_to_config_string(enum FontWeight weight); - -void cho_debug_style_print(struct ChoStyle *style); -// void cho_debug_songs_print(struct ChoSong **songs); - -#endif /* _CHORDPRO_H_ */ diff --git a/config.c b/config.c @@ -1,1065 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <toml.h> -#include "types.h" -#include "config.h" -#include "chordpro.h" -#include "util.h" -#include "chord_diagram.h" - -static const char *notation_systems[] = { - "common", - "german", - "scandinavian", - "latin", - "roman", - "nashville", - "custom" // TODO: Is this needed -}; - -static const char *parse_modes[] = { - "strict", - "relaxed" -}; - -static const char *instruments[] = { - "guitar", - "keyboard", - "mandolin", - "ukulele" -}; - -static const char *text_types[] = { - "chord", "annotation", "chorus", - "footer", "grid", "tab", "toc", "toc_title", "text", - "title", "subtitle", "label", "comment", - "comment_italic", "comment_box" -}; - -static const char *alignments[] = { - "left", "center", "right" -}; - -/* static const char *g_valid_styles[] = { - "title", - "subtitle", - "footer", // TODO - "text", - "chorus", - "chord", - "annotation", - "comment", - "comment_italic", - "comment_boxed", - "tab", - "label", - "toc", - "grid", - "grid_margin", // TODO - "empty", // TODO - "diagram", // TODO - "diagram_base", // TODO - "chordfingers" // TODO -}; */ - -static struct Note notes_common[] = { - { .note = "C", .sharp = "C#", .flat = NULL }, - { .note = "D", .sharp = "D#", .flat = "Db" }, - { .note = "E", .sharp = NULL, .flat = "Eb" }, - { .note = "F", .sharp = "F#", .flat = NULL }, - { .note = "G", .sharp = "G#", .flat = "Gb" }, - { .note = "A", .sharp = "A#", .flat = "Ab" }, - { .note = "B", .sharp = NULL, .flat = "Bb" } -}; - -static struct Note notes_german[] = { - { .note = "C", .sharp = "Cis", .flat = NULL }, - { .note = "D", .sharp = "Dis", .flat = "Des" }, - { .note = "E", .sharp = NULL, .flat = "Es" }, - { .note = "F", .sharp = "Fis", .flat = NULL }, - { .note = "G", .sharp = "Gis", .flat = "Ges" }, - { .note = "A", .sharp = "Ais", .flat = "As" }, - { .note = "H", .sharp = NULL, .flat = "B"}, -}; - -static struct Note notes_scandinavian[] = { - { .note = "C", .sharp = "C#", .flat = NULL }, - { .note = "D", .sharp = "D#", .flat = "Db"}, - { .note = "E", .sharp = NULL, .flat = "Eb"}, - { .note = "F", .sharp = "F#", .flat = NULL }, - { .note = "G", .sharp = "G#", .flat = "Gb"}, - { .note = "A", .sharp = "A#", .flat = "Ab"}, - { .note = "H", .sharp = NULL, .flat = "B"}, -}; - -static struct Note notes_latin[] = { - { .note = "Do", .sharp = "Do#", .flat = NULL }, - { .note = "Re", .sharp = "Re#", .flat = "Reb"}, - { .note = "Mi", .sharp = NULL, .flat = "Mib"}, - { .note = "Fa", .sharp = "Fa#", .flat = NULL }, - { .note = "Sol", .sharp = "Sol#", .flat = "Solb"}, - { .note = "La", .sharp = "La#", .flat = "Lab"}, - { .note = "Si", .sharp = NULL, .flat = "Sib"}, -}; - -static struct Note notes_roman[] = { - { .note = "I", .sharp = "I#", .flat = NULL }, - { .note = "II", .sharp = "II#", .flat = "IIb" }, - { .note = "III", .sharp = NULL, .flat = "IIIb" }, - { .note = "IV", .sharp = "IV#", .flat = NULL }, - { .note = "V", .sharp = "V#", .flat = "Vb" }, - { .note = "VI", .sharp = "VI#", .flat = "VIb" }, - { .note = "VII", .sharp = NULL, .flat = "VIIb" }, -}; - -static struct Note notes_nashville[] = { - { .note = "1", .sharp = "1#", .flat = NULL }, - { .note = "2", .sharp = "2#", .flat = "2b" }, - { .note = "3", .sharp = NULL, .flat = "3b" }, - { .note = "4", .sharp = "4#", .flat = NULL }, - { .note = "5", .sharp = "5#", .flat = "5b" }, - { .note = "6", .sharp = "6#", .flat = "6b" }, - { .note = "7", .sharp = NULL, .flat = "7b" }, -}; - -static enum TextType -config_text_type_parse(const char *str) -{ - if (!strcmp(str, text_types[TT_CHORD])) { - return TT_CHORD; - } else - if (!strcmp(str, text_types[TT_ANNOT])) { - return TT_ANNOT; - } else - if (!strcmp(str, text_types[TT_CHORUS])) { - return TT_CHORUS; - } else - if (!strcmp(str, text_types[TT_FOOTER])) { - return TT_FOOTER; - } else - if (!strcmp(str, text_types[TT_GRID])) { - return TT_GRID; - } else - if (!strcmp(str, text_types[TT_TAB])) { - return TT_TAB; - } else - if (!strcmp(str, text_types[TT_TOC])) { - return TT_TOC; - } else - if (!strcmp(str, text_types[TT_TOC_TITLE])) { - return TT_TOC_TITLE; - } else - if (!strcmp(str, text_types[TT_TEXT])) { - return TT_TEXT; - } else - if (!strcmp(str, text_types[TT_TITLE])) { - return TT_TITLE; - } else - if (!strcmp(str, text_types[TT_SUBTITLE])) { - return TT_SUBTITLE; - } else - if (!strcmp(str, text_types[TT_LABEL])) { - return TT_LABEL; - } else - if (!strcmp(str, text_types[TT_COMMENT])) { - return TT_COMMENT; - } else - if (!strcmp(str, text_types[TT_COMMENT_ITALIC])) { - return TT_COMMENT_ITALIC; - } else - if (!strcmp(str, text_types[TT_COMMENT_BOX])) { - return TT_COMMENT_BOX; - } - return -1; -} - -static enum NotationSystem -config_notation_system_parse(const char *str) -{ - if (!strcmp(str, notation_systems[NS_COMMON]) || !strcmp(str, "dutch")) { - return NS_COMMON; - } else if (!strcmp(str, notation_systems[NS_GERMAN])) { - return NS_GERMAN; - } else if (!strcmp(str, notation_systems[NS_SCANDINAVIAN])) { - return NS_SCANDINAVIAN; - } else if (!strcmp(str, notation_systems[NS_LATIN])) { - return NS_LATIN; - } else if (!strcmp(str, notation_systems[NS_ROMAN])) { - return NS_ROMAN; - } else if (!strcmp(str, notation_systems[NS_NASHVILLE])) { - return NS_NASHVILLE; - } else { - return NS_CUSTOM; - } -} - -static const char * -config_notation_system_to_config_string(enum NotationSystem system) -{ - return notation_systems[system]; -} - -static enum Instrument -config_instrument_parse(const char *str) -{ - if (!strcmp(str, "guitar")) { - return INS_GUITAR; - } else - if (!strcmp(str, "keyboard")) { - return INS_KEYBOARD; - } else - if (!strcmp(str, "mandolin")) { - return INS_MANDOLIN; - } else - if (!strcmp(str, "ukulele")) { - return INS_UKULELE; - } - return -1; -} - -static const char * -config_instrument_to_config_string(enum Instrument ins) -{ - return instruments[ins]; -} - -static struct Note * -config_note_new(void) -{ - struct Note *note = emalloc(sizeof(struct Note)); - note->note = NULL; - note->sharp = NULL; - note->flat = NULL; - return note; -} - -static void -config_note_free(struct Note *note) -{ - free(note->note); - free(note->sharp); - free(note->flat); - free(note); -} - -static void -config_notes_free(struct Note **notes) -{ - int i; - for (i = 0; i<7; i++) { - config_note_free(notes[i]); - } - free(notes); -} - -static struct Note ** -config_notes_new_default(enum NotationSystem system) -{ - struct Note **notes_default = emalloc(8 * sizeof(struct Note *)); - struct Note *notes; - switch (system) { - case NS_GERMAN: - notes = (struct Note *)&notes_german; - break; - case NS_SCANDINAVIAN: - notes = (struct Note *)&notes_scandinavian; - break; - case NS_LATIN: - notes = (struct Note *)&notes_latin; - break; - case NS_ROMAN: - notes = (struct Note *)&notes_roman; - break; - case NS_NASHVILLE: - notes = (struct Note *)&notes_nashville; - break; - default: - notes = (struct Note *)&notes_common; - break; - } - int i; - for (i = 0; i<7; i++) { - notes_default[i] = config_note_new(); - if (notes[i].note) { - notes_default[i]->note = strdup(notes[i].note); - } - if (notes[i].sharp) { - notes_default[i]->sharp = strdup(notes[i].sharp); - } - if (notes[i].flat) { - notes_default[i]->flat = strdup(notes[i].flat); - } - } - // notes_default[7] = NULL; // TODO: This is probably needless - return notes_default; -} - -static struct Note ** -config_notes_load(toml_table_t *notes, const char *system) -{ - struct Note **custom_notes = emalloc(8 * sizeof(struct Note *)); - toml_array_t *arr = toml_table_array(notes, system); - int arr_len = toml_array_len(arr); - if (arr_len != 7) { - util_log(LOG_ERR, "Custom notation system '%s' in [notes] has to have exactly 7 items. For an example see `lorid --print-default-config`.", system); - free(notes); - return NULL; - } - toml_table_t *note; - toml_value_t value; - int i; - for (i = 0; i<arr_len; i++) { - note = toml_array_table(arr, i); - if (note) { - custom_notes[i] = config_note_new(); - value = toml_table_string(note, "note"); - if (value.ok) { - custom_notes[i]->note = value.u.s; - } - value = toml_table_string(note, "sharp"); - if (value.ok) { - custom_notes[i]->sharp = value.u.s; - if (i == 2 || i == 6) { - util_log(LOG_ERR, "Custom notation system '%s' in [notes] can't have sharp value at array index '%d'.", system, i); - goto CLEAN; - } - } - value = toml_table_string(note, "flat"); - if (value.ok) { - custom_notes[i]->flat = value.u.s; - if (i == 0 || i == 3) { - util_log(LOG_ERR, "Custom notation system '%s' in [notes] can't have flat value at array index '%d'.", system, i); - goto CLEAN; - } - } - } - } - custom_notes[7] = NULL; - return custom_notes; - CLEAN: - for (int k=i; k>=0; k--) { - config_note_free(custom_notes[k]); - } - free(custom_notes); - return NULL; -} - -static void -config_notes_print_as_toml(enum NotationSystem system) -{ - struct Note *notes; - switch (system) { - case NS_COMMON: - notes = (struct Note *)&notes_common; - break; - case NS_GERMAN: - notes = (struct Note *)&notes_german; - break; - case NS_SCANDINAVIAN: - notes = (struct Note *)&notes_scandinavian; - break; - case NS_LATIN: - notes = (struct Note *)&notes_latin; - break; - case NS_ROMAN: - notes = (struct Note *)&notes_roman; - break; - case NS_NASHVILLE: - notes = (struct Note *)&notes_nashville; - break; - case NS_CUSTOM: - return; - } - printf("%s = [\n", config_notation_system_to_config_string(system)); - int i; - for (i = 0; i<7; i++) { - printf("\t{ note = \"%s\",", notes[i].note); - if (notes[i].sharp) { - printf(" sharp = \"%s\",", notes[i].sharp); - } - if (notes[i].flat) { - printf(" flat = \"%s\"", notes[i].flat); - } - printf(" },\n"); - } - printf("]\n\n"); -} - -static const char * -config_parse_mode_to_config_string(enum ParseMode mode) -{ - return parse_modes[mode]; -} - -static enum Alignment -config_alignment_parse(const char *str) -{ - if (!strcmp(str, "left")) { - return A_LEFT; - } else - if (!strcmp(str, "center")) { - return A_CENTER; - } else - if (!strcmp(str, "right")) { - return A_RIGHT; - } - return -1; -} - -static const char * -config_alignment_to_config_string(enum Alignment align) -{ - return alignments[align]; -} - -static struct Config * -config_load_default(void) -{ - struct Config *config = emalloc(sizeof(struct Config)); - config->output = emalloc(sizeof(struct ConfigOutput)); - config->output->toc = emalloc(sizeof(struct ConfigToc)); - config->output->toc->show = false; - config->output->toc->title = strdup("Table Of Contents"); - 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_GUITAR; - config->output->page_no = emalloc(sizeof(struct ConfigPageNo)); - config->output->page_no->show = true; - config->output->page_no->align = A_CENTER; - config->output->notation_system = NS_COMMON; - config->output->start_song_on_new_page = true; - config->output->styles = emalloc(TT_LENGTH * sizeof(struct ChoStyle *)); - - config->output->styles[TT_CHORD] = cho_style_new(); - config->output->styles[TT_CHORD]->font->name = strdup(DEFAULT_FONT_FAMILY); - config->output->styles[TT_CHORD]->font->weight = FW_BOLD; - config->output->styles[TT_ANNOT] = cho_style_new(); - config->output->styles[TT_ANNOT]->font->name = strdup(DEFAULT_FONT_FAMILY); - config->output->styles[TT_ANNOT]->font->style = FS_ITALIC; - config->output->styles[TT_CHORUS] = cho_style_new(); - config->output->styles[TT_CHORUS]->font->name = strdup(DEFAULT_FONT_FAMILY); - config->output->styles[TT_FOOTER] = cho_style_new(); - config->output->styles[TT_GRID] = cho_style_new(); - config->output->styles[TT_GRID]->font->name = strdup(DEFAULT_FONT_FAMILY); - config->output->styles[TT_GRID]->font->weight = FW_BOLD; - config->output->styles[TT_TAB] = cho_style_new(); - config->output->styles[TT_TAB]->font->name = strdup("Courier"); - // config->output->styles[TT_TAB]->font->family = FF_MONOSPACE; - config->output->styles[TT_TOC] = cho_style_new(); - config->output->styles[TT_TOC]->font->name = strdup(DEFAULT_FONT_FAMILY); - config->output->styles[TT_TOC]->font->size = 12.0; - config->output->styles[TT_TOC_TITLE] = cho_style_new(); - config->output->styles[TT_TOC_TITLE]->font->name = strdup(DEFAULT_FONT_FAMILY); - config->output->styles[TT_TOC_TITLE]->font->weight = FW_BOLD; - config->output->styles[TT_TOC_TITLE]->font->size = 18.0; - config->output->styles[TT_TEXT] = cho_style_new(); - config->output->styles[TT_TEXT]->font->name = strdup(DEFAULT_FONT_FAMILY); - config->output->styles[TT_TITLE] = cho_style_new(); - config->output->styles[TT_TITLE]->font->name = strdup(DEFAULT_FONT_FAMILY); - config->output->styles[TT_TITLE]->font->weight = FW_BOLD; - config->output->styles[TT_TITLE]->font->size = 18.0; - config->output->styles[TT_SUBTITLE] = cho_style_new(); - config->output->styles[TT_SUBTITLE]->font->name = strdup(DEFAULT_FONT_FAMILY); - config->output->styles[TT_SUBTITLE]->font->size = 12.0; - config->output->styles[TT_LABEL] = cho_style_new(); - config->output->styles[TT_LABEL]->font->name = strdup(DEFAULT_FONT_FAMILY); - config->output->styles[TT_LABEL]->font->style = FS_ITALIC; - config->output->styles[TT_COMMENT] = cho_style_new(); - config->output->styles[TT_COMMENT]->font->name = strdup(DEFAULT_FONT_FAMILY); - config->output->styles[TT_COMMENT]->background_color->red = 228; - config->output->styles[TT_COMMENT]->background_color->green = 228; - config->output->styles[TT_COMMENT]->background_color->blue = 228; - config->output->styles[TT_COMMENT_ITALIC] = cho_style_new(); - config->output->styles[TT_COMMENT_ITALIC]->font->name = strdup(DEFAULT_FONT_FAMILY); - config->output->styles[TT_COMMENT_ITALIC]->font->style = FS_ITALIC; - config->output->styles[TT_COMMENT_BOX] = cho_style_new(); - config->output->styles[TT_COMMENT_BOX]->font->name = strdup(DEFAULT_FONT_FAMILY); - config->output->styles[TT_COMMENT_BOX]->boxed = true; - - // config->output->styles[15] = NULL; - config->output->notes = config_notes_new_default(NS_COMMON); - config->parser = emalloc(sizeof(struct ConfigParser)); - config->parser->chords = emalloc(sizeof(struct ConfigChords)); - config->parser->chords->notation_system = NS_COMMON; - config->parser->chords->mode = PM_STRICT; - config->parser->notes = config_notes_new_default(NS_COMMON); - return config; -} - -void -config_print_default(void) -{ - struct Config *config = config_load_default(); - printf("[notes]\n"); - config_notes_print_as_toml(NS_COMMON); - config_notes_print_as_toml(NS_GERMAN); - config_notes_print_as_toml(NS_SCANDINAVIAN); - config_notes_print_as_toml(NS_LATIN); - config_notes_print_as_toml(NS_ROMAN); - config_notes_print_as_toml(NS_NASHVILLE); - - printf("[parser]\n"); - printf("[parser.chords]\n"); - printf("mode = \"%s\"\n", config_parse_mode_to_config_string(config->parser->chords->mode)); - printf("notation_system = \"%s\"\n\n", config_notation_system_to_config_string(config->parser->chords->notation_system)); - - printf("[output]\n"); - printf("notation_system = \"%s\"\n", config_notation_system_to_config_string(config->output->notation_system)); - printf("start_song_on_new_page = %s\n\n", config->output->start_song_on_new_page ? "true" : "false"); - printf("[output.toc]\n"); - printf("show = %s\n", config->output->toc->show ? "true" : "false"); - printf("title = \"%s\"\n\n", config->output->toc->title); - printf("[output.chord_diagram]\n"); - printf("show = %s\n", config->output->diagram->show ? "true" : "false"); - printf("instrument = \"%s\"\n\n", config_instrument_to_config_string(config->output->diagram->instrument)); - printf("[output.chorus]\n"); - printf("label = \"Chorus\"\n"); - printf("quote = false\n\n"); - printf("[output.page_no]\n"); - printf("show = %s\n", config->output->page_no->show ? "true" : "false"); - printf("alignment = \"%s\"\n\n", config_alignment_to_config_string(config->output->page_no->align)); - printf("[output.styles]\n"); - int i; - for (i = 1; i<TT_LENGTH; i++) { - printf("[output.styles.%s]\n\n", text_types[i]); - cho_style_print_as_toml(config->output->styles[i], text_types[i]); - } - config_free(config); -} - -static bool -config_load_font(struct Font *font, toml_table_t *table, const char *key_name, struct ChoStylePresence *presence) -{ - enum FontFamily family; - enum FontStyle style; - enum FontWeight weight; - toml_value_t value; - value = toml_table_string(table, "name"); - if (value.ok) { - presence->font.name = true; - free(font->name); - font->name = value.u.s; - } - value = toml_table_string(table, "family"); - if (value.ok) { - presence->font.family = true; - family = cho_font_family_parse(value.u.s); - if (family != -1) { - font->family = family; - } else { - util_log(LOG_ERR, "Config section [output.styles.%s.font] family value is invalid.", key_name); - return false; - } - free(value.u.s); - } - value = toml_table_string(table, "style"); - if (value.ok) { - presence->font.style = true; - style = cho_font_style_parse(value.u.s); - if (style != -1) { - font->style = style; - } else { - util_log(LOG_ERR, "Config section [output.styles.%s.font] style value is invalid.", key_name); - return false; - } - free(value.u.s); - } - value = toml_table_string(table, "weight"); - if (value.ok) { - presence->font.weight = true; - weight = cho_font_weight_parse(value.u.s); - if (weight != -1) { - font->weight = weight; - } else { - util_log(LOG_ERR, "Config section [output.styles.%s.font] weight value is invalid.", key_name); - return false; - } - free(value.u.s); - } - value = toml_table_int(table, "size"); - if (value.ok) { - presence->font.size = true; - font->size = value.u.i; - } - return true; -} - -static bool -config_load_style( - struct ChoStyle *style, - toml_table_t *table, - const char *key_name, - struct ChoStylePresence *presence -) -{ - toml_value_t value; - struct RGBColor *color; - enum LineStyle line_style; - toml_table_t *font_section = toml_table_table(table, "font"); - if (font_section) { - if (!config_load_font(style->font, font_section, key_name, presence)) { - LOG_DEBUG("config_load_font failed."); - return false; - } - } - value = toml_table_string(table, "foreground_color"); - if (value.ok) { - presence->foreground_color = true; - color = cho_color_parse(value.u.s); - if (color) { - free(style->foreground_color); - style->foreground_color = color; - } else { - util_log(LOG_ERR, "Config section [output.styles.%s] foreground color value is invalid.", key_name); - return false; - } - free(value.u.s); - } - value = toml_table_string(table, "background_color"); - if (value.ok) { - presence->background_color = true; - color = cho_color_parse(value.u.s); - if (color) { - free(style->background_color); - style->background_color = color; - } else { - util_log(LOG_ERR, "Config section [output.styles.%s] background color value is invalid.", key_name); - return false; - } - free(value.u.s); - } - value = toml_table_string(table, "underline_style"); - if (value.ok) { - presence->underline_style = true; - line_style = cho_linestyle_parse(value.u.s); - if (line_style != -1) { - style->underline_style = line_style; - } else { - util_log(LOG_ERR, "Config section [output.styles.%s] underline style value is invalid.", key_name); - return false; - } - free(value.u.s); - } - value = toml_table_string(table, "underline_color"); - if (value.ok) { - presence->underline_color = true; - color = cho_color_parse(value.u.s); - if (color) { - free(style->underline_color); - style->underline_color = color; - } else { - util_log(LOG_ERR, "Config section [output.styles.%s] underline color value is invalid.", key_name); - return false; - } - free(value.u.s); - } - value = toml_table_string(table, "overline_style"); - if (value.ok) { - presence->overline_style = true; - line_style = cho_linestyle_parse(value.u.s); - if (line_style != -1) { - style->overline_style = line_style; - } else { - util_log(LOG_ERR, "Config section [output.styles.%s] overline style value is invalid.", key_name); - return false; - } - free(value.u.s); - } - value = toml_table_string(table, "overline_color"); - if (value.ok) { - presence->overline_color = true; - color = cho_color_parse(value.u.s); - if (color) { - free(style->overline_color); - style->overline_color = color; - } else { - util_log(LOG_ERR, "Config section [output.styles.%s] overline color valeu is invalid.", key_name); - return false; - } - free(value.u.s); - } - value = toml_table_bool(table, "strikethrough"); - if (value.ok) { - presence->strikethrough = true; - style->strikethrough = value.u.b; - } - value = toml_table_string(table, "strikethrough_color"); - if (value.ok) { - presence->strikethrough_color = true; - color = cho_color_parse(value.u.s); - if (color) { - free(style->strikethrough_color); - style->strikethrough_color = color; - } else { - util_log(LOG_ERR, "Config section [output.styles.%s] strikethrough color value is invalid.", key_name); - return false; - } - free(value.u.s); - } - value = toml_table_bool(table, "boxed"); - if (value.ok) { - presence->boxed = true; - style->boxed = value.u.b; - } - value = toml_table_string(table, "boxed_color"); - if (value.ok) { - presence->boxed_color = true; - color = cho_color_parse(value.u.s); - if (color) { - free(style->boxed_color); - style->boxed_color = color; - } else { - util_log(LOG_ERR, "Config section [output.styles.%s] boxed color value is invalid.", key_name); - return false; - } - free(value.u.s); - } - value = toml_table_double(table, "rise"); - if (value.ok) { - presence->rise = true; - style->rise = value.u.d; - } - value = toml_table_string(table, "href"); - if (value.ok) { - presence->href = true; - style->href = value.u.s; - } - return true; -} - -#ifdef DEBUG - -static void -presence_print(const char *name, struct ChoStylePresence *presence) -{ - printf("---- BEGIN PRESENCE ----\n"); - printf("style '%s'\n", name); - printf("font.name %d\n", presence->font.name); - printf("font.family %d\n", presence->font.family); - printf("font.style %d\n", presence->font.style); - printf("font.weight %d\n", presence->font.weight); - printf("font.size %d\n", presence->font.size); - printf("foreground_color %d\n", presence->foreground_color); - printf("background_color %d\n", presence->background_color); - printf("underline_style %d\n", presence->underline_style); - printf("underline_color %d\n", presence->underline_color); - printf("overline_style %d\n", presence->overline_style); - printf("overline_color %d\n", presence->overline_color); - printf("strikethrough %d\n", presence->strikethrough); - printf("strikethrough_color %d\n", presence->strikethrough_color); - printf("boxed %d\n", presence->boxed); - printf("boxed_color %d\n", presence->boxed_color); - printf("rise %d\n", presence->rise); - printf("href %d\n", presence->href); - printf("---- END PRESENCE ------\n"); -} - -#endif /* DEBUG */ - -static void -set_text_style( - struct ChoStylePresence *text_presence, - struct ChoStyle *text_style, - struct ChoStylePresence *presence, - struct ChoStyle *style -) -{ - - if (!presence->font.name && - text_presence->font.name) { - free(style->font->name); - style->font->name = strdup(text_style->font->name); - } - if (!presence->font.family && text_presence->font.family) { - style->font->family = text_style->font->family; - } - if (!presence->font.style && text_presence->font.style) { - style->font->style = text_style->font->style; - } - if (!presence->font.weight && text_presence->font.weight) { - style->font->weight = text_style->font->weight; - } - if (!presence->font.size && text_presence->font.size) { - style->font->size = text_style->font->size; - } - if (!presence->foreground_color && text_presence->foreground_color) { - free(style->foreground_color); - style->foreground_color = cho_color_copy(text_style->foreground_color); - } - if (!presence->background_color && text_presence->background_color) { - free(style->background_color); - style->background_color = cho_color_copy(text_style->background_color); - } - if (!presence->underline_style && text_presence->underline_style) { - style->underline_style = text_style->underline_style; - } - if (!presence->underline_color && text_presence->underline_color) { - free(style->underline_color); - style->underline_color = cho_color_copy(text_style->underline_color); - } - if (!presence->overline_style && text_presence->overline_style) { - style->overline_style = text_style->overline_style; - } - if (!presence->overline_color && text_presence->overline_color) { - free(style->overline_color); - style->overline_color = cho_color_copy(text_style->overline_color); - } - if (!presence->strikethrough && text_presence->strikethrough) { - style->strikethrough = text_style->strikethrough; - } - if (!presence->strikethrough_color && text_presence->strikethrough_color) { - free(style->strikethrough_color); - style->strikethrough_color = cho_color_copy(text_style->strikethrough_color); - } - if (!presence->boxed && text_presence->boxed) { - style->boxed = text_style->boxed; - } - if (!presence->boxed_color && text_presence->boxed_color) { - free(style->boxed_color); - style->boxed_color = cho_color_copy(text_style->boxed_color); - } - if (!presence->rise && text_presence->rise) { - style->rise = text_style->rise; - } - if (!presence->href && text_presence->href) { - free(style->href); - style->href = strdup(text_style->href); - } -} - -static void -lyrics_set_text_style_as_default( - struct ChoStylePresence presences[], - struct ChoStyle **styles -) -{ - struct ChoStyle *style, *text_style; - struct ChoStylePresence *presence, *text_presence; - enum TextType lyric_types[] = { - TT_CHORUS, TT_COMMENT, - TT_COMMENT_ITALIC, TT_COMMENT_BOX - }; - text_presence = &presences[TT_TEXT]; - text_style = styles[TT_TEXT]; - size_t i; - for (i = 0; i<LENGTH(lyric_types); i++) { - presence = &presences[lyric_types[i]]; - style = styles[lyric_types[i]]; - set_text_style(text_presence, text_style, presence, style); - } -} - -struct Config * -config_load(const char *filepath) -{ - struct Config *config = config_load_default(); - char *home = getenv("HOME"); - char path[26+strlen(home)+1]; - if (!filepath) { - sprintf(path, "%s/.config/lorid/config.toml", home); - filepath = path; - } - FILE *fp = fopen(filepath, "r"); - if (!fp) { - util_log(LOG_WARN, "Couldn't open config file '%s'. Using default configuration.", filepath); - return config; - } - char errbuf[200]; - toml_table_t *table = toml_parse_file(fp, (char *)&errbuf, sizeof(errbuf)); - if (!table) { - LOG_DEBUG("toml_parse_file failed."); - util_log(LOG_ERR, "Config file is not a valid toml file: %s.", (char *)&errbuf); - return NULL; - } - toml_table_t *output = toml_table_table(table, "output"); - if (output) { - toml_table_t *styles, *notes, *chorus, *diagram, *toc, *page_no; - toml_value_t value; - enum NotationSystem notation_system; - enum Instrument instrument; - enum Alignment align; - struct Note **custom_notes; - chorus = toml_table_table(output, "chorus"); - if (chorus) { - value = toml_table_string(chorus, "label"); - if (value.ok) { - free(config->output->chorus->label); - config->output->chorus->label = value.u.s; - } - value = toml_table_bool(chorus, "quote"); - if (value.ok) { - config->output->chorus->quote = value.u.b; - } - } - toc = toml_table_table(output, "toc"); - if (toc) { - value = toml_table_bool(toc, "show"); - if (value.ok) { - config->output->toc->show = value.u.b; - } - value = toml_table_string(toc, "title"); - if (value.ok) { - free(config->output->toc->title); - config->output->toc->title = value.u.s; - } - } - 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 == -1) { - 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, "notation_system"); - if (value.ok) { - notation_system = config_notation_system_parse(value.u.s); - if (notation_system == NS_CUSTOM) { - notes = toml_table_table(table, "notes"); - if (!notes) { - util_log(LOG_ERR, "Custom notes '%s' has no corresponding definition in [notes].", value.u.s); - return NULL; - } - custom_notes = config_notes_load(notes, value.u.s); - if (custom_notes) { - config_notes_free(config->output->notes); - config->output->notes = custom_notes; - } else { - LOG_DEBUG("config_notes_load failed."); - util_log(LOG_ERR, "Couldn't load custom notes '%s' from [notes] section.", value.u.s); - return NULL; - } - } else { - config_notes_free(config->output->notes); - config->output->notes = config_notes_new_default(notation_system); - } - free(value.u.s); - } - value = toml_table_bool(output, "start_song_on_new_page"); - if (value.ok) { - config->output->start_song_on_new_page = value.u.b; - } - page_no = toml_table_table(output, "page_no"); - if (page_no) { - value = toml_table_bool(page_no, "show"); - if (value.ok) { - config->output->page_no->show = value.u.b; - } - value = toml_table_string(page_no, "alignment"); - if (value.ok) { - align = config_alignment_parse(value.u.s); - if (align == -1) { - LOG_DEBUG("config_alignment_parse failed."); - return NULL; - } - config->output->page_no->align = align; - free(value.u.s); - } - } - styles = toml_table_table(output, "styles"); - if (styles) { - int i, unused; - const char *key_name; - enum TextType ttype; - struct ChoStyle *style; - struct ChoStylePresence presences[TT_LENGTH] = {0}; - toml_table_t *key; - for (i = 0; i<toml_table_len(styles); i++) { - key_name = toml_table_key(styles, i, &unused); - ttype = config_text_type_parse(key_name); - if (ttype != -1) { - key = toml_table_table(styles, key_name); - if (key) { - style = config->output->styles[ttype]; - if (!config_load_style(style, key, key_name, &presences[ttype])) { - LOG_DEBUG("config_load_style failed."); - return NULL; - } - } - } - } - lyrics_set_text_style_as_default(presences, config->output->styles); - } - } - toml_table_t *parser = toml_table_table(table, "parser"); - if (parser) { - toml_table_t *chords = toml_table_table(parser, "chords"); - if (chords) { - toml_table_t *notes; - toml_value_t value; - enum NotationSystem notation_system; - struct Note **custom_notes; - value = toml_table_string(chords, "notation_system"); - if (value.ok) { - notation_system = config_notation_system_parse(value.u.s); - if (notation_system == NS_CUSTOM) { - notes = toml_table_table(table, "notes"); - if (!notes) { - util_log(LOG_ERR, "Custom notes '%s' has no corresponding definition in [notes].", value.u.s); - return NULL; - } - custom_notes = config_notes_load(notes, value.u.s); - if (custom_notes) { - config_notes_free(config->parser->notes); - config->parser->notes = custom_notes; - } else { - LOG_DEBUG("config_notes_load failed."); - util_log(LOG_ERR, "Couldn't load custom notes '%s' from [notes] section.", value.u.s); - return NULL; - } - } else { - config_notes_free(config->parser->notes); - config->parser->notes = config_notes_new_default(notation_system); - } - free(value.u.s); - } - value = toml_table_string(chords, "mode"); - if (value.ok) { - if (!strcmp(value.u.s, "strict")) { - config->parser->chords->mode = PM_STRICT; - } else if (!strcmp(value.u.s, "relaxed")) { - config->parser->chords->mode = PM_RELAXED; - } - free(value.u.s); - } - } - } - toml_free(table); - fclose(fp); - return config; -} - -void -config_free(struct Config *config) -{ - free(config->output->toc->title); - free(config->output->toc); - free(config->output->chorus->label); - free(config->output->chorus); - int i; - for (i = 0; i<TT_LENGTH; i++) { - cho_style_free(config->output->styles[i]); - } - free(config->output->styles); - config_notes_free(config->output->notes); - free(config->output->diagram); - free(config->output->page_no); - free(config->output); - free(config->parser->chords); - config_notes_free(config->parser->notes); - free(config->parser); - free(config); -} diff --git a/config.h b/config.h @@ -1,17 +0,0 @@ -#include "types.h" - -#ifndef _CONFIG_H_ -#define _CONFIG_H_ - -#ifdef DEBUG -#define SYMBOLS_FILEPATH "./misc/ChordProSymbols.ttf" -#else -#define SYMBOLS_FILEPATH PREFIX"/share/lorid/ChordProSymbols.ttf" -#endif /* DEBUG */ -#define DEFAULT_FONT_FAMILY "Open Sans" - -struct Config *config_load(const char *filepath); -void config_free(struct Config *config); -void config_print_default(void); - -#endif /* _CONFIG_H_ */ diff --git a/diagrams.h b/diagrams.h @@ -1,4394 +0,0 @@ -static struct StringDiagram guitar_diagrams[] = { - { - .name = "C", - .base_fret = 1, - .frets = { 0, 3, 2, 0, 1, 0, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 3, 2, 0, 1, 0, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Cm", - .base_fret = 3, - .frets = { 1, 1, 3, 3, 2, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 3, 4, 2, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C+", - .base_fret = 1, - .frets = { -1, -1, 2, 1, 1, 4, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 2, 1, 1, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Caug", - .base_fret = 1, - .frets = { -1, -1, 2, 1, 1, 4, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 2, 1, 1, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Cdim", - .base_fret = 3, - .frets = { -1, 1, 2, 3, 2, -1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 1, 2, 4, 3, 0, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C0", - .base_fret = 1, - .frets = { -1, -1, 1, 2, 1, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 3, 2, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Cdim", - .base_fret = 1, - .frets = { -1, -1, 1, 2, 1, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 3, 2, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Cdim7", - .base_fret = 1, - .frets = { -1, -1, 1, 2, 1, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 3, 2, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C7", - .base_fret = 1, - .frets = { 0, 3, 2, 3, 1, 0, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 3, 2, 4, 1, 0, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Cmaj7", - .base_fret = 1, - .frets = { -1, 3, 2, 0, 0, 0, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 3, 2, 0, 0, 0, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Cm7", - .base_fret = 3, - .frets = { 1, 1, 3, 1, 2, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 3, 1, 2, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C#", - .base_fret = 1, - .frets = { -1, -1, 3, 1, 2, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 3, 1, 2, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C#m", - .base_fret = 1, - .frets = { -1, -1, 2, 1, 2, 0, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 2, 1, 3, 0, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C#+", - .base_fret = 2, - .frets = { -1, 3, 2, 1, 1, -1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 4, 3, 1, 2, 0, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C#aug", - .base_fret = 2, - .frets = { -1, 3, 2, 1, 1, -1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 4, 3, 1, 2, 0, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C#dim", - .base_fret = 1, - .frets = { -1, -1, 2, 0, 2, 0, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 3, 0, 4, 0, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C#0", - .base_fret = 2, - .frets = { -1, -1, 1, 2, 1, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 3, 2, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C#dim", - .base_fret = 2, - .frets = { -1, -1, 1, 2, 1, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 3, 2, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C#dim7", - .base_fret = 2, - .frets = { -1, -1, 1, 2, 1, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 3, 2, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C#7", - .base_fret = 2, - .frets = { -1, -1, 2, 3, 1, 3, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 2, 3, 1, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C#maj7", - .base_fret = 1, - .frets = { -1, 4, 3, 1, 1, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 4, 3, 1, 1, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C#m7", - .base_fret = 1, - .frets = { -1, 4, 2, 1, 0, 0, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 4, 2, 1, 0, 0, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Db", - .base_fret = 1, - .frets = { -1, -1, 3, 1, 2, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 3, 1, 2, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Dbm", - .base_fret = 1, - .frets = { -1, -1, 2, 1, 2, 0, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 2, 1, 3, 0, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Db+", - .base_fret = 2, - .frets = { -1, 3, 2, 1, 1, -1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 4, 3, 1, 2, 0, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Dbaug", - .base_fret = 2, - .frets = { -1, 3, 2, 1, 1, -1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 4, 3, 1, 2, 0, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Dbdim", - .base_fret = 1, - .frets = { -1, -1, 2, 0, 2, 0, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 3, 0, 4, 0, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Db0", - .base_fret = 2, - .frets = { -1, -1, 1, 2, 1, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 3, 2, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Dbdim", - .base_fret = 2, - .frets = { -1, -1, 1, 2, 1, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 3, 2, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Dbdim7", - .base_fret = 2, - .frets = { -1, -1, 1, 2, 1, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 3, 2, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Db7", - .base_fret = 2, - .frets = { -1, -1, 2, 3, 1, 3, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 2, 3, 1, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Dbmaj7", - .base_fret = 1, - .frets = { -1, 4, 3, 1, 1, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 4, 3, 1, 1, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Dbm7", - .base_fret = 1, - .frets = { -1, 4, 2, 1, 0, 0, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 4, 2, 1, 0, 0, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D", - .base_fret = 1, - .frets = { -1, -1, 0, 2, 3, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 0, 1, 3, 2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Dm", - .base_fret = 1, - .frets = { -1, -1, 0, 2, 3, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 0, 2, 3, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D+", - .base_fret = 1, - .frets = { -1, -1, 0, 3, 3, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 0, 2, 3, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Daug", - .base_fret = 1, - .frets = { -1, -1, 0, 3, 3, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 0, 2, 3, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ddim", - .base_fret = 1, - .frets = { -1, -1, 0, 1, 3, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 0, 1, 3, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D0", - .base_fret = 1, - .frets = { -1, -1, 0, 1, 0, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 0, 1, 0, 2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ddim", - .base_fret = 1, - .frets = { -1, -1, 0, 1, 0, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 0, 1, 0, 2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ddim7", - .base_fret = 1, - .frets = { -1, -1, 0, 1, 0, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 0, 1, 0, 2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D7", - .base_fret = 1, - .frets = { -1, -1, 0, 2, 1, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 0, 2, 1, 3, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Dmaj7", - .base_fret = 1, - .frets = { -1, -1, 0, 2, 2, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 0, 1, 2, 3, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Dm7", - .base_fret = 1, - .frets = { -1, -1, 0, 2, 1, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 0, 2, 1, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D#", - .base_fret = 3, - .frets = { -1, -1, 3, 1, 2, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 3, 1, 2, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D#m", - .base_fret = 1, - .frets = { -1, -1, 4, 3, 4, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 3, 2, 4, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D#+", - .base_fret = 1, - .frets = { 3, 2, 1, 0, 0, 3, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 2, 1, 0, 0, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D#aug", - .base_fret = 1, - .frets = { 3, 2, 1, 0, 0, 3, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 2, 1, 0, 0, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D#dim", - .base_fret = 2, - .frets = { -1, -1, 3, 1, 3, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 3, 1, 4, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D#0", - .base_fret = 1, - .frets = { -1, -1, 1, 2, 1, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 3, 2, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D#dim", - .base_fret = 1, - .frets = { -1, -1, 1, 2, 1, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 3, 2, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D#dim7", - .base_fret = 1, - .frets = { -1, -1, 1, 2, 1, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 3, 2, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D#7", - .base_fret = 1, - .frets = { -1, -1, 1, 3, 2, 3, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 3, 2, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D#maj7", - .base_fret = 1, - .frets = { -1, -1, 1, 3, 3, 3, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 2, 3, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D#m7", - .base_fret = 1, - .frets = { -1, -1, 1, 3, 2, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 2, 3, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Eb", - .base_fret = 3, - .frets = { -1, -1, 3, 1, 2, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 3, 1, 2, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ebm", - .base_fret = 1, - .frets = { -1, -1, 4, 3, 4, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 3, 2, 4, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Eb+", - .base_fret = 1, - .frets = { 3, 2, 1, 0, 0, 3, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 2, 1, 0, 0, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ebaug", - .base_fret = 1, - .frets = { 3, 2, 1, 0, 0, 3, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 2, 1, 0, 0, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ebdim", - .base_fret = 2, - .frets = { -1, -1, 3, 1, 3, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 3, 1, 4, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Eb0", - .base_fret = 1, - .frets = { -1, -1, 1, 2, 1, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 3, 2, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ebdim", - .base_fret = 1, - .frets = { -1, -1, 1, 2, 1, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 3, 2, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ebdim7", - .base_fret = 1, - .frets = { -1, -1, 1, 2, 1, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 3, 2, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Eb7", - .base_fret = 1, - .frets = { -1, -1, 1, 3, 2, 3, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 3, 2, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ebmaj7", - .base_fret = 1, - .frets = { -1, -1, 1, 3, 3, 3, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 2, 3, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ebm7", - .base_fret = 1, - .frets = { -1, -1, 1, 3, 2, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 2, 3, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "E", - .base_fret = 1, - .frets = { 0, 2, 2, 1, 0, 0, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 2, 3, 1, 0, 0, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Em", - .base_fret = 1, - .frets = { 0, 2, 2, 0, 0, 0, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 2, 3, 0, 0, 0, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "E+", - .base_fret = 1, - .frets = { 0, 3, 2, 1, -1, -1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 3, 2, 1, 0, 0, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Eaug", - .base_fret = 1, - .frets = { 0, 3, 2, 1, -1, -1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 3, 2, 1, 0, 0, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Edim", - .base_fret = 3, - .frets = { -1, -1, 3, 1, 3, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 3, 1, 4, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "E0", - .base_fret = 2, - .frets = { -1, -1, 1, 2, 1, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 3, 2, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Edim", - .base_fret = 2, - .frets = { -1, -1, 1, 2, 1, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 3, 2, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Edim7", - .base_fret = 2, - .frets = { -1, -1, 1, 2, 1, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 3, 2, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "E7", - .base_fret = 1, - .frets = { 0, 2, 0, 1, 0, 0, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 2, 0, 1, 0, 0, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Emaj7", - .base_fret = 1, - .frets = { 0, 2, 1, 1, 0, -1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 3, 1, 2, 0, 0, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Em7", - .base_fret = 1, - .frets = { 0, 2, 0, 0, 0, 0, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 2, 0, 0, 0, 0, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F", - .base_fret = 1, - .frets = { 1, 3, 3, 2, 1, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 4, 2, 1, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Fm", - .base_fret = 1, - .frets = { 1, 3, 3, 1, 1, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 4, 1, 1, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F+", - .base_fret = 1, - .frets = { -1, -1, 1, 4, 4, 3, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 3, 4, 2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Faug", - .base_fret = 1, - .frets = { -1, -1, 1, 4, 4, 3, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 3, 4, 2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Fdim", - .base_fret = 4, - .frets = { -1, -1, 3, 1, 3, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 3, 1, 4, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F0", - .base_fret = 1, - .frets = { -1, -1, 0, 1, 0, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 0, 1, 0, 2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Fdim", - .base_fret = 1, - .frets = { -1, -1, 0, 1, 0, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 0, 1, 0, 2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Fdim7", - .base_fret = 1, - .frets = { -1, -1, 0, 1, 0, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 0, 1, 0, 2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F7", - .base_fret = 1, - .frets = { 1, 3, 1, 2, 1, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 1, 2, 1, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Fmaj7", - .base_fret = 1, - .frets = { -1, -1, 3, 2, 1, 0, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 3, 2, 1, 0, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Fm7", - .base_fret = 1, - .frets = { 1, 3, 1, 1, 1, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 1, 1, 1, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F#", - .base_fret = 2, - .frets = { 1, 3, 3, 2, 1, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 4, 2, 1, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F#m", - .base_fret = 2, - .frets = { 1, 3, 3, 1, 1, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 4, 1, 1, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F#+", - .base_fret = 1, - .frets = { 2, 1, 0, 3, 3, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 0, 4, 4, 3, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F#aug", - .base_fret = 1, - .frets = { 2, 1, 0, 3, 3, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 0, 4, 4, 3, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F#dim", - .base_fret = 5, - .frets = { -1, -1, 3, 1, 3, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 3, 1, 4, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F#0", - .base_fret = 1, - .frets = { -1, -1, 1, 2, 1, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 3, 2, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F#dim", - .base_fret = 1, - .frets = { -1, -1, 1, 2, 1, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 3, 2, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F#dim7", - .base_fret = 1, - .frets = { -1, -1, 1, 2, 1, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 3, 2, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F#7", - .base_fret = 2, - .frets = { 1, 3, 1, 2, 1, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 1, 2, 1, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F#maj7", - .base_fret = 1, - .frets = { -1, -1, 4, 3, 2, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 4, 3, 2, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F#m7", - .base_fret = 2, - .frets = { 1, 3, 1, 1, 1, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 1, 1, 1, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gb", - .base_fret = 2, - .frets = { 1, 3, 3, 2, 1, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 4, 2, 1, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gbm", - .base_fret = 2, - .frets = { 1, 3, 3, 1, 1, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 4, 1, 1, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gb+", - .base_fret = 1, - .frets = { 2, 1, 0, 3, 3, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 0, 4, 4, 3, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gbaug", - .base_fret = 1, - .frets = { 2, 1, 0, 3, 3, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 0, 4, 4, 3, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gbdim", - .base_fret = 5, - .frets = { -1, -1, 3, 1, 3, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 3, 1, 4, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gb0", - .base_fret = 1, - .frets = { -1, -1, 1, 2, 1, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 3, 2, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gbdim", - .base_fret = 1, - .frets = { -1, -1, 1, 2, 1, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 3, 2, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gbdim7", - .base_fret = 1, - .frets = { -1, -1, 1, 2, 1, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 3, 2, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gb7", - .base_fret = 2, - .frets = { 1, 3, 1, 2, 1, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 1, 2, 1, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gbmaj7", - .base_fret = 1, - .frets = { -1, -1, 4, 3, 2, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 4, 3, 2, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gbm7", - .base_fret = 2, - .frets = { 1, 3, 1, 1, 1, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 1, 1, 1, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G", - .base_fret = 1, - .frets = { 3, 2, 0, 0, 0, 3, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 0, 0, 0, 3, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gm", - .base_fret = 3, - .frets = { 1, 3, 3, 1, 1, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 4, 1, 1, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G+", - .base_fret = 5, - .frets = { -1, -1, 1, 4, 4, 3, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 3, 4, 2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gaug", - .base_fret = 5, - .frets = { -1, -1, 1, 4, 4, 3, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 3, 4, 2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gdim", - .base_fret = 6, - .frets = { -1, -1, 3, 1, 3, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 3, 1, 4, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G0", - .base_fret = 2, - .frets = { -1, -1, 1, 2, 1, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 3, 2, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gdim", - .base_fret = 2, - .frets = { -1, -1, 1, 2, 1, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 3, 2, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gdim7", - .base_fret = 2, - .frets = { -1, -1, 1, 2, 1, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 3, 2, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G7", - .base_fret = 1, - .frets = { 3, 2, 0, 0, 0, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 2, 0, 0, 0, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gmaj7", - .base_fret = 2, - .frets = { -1, -1, 4, 3, 2, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 4, 3, 2, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gm7", - .base_fret = 3, - .frets = { 1, 3, 1, 1, 1, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 1, 1, 1, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G#", - .base_fret = 4, - .frets = { 1, 3, 3, 2, 1, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 4, 2, 1, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G#m", - .base_fret = 4, - .frets = { 1, 3, 3, 1, 1, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 4, 1, 1, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G#+", - .base_fret = 1, - .frets = { 0, 3, 2, 1, 1, 0, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 4, 3, 1, 2, 0, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G#aug", - .base_fret = 1, - .frets = { 0, 3, 2, 1, 1, 0, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 4, 3, 1, 2, 0, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G#dim", - .base_fret = 7, - .frets = { -1, -1, 3, 1, 3, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 3, 1, 4, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G#0", - .base_fret = 1, - .frets = { -1, -1, 0, 1, 0, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 0, 1, 0, 2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G#dim", - .base_fret = 1, - .frets = { -1, -1, 0, 1, 0, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 0, 1, 0, 2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G#dim7", - .base_fret = 1, - .frets = { -1, -1, 0, 1, 0, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 0, 1, 0, 2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G#7", - .base_fret = 4, - .frets = { 1, 3, 1, 2, 1, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 1, 2, 1, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G#maj7", - .base_fret = 1, - .frets = { -1, -1, 1, 1, 1, 3, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 1, 1, 3, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G#m7", - .base_fret = 4, - .frets = { 1, 3, 1, 1, 1, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 1, 1, 1, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ab", - .base_fret = 4, - .frets = { 1, 3, 3, 2, 1, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 4, 2, 1, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Abm", - .base_fret = 4, - .frets = { 1, 3, 3, 1, 1, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 4, 1, 1, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ab+", - .base_fret = 1, - .frets = { 0, 3, 2, 1, 1, 0, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 4, 3, 1, 2, 0, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Abaug", - .base_fret = 1, - .frets = { 0, 3, 2, 1, 1, 0, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 4, 3, 1, 2, 0, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Abdim", - .base_fret = 7, - .frets = { -1, -1, 3, 1, 3, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 3, 1, 4, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ab0", - .base_fret = 1, - .frets = { -1, -1, 0, 1, 0, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 0, 1, 0, 2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Abdim", - .base_fret = 1, - .frets = { -1, -1, 0, 1, 0, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 0, 1, 0, 2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Abdim7", - .base_fret = 1, - .frets = { -1, -1, 0, 1, 0, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 0, 1, 0, 2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ab7", - .base_fret = 4, - .frets = { 1, 3, 1, 2, 1, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 1, 2, 1, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Abmaj7", - .base_fret = 1, - .frets = { -1, -1, 1, 1, 1, 3, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 1, 1, 3, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Abm7", - .base_fret = 4, - .frets = { 1, 3, 1, 1, 1, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 1, 1, 1, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A", - .base_fret = 1, - .frets = { -1, 0, 2, 2, 2, 0, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 2, 3, 0, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Am", - .base_fret = 1, - .frets = { -1, 0, 2, 2, 1, 0, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 2, 3, 1, 0, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A+", - .base_fret = 1, - .frets = { -1, 0, 3, 2, 2, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 4, 2, 3, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Aaug", - .base_fret = 1, - .frets = { -1, 0, 3, 2, 2, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 4, 2, 3, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Adim", - .base_fret = 1, - .frets = { -1, 0, 1, 2, 1, -1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 2, 3, 0, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A0", - .base_fret = 1, - .frets = { -1, -1, 1, 2, 1, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 3, 2, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Adim", - .base_fret = 1, - .frets = { -1, -1, 1, 2, 1, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 3, 2, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Adim7", - .base_fret = 1, - .frets = { -1, -1, 1, 2, 1, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 3, 2, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A7", - .base_fret = 1, - .frets = { -1, 0, 2, 0, 2, 0, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 0, 3, 0, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Amaj7", - .base_fret = 1, - .frets = { -1, 0, 2, 1, 2, 0, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 2, 1, 3, 0, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Am7", - .base_fret = 1, - .frets = { -1, 0, 2, 0, 1, 0, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 2, 0, 1, 0, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A#", - .base_fret = 1, - .frets = { 1, 1, 3, 3, 3, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 2, 3, 4, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A#m", - .base_fret = 1, - .frets = { 1, 1, 3, 3, 2, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 3, 4, 2, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A#+", - .base_fret = 1, - .frets = { 2, 1, 0, 3, 3, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 0, 4, 4, 3, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A#aug", - .base_fret = 1, - .frets = { 2, 1, 0, 3, 3, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 0, 4, 4, 3, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A#dim", - .base_fret = 1, - .frets = { -1, 1, 2, 3, 2, -1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 1, 2, 4, 3, 0, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A#0", - .base_fret = 2, - .frets = { -1, -1, 1, 2, 1, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 3, 2, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A#dim", - .base_fret = 2, - .frets = { -1, -1, 1, 2, 1, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 3, 2, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A#dim7", - .base_fret = 2, - .frets = { -1, -1, 1, 2, 1, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 3, 2, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A#7", - .base_fret = 1, - .frets = { -1, 1, 3, 1, 3, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 1, 2, 1, 3, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A#maj7", - .base_fret = 1, - .frets = { -1, 1, 3, 2, 3, -1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 1, 3, 2, 4, 0, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A#m7", - .base_fret = 1, - .frets = { 1, 1, 3, 1, 2, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 3, 1, 2, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bb", - .base_fret = 1, - .frets = { 1, 1, 3, 3, 3, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 2, 3, 4, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bbm", - .base_fret = 1, - .frets = { 1, 1, 3, 3, 2, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 3, 4, 2, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bb+", - .base_fret = 1, - .frets = { 2, 1, 0, 3, 3, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 0, 4, 4, 3, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bbaug", - .base_fret = 1, - .frets = { 2, 1, 0, 3, 3, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 0, 4, 4, 3, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bbdim", - .base_fret = 1, - .frets = { -1, 1, 2, 3, 2, -1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 1, 2, 4, 3, 0, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bb0", - .base_fret = 2, - .frets = { -1, -1, 1, 2, 1, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 3, 2, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bbdim", - .base_fret = 2, - .frets = { -1, -1, 1, 2, 1, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 3, 2, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bbdim7", - .base_fret = 2, - .frets = { -1, -1, 1, 2, 1, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 3, 2, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bb7", - .base_fret = 1, - .frets = { -1, 1, 3, 1, 3, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 1, 2, 1, 3, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bbmaj7", - .base_fret = 1, - .frets = { -1, 1, 3, 2, 3, -1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 1, 3, 2, 4, 0, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bbm7", - .base_fret = 1, - .frets = { 1, 1, 3, 1, 2, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 3, 1, 2, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "B", - .base_fret = 2, - .frets = { 1, 1, 3, 3, 3, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 2, 3, 4, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bm", - .base_fret = 2, - .frets = { 1, 1, 3, 3, 2, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 3, 4, 2, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "B+", - .base_fret = 1, - .frets = { -1, 3, 2, 0, 0, -1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 2, 1, 0, 0, 0, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Baug", - .base_fret = 1, - .frets = { -1, 3, 2, 0, 0, -1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 2, 1, 0, 0, 0, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bdim", - .base_fret = 2, - .frets = { -1, 1, 2, 3, 2, -1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 1, 2, 4, 3, 0, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "B0", - .base_fret = 1, - .frets = { -1, -1, 0, 1, 0, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 0, 1, 0, 2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bdim", - .base_fret = 1, - .frets = { -1, -1, 0, 1, 0, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 0, 1, 0, 2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bdim7", - .base_fret = 1, - .frets = { -1, -1, 0, 1, 0, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 0, 1, 0, 2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "B7", - .base_fret = 1, - .frets = { -1, 2, 1, 2, 0, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 2, 1, 3, 0, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bmaj7", - .base_fret = 2, - .frets = { -1, 1, 3, 2, 3, -1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 1, 3, 2, 4, 0, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bm7", - .base_fret = 2, - .frets = { 1, 1, 3, 1, 2, 1, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 3, 1, 2, 1, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C9", - .base_fret = 2, - .frets = { -1, 2, 1, 2, 2, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 2, 1, 3, 3, 3, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C#9", - .base_fret = 3, - .frets = { -1, 2, 1, 2, 2, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 2, 1, 3, 3, 3, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Db9", - .base_fret = 3, - .frets = { -1, 2, 1, 2, 2, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 2, 1, 3, 3, 3, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D9", - .base_fret = 4, - .frets = { -1, 2, 1, 2, 2, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 2, 1, 3, 3, 3, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D#9", - .base_fret = 5, - .frets = { -1, 2, 1, 2, 2, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 2, 1, 3, 3, 3, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Eb9", - .base_fret = 5, - .frets = { -1, 2, 1, 2, 2, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 2, 1, 3, 3, 3, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "E9", - .base_fret = 1, - .frets = { 0, 2, 0, 1, 0, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 2, 0, 1, 0, 3, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F9", - .base_fret = 1, - .frets = { 1, 3, 1, 2, 1, 3, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 1, 2, 1, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F#9", - .base_fret = 2, - .frets = { 1, 3, 1, 2, 1, 3, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 1, 2, 1, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gb9", - .base_fret = 2, - .frets = { 1, 3, 1, 2, 1, 3, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 1, 2, 1, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G9", - .base_fret = 3, - .frets = { 1, 3, 1, 2, 1, 3, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 1, 2, 1, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G#9", - .base_fret = 4, - .frets = { 1, 3, 1, 2, 1, 3, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 1, 2, 1, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ab9", - .base_fret = 4, - .frets = { 1, 3, 1, 2, 1, 3, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 1, 2, 1, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A9", - .base_fret = 5, - .frets = { 1, 3, 1, 2, 1, 3, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 1, 2, 1, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A#9", - .base_fret = 6, - .frets = { 1, 3, 1, 2, 1, 3, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 1, 2, 1, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bb9", - .base_fret = 6, - .frets = { 1, 3, 1, 2, 1, 3, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 1, 2, 1, 4, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "B9", - .base_fret = 1, - .frets = { -1, 2, 1, 2, 2, 2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 2, 1, 3, 3, 3, -2, -2, -2, -2, -2, -2 } - } -}; - -static const struct StringDiagram ukulele_diagrams[] = { - { - .name = "A", - .base_fret = 1, - .frets = { 2, 1, 0, 0, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 0, 0, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Am", - .base_fret = 1, - .frets = { 2, 0, 0, 0, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 0, 0, 0, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A7", - .base_fret = 1, - .frets = { 0, 1, 0, 0, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 1, 0, 0, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Am7", - .base_fret = 1, - .frets = { 0, 0, 0, 0, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 0, 0, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Adim", - .base_fret = 2, - .frets = { 1, 2, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 2, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Amaj7", - .base_fret = 1, - .frets = { 1, 1, 0, 0, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 2, 0, 0, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A6", - .base_fret = 2, - .frets = { 1, 3, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 2, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Asus2", - .base_fret = 2, - .frets = { 1, 3, 4, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 3, 4, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Asus", - .base_fret = 1, - .frets = { 2, 2, 0, 0, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 2, 0, 0, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Asus4", - .base_fret = 1, - .frets = { 2, 2, 0, 0, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 2, 0, 0, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A+", - .base_fret = 1, - .frets = { 2, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Aaug", - .base_fret = 1, - .frets = { 2, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A9", - .base_fret = 1, - .frets = { 0, 1, 0, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 1, 0, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A#", - .base_fret = 1, - .frets = { 3, 2, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 2, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A#m", - .base_fret = 1, - .frets = { 3, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A#7", - .base_fret = 1, - .frets = { 1, 2, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 2, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A#m7", - .base_fret = 1, - .frets = { 1, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A#dim", - .base_fret = 1, - .frets = { 0, 1, 0, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 1, 0, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A#maj7", - .base_fret = 1, - .frets = { 2, 2, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 2, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A#6", - .base_fret = 1, - .frets = { 0, 2, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 2, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A#sus2", - .base_fret = 1, - .frets = { 3, 0, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 0, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A#sus", - .base_fret = 1, - .frets = { 3, 3, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 3, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A#sus4", - .base_fret = 1, - .frets = { 3, 3, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 3, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A#+", - .base_fret = 1, - .frets = { 3, 1, 1, 5, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A#aug", - .base_fret = 1, - .frets = { 3, 1, 1, 5, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A#9", - .base_fret = 1, - .frets = { 1, 2, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 2, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bb", - .base_fret = 1, - .frets = { 3, 2, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 2, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bbm", - .base_fret = 1, - .frets = { 3, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bb7", - .base_fret = 1, - .frets = { 1, 2, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 2, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bbm7", - .base_fret = 1, - .frets = { 1, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bbdim", - .base_fret = 1, - .frets = { 0, 1, 0, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 1, 0, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bbmaj7", - .base_fret = 1, - .frets = { 2, 2, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 2, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bb6", - .base_fret = 1, - .frets = { 0, 2, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 2, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bbsus2", - .base_fret = 1, - .frets = { 3, 0, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 0, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bbsus", - .base_fret = 1, - .frets = { 3, 3, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 3, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bbsus4", - .base_fret = 1, - .frets = { 3, 3, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 3, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bb+", - .base_fret = 1, - .frets = { 3, 1, 1, 5, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bbaug", - .base_fret = 1, - .frets = { 3, 1, 1, 5, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bb9", - .base_fret = 1, - .frets = { 1, 2, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 2, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "B", - .base_fret = 2, - .frets = { 3, 2, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 2, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bm", - .base_fret = 2, - .frets = { 3, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "B7", - .base_fret = 2, - .frets = { 1, 2, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 2, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bm7", - .base_fret = 2, - .frets = { 1, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bdim", - .base_fret = 1, - .frets = { 1, 2, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 2, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bmaj7", - .base_fret = 2, - .frets = { 2, 2, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 2, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "B6", - .base_fret = 1, - .frets = { 1, 3, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 4, 2, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bsus2", - .base_fret = 1, - .frets = { 5, 1, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 4, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bsus", - .base_fret = 2, - .frets = { 3, 3, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 2, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bsus4", - .base_fret = 2, - .frets = { 3, 3, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 2, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "B+", - .base_fret = 1, - .frets = { 0, 3, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 2, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Baug", - .base_fret = 1, - .frets = { 0, 3, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 2, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "B9", - .base_fret = 2, - .frets = { 1, 2, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 3, 2, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C", - .base_fret = 1, - .frets = { 0, 0, 0, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 0, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Cm", - .base_fret = 1, - .frets = { 0, 3, 3, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 1, 2, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C7", - .base_fret = 1, - .frets = { 0, 0, 0, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 0, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Cm7", - .base_fret = 3, - .frets = { 1, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Cdim", - .base_fret = 2, - .frets = { 1, 2, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 2, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Cmaj7", - .base_fret = 1, - .frets = { 0, 0, 0, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 0, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C6", - .base_fret = 1, - .frets = { 0, 0, 0, 0, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 0, 0, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Csus2", - .base_fret = 1, - .frets = { 0, 2, 3, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 1, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Csus", - .base_fret = 1, - .frets = { 0, 0, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Csus4", - .base_fret = 1, - .frets = { 0, 0, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C+", - .base_fret = 1, - .frets = { 1, 0, 0, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 0, 0, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Caug", - .base_fret = 1, - .frets = { 1, 0, 0, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 0, 0, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C9", - .base_fret = 1, - .frets = { 0, 2, 0, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 2, 0, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C#", - .base_fret = 1, - .frets = { 1, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C#m", - .base_fret = 1, - .frets = { 1, 4, 4, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 2, 3, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C#7", - .base_fret = 1, - .frets = { 1, 1, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C#m7", - .base_fret = 1, - .frets = { 2, 2, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 2, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C#dim", - .base_fret = 1, - .frets = { 0, 1, 0, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 1, 0, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C#maj7", - .base_fret = 1, - .frets = { 1, 1, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C#6", - .base_fret = 1, - .frets = { 1, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C#sus2", - .base_fret = 1, - .frets = { 1, 3, 4, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 2, 3, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C#sus", - .base_fret = 1, - .frets = { 1, 1, 2, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 2, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C#sus4", - .base_fret = 1, - .frets = { 1, 1, 2, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 2, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C#+", - .base_fret = 1, - .frets = { 2, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C#aug", - .base_fret = 1, - .frets = { 2, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C#9", - .base_fret = 1, - .frets = { 1, 3, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Db", - .base_fret = 1, - .frets = { 1, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Dbm", - .base_fret = 1, - .frets = { 1, 4, 4, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 2, 3, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Db7", - .base_fret = 1, - .frets = { 1, 1, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Dbm7", - .base_fret = 1, - .frets = { 2, 2, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 2, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Dbdim", - .base_fret = 1, - .frets = { 0, 1, 0, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 1, 0, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Dbmaj7", - .base_fret = 1, - .frets = { 1, 1, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Db6", - .base_fret = 1, - .frets = { 1, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Dbsus2", - .base_fret = 1, - .frets = { 1, 3, 4, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 2, 3, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Dbsus", - .base_fret = 1, - .frets = { 1, 1, 2, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 2, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Dbsus4", - .base_fret = 1, - .frets = { 1, 1, 2, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 2, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Db+", - .base_fret = 1, - .frets = { 2, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Dbaug", - .base_fret = 1, - .frets = { 2, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Db9", - .base_fret = 1, - .frets = { 1, 3, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D", - .base_fret = 1, - .frets = { 2, 2, 2, 0, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 2, 3, 0, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Dm", - .base_fret = 1, - .frets = { 2, 2, 1, 0, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 2, 1, 0, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D7", - .base_fret = 2, - .frets = { 1, 1, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Dm7", - .base_fret = 1, - .frets = { 2, 2, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 2, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ddim", - .base_fret = 1, - .frets = { 1, 2, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 2, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Dmaj7", - .base_fret = 2, - .frets = { 1, 1, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D6", - .base_fret = 2, - .frets = { 1, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Dsus2", - .base_fret = 1, - .frets = { 2, 2, 0, 0, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 2, 0, 0, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Dsus", - .base_fret = 1, - .frets = { 0, 2, 3, 0, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 1, 2, 0, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Dsus4", - .base_fret = 1, - .frets = { 0, 2, 3, 0, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 1, 2, 0, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D+", - .base_fret = 2, - .frets = { 2, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Daug", - .base_fret = 2, - .frets = { 2, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D9", - .base_fret = 2, - .frets = { 1, 3, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D#", - .base_fret = 1, - .frets = { 0, 3, 3, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 2, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D#m", - .base_fret = 1, - .frets = { 3, 3, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 3, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D#7", - .base_fret = 3, - .frets = { 1, 1, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D#m7", - .base_fret = 2, - .frets = { 2, 2, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 2, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D#dim", - .base_fret = 2, - .frets = { 1, 2, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D#maj7", - .base_fret = 3, - .frets = { 1, 1, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 2, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D#6", - .base_fret = 3, - .frets = { 1, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D#sus2", - .base_fret = 1, - .frets = { 3, 3, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 2, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D#sus", - .base_fret = 1, - .frets = { 1, 3, 4, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 3, 4, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D#sus4", - .base_fret = 1, - .frets = { 1, 3, 4, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 3, 4, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D#+", - .base_fret = 1, - .frets = { 0, 3, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 2, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D#aug", - .base_fret = 1, - .frets = { 0, 3, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 2, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D#9", - .base_fret = 1, - .frets = { 0, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Eb", - .base_fret = 1, - .frets = { 0, 3, 3, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 2, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ebm", - .base_fret = 1, - .frets = { 3, 3, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 3, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Eb7", - .base_fret = 3, - .frets = { 1, 1, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ebm7", - .base_fret = 2, - .frets = { 2, 2, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 2, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ebdim", - .base_fret = 2, - .frets = { 1, 2, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ebmaj7", - .base_fret = 3, - .frets = { 1, 1, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 2, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Eb6", - .base_fret = 3, - .frets = { 1, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ebsus2", - .base_fret = 1, - .frets = { 3, 3, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 2, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ebsus", - .base_fret = 1, - .frets = { 1, 3, 4, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 3, 4, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ebsus4", - .base_fret = 1, - .frets = { 1, 3, 4, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 3, 4, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Eb+", - .base_fret = 1, - .frets = { 0, 3, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 2, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ebaug", - .base_fret = 1, - .frets = { 0, 3, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 2, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Eb9", - .base_fret = 1, - .frets = { 0, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "E", - .base_fret = 2, - .frets = { 3, 3, 3, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 3, 4, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Em", - .base_fret = 2, - .frets = { 3, 3, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 3, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "E7", - .base_fret = 1, - .frets = { 1, 2, 0, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 2, 0, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Em7", - .base_fret = 1, - .frets = { 0, 2, 0, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 1, 0, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Edim", - .base_fret = 1, - .frets = { 0, 1, 0, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 1, 0, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Emaj7", - .base_fret = 1, - .frets = { 1, 3, 0, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 0, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "E6", - .base_fret = 4, - .frets = { 1, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Esus2", - .base_fret = 2, - .frets = { 3, 3, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 3, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Esus", - .base_fret = 1, - .frets = { 2, 4, 0, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 4, 0, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Esus4", - .base_fret = 1, - .frets = { 2, 4, 0, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 4, 0, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "E+", - .base_fret = 1, - .frets = { 1, 0, 0, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 0, 0, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Eaug", - .base_fret = 1, - .frets = { 1, 0, 0, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 0, 0, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "E9", - .base_fret = 1, - .frets = { 1, 2, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 2, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F", - .base_fret = 1, - .frets = { 2, 0, 1, 0, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 0, 1, 0, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Fm", - .base_fret = 1, - .frets = { 1, 0, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 0, 2, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F7", - .base_fret = 1, - .frets = { 2, 3, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 3, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Fm7", - .base_fret = 1, - .frets = { 1, 3, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 2, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Fdim", - .base_fret = 1, - .frets = { 1, 2, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 2, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Fmaj7", - .base_fret = 1, - .frets = { 2, 4, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 4, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F6", - .base_fret = 1, - .frets = { 2, 2, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 2, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Fsus2", - .base_fret = 1, - .frets = { 0, 0, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Fsus", - .base_fret = 1, - .frets = { 3, 0, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 0, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Fsus4", - .base_fret = 1, - .frets = { 3, 0, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 0, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F+", - .base_fret = 1, - .frets = { 2, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Faug", - .base_fret = 1, - .frets = { 2, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F9", - .base_fret = 2, - .frets = { 1, 2, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 2, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F#", - .base_fret = 1, - .frets = { 3, 1, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 1, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F#m", - .base_fret = 1, - .frets = { 2, 1, 2, 0, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 3, 0, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F#7", - .base_fret = 1, - .frets = { 3, 4, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 4, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F#m7", - .base_fret = 2, - .frets = { 1, 3, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 2, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F#dim", - .base_fret = 2, - .frets = { 1, 2, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 2, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F#maj7", - .base_fret = 2, - .frets = { 2, 4, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 4, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F#6", - .base_fret = 2, - .frets = { 2, 2, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 2, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F#sus2", - .base_fret = 1, - .frets = { 1, 1, 2, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 2, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F#sus", - .base_fret = 1, - .frets = { 4, 1, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 4, 1, 2, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F#sus4", - .base_fret = 1, - .frets = { 4, 1, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 4, 1, 2, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F#+", - .base_fret = 2, - .frets = { 2, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F#aug", - .base_fret = 2, - .frets = { 2, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F#9", - .base_fret = 3, - .frets = { 1, 2, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 2, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gb", - .base_fret = 1, - .frets = { 3, 1, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 1, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gbm", - .base_fret = 1, - .frets = { 2, 1, 2, 0, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 3, 0, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gb7", - .base_fret = 1, - .frets = { 3, 4, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 4, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gbm7", - .base_fret = 2, - .frets = { 1, 3, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 2, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gbdim", - .base_fret = 2, - .frets = { 1, 2, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 2, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gbmaj7", - .base_fret = 2, - .frets = { 2, 4, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 4, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gb6", - .base_fret = 2, - .frets = { 2, 2, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 2, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gbsus2", - .base_fret = 1, - .frets = { 1, 1, 2, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 2, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gbsus", - .base_fret = 1, - .frets = { 4, 1, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 4, 1, 2, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gbsus4", - .base_fret = 1, - .frets = { 4, 1, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 4, 1, 2, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gb+", - .base_fret = 2, - .frets = { 2, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gbaug", - .base_fret = 2, - .frets = { 2, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gb9", - .base_fret = 3, - .frets = { 1, 2, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 2, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G", - .base_fret = 1, - .frets = { 0, 2, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gm", - .base_fret = 1, - .frets = { 0, 2, 3, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 2, 3, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G7", - .base_fret = 1, - .frets = { 0, 2, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 2, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gm7", - .base_fret = 1, - .frets = { 0, 2, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 2, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gdim", - .base_fret = 1, - .frets = { 0, 1, 0, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 1, 0, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gmaj7", - .base_fret = 1, - .frets = { 0, 2, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 1, 2, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G6", - .base_fret = 1, - .frets = { 0, 2, 0, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 1, 0, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gsus2", - .base_fret = 1, - .frets = { 0, 2, 3, 0, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 1, 2, 0, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gsus", - .base_fret = 1, - .frets = { 0, 2, 3, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 1, 2, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gsus4", - .base_fret = 1, - .frets = { 0, 2, 3, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 1, 2, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G+", - .base_fret = 1, - .frets = { 0, 3, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 2, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gaug", - .base_fret = 1, - .frets = { 0, 3, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 2, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G9", - .base_fret = 1, - .frets = { 2, 2, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 3, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G#", - .base_fret = 3, - .frets = { 3, 1, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 1, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G#m", - .base_fret = 1, - .frets = { 1, 3, 4, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 4, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G#7", - .base_fret = 1, - .frets = { 1, 3, 2, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 2, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G#m7", - .base_fret = 1, - .frets = { 1, 3, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 4, 2, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G#dim", - .base_fret = 1, - .frets = { 1, 2, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 2, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G#maj7", - .base_fret = 1, - .frets = { 1, 3, 3, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 2, 3, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G#6", - .base_fret = 1, - .frets = { 1, 3, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 2, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G#sus2", - .base_fret = 1, - .frets = { 1, 3, 4, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 3, 4, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G#sus", - .base_fret = 1, - .frets = { 1, 2, 4, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 3, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G#sus4", - .base_fret = 1, - .frets = { 1, 2, 4, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 3, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G#+", - .base_fret = 1, - .frets = { 1, 0, 0, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 0, 0, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G#aug", - .base_fret = 1, - .frets = { 1, 0, 0, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 0, 0, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G#9", - .base_fret = 1, - .frets = { 1, 0, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 0, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ab", - .base_fret = 3, - .frets = { 3, 1, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 1, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Abm", - .base_fret = 1, - .frets = { 1, 3, 4, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 4, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ab7", - .base_fret = 1, - .frets = { 1, 3, 2, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 2, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Abm7", - .base_fret = 1, - .frets = { 1, 3, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 4, 2, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Abdim", - .base_fret = 1, - .frets = { 1, 2, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 2, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Abmaj7", - .base_fret = 1, - .frets = { 1, 3, 3, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 2, 3, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ab6", - .base_fret = 1, - .frets = { 1, 3, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 2, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Absus2", - .base_fret = 1, - .frets = { 1, 3, 4, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 3, 4, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Absus", - .base_fret = 1, - .frets = { 1, 2, 4, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 3, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Absus4", - .base_fret = 1, - .frets = { 1, 2, 4, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 3, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ab+", - .base_fret = 1, - .frets = { 1, 0, 0, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 0, 0, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Abaug", - .base_fret = 1, - .frets = { 1, 0, 0, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 0, 0, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ab9", - .base_fret = 1, - .frets = { 1, 0, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 0, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - } -}; - -static const struct StringDiagram mandolin_diagrams[] = { - { - .name = "A", - .base_fret = 1, - .frets = { 2, 2, 4, 0, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 3, 0, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Am", - .base_fret = 1, - .frets = { 2, 2, 3, 0, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 2, 0, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A7", - .base_fret = 2, - .frets = { 1, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Am7", - .base_fret = 2, - .frets = { 1, 1, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Aø", - .base_fret = 1, - .frets = { 2, 1, 3, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Am7b5", - .base_fret = 1, - .frets = { 2, 1, 3, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ah", - .base_fret = 1, - .frets = { 2, 1, 3, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A0", - .base_fret = 1, - .frets = { 2, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 4, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Adim", - .base_fret = 1, - .frets = { 2, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 4, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Adim7", - .base_fret = 1, - .frets = { 2, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 4, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Amaj7", - .base_fret = 2, - .frets = { 1, 1, 3, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 3, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A6", - .base_fret = 2, - .frets = { 1, 1, 3, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 3, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Asus2", - .base_fret = 1, - .frets = { 2, 2, 2, 0, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 1, 0, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Asus", - .base_fret = 1, - .frets = { 2, 0, 0, 0, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 0, 0, 0, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Asus4", - .base_fret = 1, - .frets = { 2, 0, 0, 0, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 0, 0, 0, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A+", - .base_fret = 1, - .frets = { 2, 3, 4, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 3, 4, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Aaug", - .base_fret = 1, - .frets = { 2, 3, 4, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 3, 4, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A9", - .base_fret = 2, - .frets = { 1, 4, 3, 6, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 2, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A#", - .base_fret = 1, - .frets = { 3, 0, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 0, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A#m", - .base_fret = 3, - .frets = { 1, 1, 2, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 2, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A#7", - .base_fret = 3, - .frets = { 1, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A#m7", - .base_fret = 3, - .frets = { 1, 1, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A#ø", - .base_fret = 2, - .frets = { 2, 1, 3, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A#m7b5", - .base_fret = 2, - .frets = { 2, 1, 3, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A#h", - .base_fret = 2, - .frets = { 2, 1, 3, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A#0", - .base_fret = 2, - .frets = { 2, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 4, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A#dim", - .base_fret = 2, - .frets = { 2, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 4, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A#dim7", - .base_fret = 2, - .frets = { 2, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 4, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A#maj7", - .base_fret = 1, - .frets = { 3, 0, 0, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 0, 0, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A#6", - .base_fret = 1, - .frets = { 0, 0, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A#sus2", - .base_fret = 3, - .frets = { 1, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A#sus", - .base_fret = 1, - .frets = { 3, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A#sus4", - .base_fret = 1, - .frets = { 3, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A#+", - .base_fret = 1, - .frets = { 3, 0, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 0, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A#aug", - .base_fret = 1, - .frets = { 3, 0, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 0, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "A#9", - .base_fret = 1, - .frets = { 3, 0, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 0, 2, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bb", - .base_fret = 1, - .frets = { 3, 0, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 0, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bbm", - .base_fret = 3, - .frets = { 1, 1, 2, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 2, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bb7", - .base_fret = 3, - .frets = { 1, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bbm7", - .base_fret = 3, - .frets = { 1, 1, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bbø", - .base_fret = 2, - .frets = { 2, 1, 3, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bbm7b5", - .base_fret = 2, - .frets = { 2, 1, 3, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bbh", - .base_fret = 2, - .frets = { 2, 1, 3, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bb0", - .base_fret = 2, - .frets = { 2, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 4, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bbdim", - .base_fret = 2, - .frets = { 2, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 4, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bbdim7", - .base_fret = 2, - .frets = { 2, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 4, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bbmaj7", - .base_fret = 1, - .frets = { 3, 0, 0, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 0, 0, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bb6", - .base_fret = 1, - .frets = { 0, 0, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bbsus2", - .base_fret = 3, - .frets = { 1, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bbsus", - .base_fret = 1, - .frets = { 3, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bbsus4", - .base_fret = 1, - .frets = { 3, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bb+", - .base_fret = 1, - .frets = { 3, 0, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 0, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bbaug", - .base_fret = 1, - .frets = { 3, 0, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 0, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bb9", - .base_fret = 1, - .frets = { 3, 0, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 0, 2, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "B", - .base_fret = 4, - .frets = { 1, 1, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bm", - .base_fret = 1, - .frets = { 4, 0, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 0, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "B7", - .base_fret = 4, - .frets = { 1, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bm7", - .base_fret = 1, - .frets = { 4, 0, 0, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 0, 0, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bø", - .base_fret = 1, - .frets = { 2, 0, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 0, 3, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bm7b5", - .base_fret = 1, - .frets = { 2, 0, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 0, 3, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bh", - .base_fret = 1, - .frets = { 2, 0, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 0, 3, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "B0", - .base_fret = 1, - .frets = { 1, 0, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 0, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bdim", - .base_fret = 1, - .frets = { 1, 0, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 0, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bdim7", - .base_fret = 1, - .frets = { 1, 0, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 0, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bmaj7", - .base_fret = 1, - .frets = { 4, 1, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 4, 1, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "B6", - .base_fret = 1, - .frets = { 1, 1, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bsus2", - .base_fret = 4, - .frets = { 1, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bsus", - .base_fret = 2, - .frets = { 3, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Bsus4", - .base_fret = 2, - .frets = { 3, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "B+", - .base_fret = 1, - .frets = { 4, 1, 2, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 4, 1, 2, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Baug", - .base_fret = 1, - .frets = { 4, 1, 2, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 4, 1, 2, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "B9", - .base_fret = 1, - .frets = { 4, 1, 4, 5, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C", - .base_fret = 1, - .frets = { 5, 2, 3, 0, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 4, 1, 2, 0, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Cm", - .base_fret = 5, - .frets = { 1, 1, 2, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 2, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C7", - .base_fret = 1, - .frets = { 5, 2, 1, 0, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 4, 2, 1, 0, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Cm7", - .base_fret = 5, - .frets = { 1, 1, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Cø", - .base_fret = 1, - .frets = { 3, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 1, 4, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Cm7b5", - .base_fret = 1, - .frets = { 3, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 1, 4, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ch", - .base_fret = 1, - .frets = { 3, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 1, 4, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C0", - .base_fret = 1, - .frets = { 2, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 4, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Cdim", - .base_fret = 1, - .frets = { 2, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 4, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Cdim7", - .base_fret = 1, - .frets = { 2, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 4, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Cmaj7", - .base_fret = 2, - .frets = { 4, 1, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 4, 1, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C6", - .base_fret = 2, - .frets = { 1, 1, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Csus2", - .base_fret = 1, - .frets = { 5, 0, 3, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 0, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Csus", - .base_fret = 3, - .frets = { 3, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Csus4", - .base_fret = 3, - .frets = { 3, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C+", - .base_fret = 2, - .frets = { 4, 1, 2, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 4, 1, 2, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Caug", - .base_fret = 2, - .frets = { 4, 1, 2, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 4, 1, 2, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C9", - .base_fret = 1, - .frets = { 5, 0, 7, 6, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 0, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C#", - .base_fret = 1, - .frets = { 6, 3, 4, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 4, 2, 3, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C#m", - .base_fret = 1, - .frets = { 6, 6, 4, 0, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 3, 1, 0, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C#7", - .base_fret = 2, - .frets = { 5, 2, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 4, 2, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C#m7", - .base_fret = 6, - .frets = { 1, 1, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C#ø", - .base_fret = 2, - .frets = { 3, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 1, 4, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C#m7b5", - .base_fret = 2, - .frets = { 3, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 1, 4, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C#h", - .base_fret = 2, - .frets = { 3, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 1, 4, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C#0", - .base_fret = 1, - .frets = { 3, 2, 1, 0, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 1, 0, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C#dim", - .base_fret = 1, - .frets = { 3, 2, 1, 0, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 1, 0, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C#dim7", - .base_fret = 1, - .frets = { 3, 2, 1, 0, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 1, 0, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C#maj7", - .base_fret = 3, - .frets = { 4, 1, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 4, 1, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C#6", - .base_fret = 3, - .frets = { 1, 1, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C#sus2", - .base_fret = 1, - .frets = { 1, 1, 4, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C#sus", - .base_fret = 4, - .frets = { 3, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C#sus4", - .base_fret = 4, - .frets = { 3, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C#+", - .base_fret = 1, - .frets = { 6, 3, 0, 5, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 4, 1, 0, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C#aug", - .base_fret = 1, - .frets = { 6, 3, 0, 5, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 4, 1, 0, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "C#9", - .base_fret = 3, - .frets = { 4, 1, 4, 5, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Db", - .base_fret = 1, - .frets = { 6, 3, 4, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 4, 2, 3, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Dbm", - .base_fret = 1, - .frets = { 6, 6, 4, 0, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 3, 1, 0, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Db7", - .base_fret = 2, - .frets = { 5, 2, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 4, 2, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Dbm7", - .base_fret = 6, - .frets = { 1, 1, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Dbø", - .base_fret = 2, - .frets = { 3, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 1, 4, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Dbm7b5", - .base_fret = 2, - .frets = { 3, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 1, 4, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Dbh", - .base_fret = 2, - .frets = { 3, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 1, 4, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Db0", - .base_fret = 1, - .frets = { 3, 2, 1, 0, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 1, 0, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Dbdim", - .base_fret = 1, - .frets = { 3, 2, 1, 0, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 1, 0, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Dbdim7", - .base_fret = 1, - .frets = { 3, 2, 1, 0, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 1, 0, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Dbmaj7", - .base_fret = 3, - .frets = { 4, 1, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 4, 1, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Db6", - .base_fret = 3, - .frets = { 1, 1, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Dbsus2", - .base_fret = 1, - .frets = { 1, 1, 4, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Dbsus", - .base_fret = 4, - .frets = { 3, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Dbsus4", - .base_fret = 4, - .frets = { 3, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Db+", - .base_fret = 1, - .frets = { 6, 3, 0, 5, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 4, 1, 0, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Dbaug", - .base_fret = 1, - .frets = { 6, 3, 0, 5, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 4, 1, 0, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Db9", - .base_fret = 3, - .frets = { 4, 1, 4, 5, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D", - .base_fret = 1, - .frets = { 2, 0, 0, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 0, 0, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Dm", - .base_fret = 1, - .frets = { 2, 0, 0, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 0, 0, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D7", - .base_fret = 1, - .frets = { 2, 0, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 0, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Dm7", - .base_fret = 1, - .frets = { 2, 0, 3, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 0, 3, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Dø", - .base_fret = 1, - .frets = { 1, 0, 3, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 0, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Dm7b5", - .base_fret = 1, - .frets = { 1, 0, 3, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 0, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Dh", - .base_fret = 1, - .frets = { 1, 0, 3, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 0, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D0", - .base_fret = 1, - .frets = { 1, 0, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 0, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ddim", - .base_fret = 1, - .frets = { 1, 0, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 0, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ddim7", - .base_fret = 1, - .frets = { 1, 0, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 0, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Dmaj7", - .base_fret = 1, - .frets = { 2, 0, 4, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 0, 4, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D6", - .base_fret = 1, - .frets = { 2, 0, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 0, 2, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Dsus2", - .base_fret = 1, - .frets = { 2, 0, 0, 0, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 0, 0, 0, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Dsus", - .base_fret = 1, - .frets = { 2, 0, 0, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 0, 0, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Dsus4", - .base_fret = 1, - .frets = { 2, 0, 0, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 0, 0, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D+", - .base_fret = 1, - .frets = { 3, 0, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 0, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Daug", - .base_fret = 1, - .frets = { 3, 0, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 0, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D9", - .base_fret = 1, - .frets = { 7, 4, 3, 0, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 4, 2, 1, 0, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D#", - .base_fret = 1, - .frets = { 3, 1, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D#m", - .base_fret = 1, - .frets = { 3, 1, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 1, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D#7", - .base_fret = 1, - .frets = { 3, 1, 4, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 4, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D#m7", - .base_fret = 1, - .frets = { 3, 1, 4, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 1, 4, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D#ø", - .base_fret = 1, - .frets = { 2, 1, 4, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 4, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D#m7b5", - .base_fret = 1, - .frets = { 2, 1, 4, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 4, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D#h", - .base_fret = 1, - .frets = { 2, 1, 4, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 4, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D#0", - .base_fret = 1, - .frets = { 2, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 4, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D#dim", - .base_fret = 1, - .frets = { 2, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 4, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D#dim7", - .base_fret = 1, - .frets = { 2, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 4, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D#maj7", - .base_fret = 1, - .frets = { 3, 1, 5, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 4, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D#6", - .base_fret = 1, - .frets = { 3, 1, 3, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D#sus2", - .base_fret = 1, - .frets = { 3, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D#sus", - .base_fret = 1, - .frets = { 3, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D#sus4", - .base_fret = 1, - .frets = { 3, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D#+", - .base_fret = 1, - .frets = { 0, 1, 2, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 1, 2, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D#aug", - .base_fret = 1, - .frets = { 0, 1, 2, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 1, 2, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "D#9", - .base_fret = 5, - .frets = { 4, 1, 4, 5, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Eb", - .base_fret = 1, - .frets = { 3, 1, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ebm", - .base_fret = 1, - .frets = { 3, 1, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 1, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Eb7", - .base_fret = 1, - .frets = { 3, 1, 4, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 4, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ebm7", - .base_fret = 1, - .frets = { 3, 1, 4, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 1, 4, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ebø", - .base_fret = 1, - .frets = { 2, 1, 4, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 4, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ebm7b5", - .base_fret = 1, - .frets = { 2, 1, 4, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 4, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ebh", - .base_fret = 1, - .frets = { 2, 1, 4, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 4, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Eb0", - .base_fret = 1, - .frets = { 2, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 4, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ebdim", - .base_fret = 1, - .frets = { 2, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 4, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ebdim7", - .base_fret = 1, - .frets = { 2, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 4, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ebmaj7", - .base_fret = 1, - .frets = { 3, 1, 5, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 4, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Eb6", - .base_fret = 1, - .frets = { 3, 1, 3, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ebsus2", - .base_fret = 1, - .frets = { 3, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ebsus", - .base_fret = 1, - .frets = { 3, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ebsus4", - .base_fret = 1, - .frets = { 3, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Eb+", - .base_fret = 1, - .frets = { 0, 1, 2, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 1, 2, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ebaug", - .base_fret = 1, - .frets = { 0, 1, 2, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 1, 2, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Eb9", - .base_fret = 5, - .frets = { 4, 1, 4, 5, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "E", - .base_fret = 1, - .frets = { 1, 2, 2, 0, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 2, 3, 0, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Em", - .base_fret = 1, - .frets = { 0, 2, 2, 0, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 2, 3, 0, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "E7", - .base_fret = 1, - .frets = { 1, 0, 2, 0, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 0, 2, 0, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Em7", - .base_fret = 1, - .frets = { 0, 0, 2, 0, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 2, 0, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Eø", - .base_fret = 1, - .frets = { 0, 0, 1, 0, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 0, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Em7b5", - .base_fret = 1, - .frets = { 0, 0, 1, 0, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 0, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Eh", - .base_fret = 1, - .frets = { 0, 0, 1, 0, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 0, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "E0", - .base_fret = 2, - .frets = { 2, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 4, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Edim", - .base_fret = 2, - .frets = { 2, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 4, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Edim7", - .base_fret = 2, - .frets = { 2, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 4, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Emaj7", - .base_fret = 1, - .frets = { 1, 1, 2, 0, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 2, 0, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "E6", - .base_fret = 1, - .frets = { 4, 6, 4, 0, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 2, 0, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Esus2", - .base_fret = 2, - .frets = { 3, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Esus", - .base_fret = 1, - .frets = { 4, 2, 0, 0, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 1, 0, 0, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Esus4", - .base_fret = 1, - .frets = { 4, 2, 0, 0, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 1, 0, 0, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "E+", - .base_fret = 1, - .frets = { 1, 2, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 2, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Eaug", - .base_fret = 1, - .frets = { 1, 2, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 2, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "E9", - .base_fret = 6, - .frets = { 4, 1, 4, 5, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F", - .base_fret = 1, - .frets = { 2, 3, 0, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 3, 0, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Fm", - .base_fret = 1, - .frets = { 1, 3, 3, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 4, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F7", - .base_fret = 1, - .frets = { 2, 1, 3, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 3, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Fm7", - .base_fret = 1, - .frets = { 1, 1, 3, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 3, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Fø", - .base_fret = 1, - .frets = { 1, 1, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Fm7b5", - .base_fret = 1, - .frets = { 1, 1, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Fh", - .base_fret = 1, - .frets = { 1, 1, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F0", - .base_fret = 1, - .frets = { 1, 0, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 0, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Fdim", - .base_fret = 1, - .frets = { 1, 0, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 0, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Fdim7", - .base_fret = 1, - .frets = { 1, 0, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 0, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Fmaj7", - .base_fret = 1, - .frets = { 2, 2, 3, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 3, 4, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F6", - .base_fret = 1, - .frets = { 2, 0, 3, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 0, 3, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Fsus2", - .base_fret = 1, - .frets = { 0, 3, 3, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 3, 4, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Fsus", - .base_fret = 1, - .frets = { 5, 3, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 4, 2, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Fsus4", - .base_fret = 1, - .frets = { 5, 3, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 4, 2, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F+", - .base_fret = 2, - .frets = { 1, 2, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 2, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Faug", - .base_fret = 2, - .frets = { 1, 2, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 2, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F9", - .base_fret = 7, - .frets = { 4, 1, 4, 5, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F#", - .base_fret = 2, - .frets = { 2, 3, 3, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 3, 4, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F#m", - .base_fret = 2, - .frets = { 1, 3, 3, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 4, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F#7", - .base_fret = 2, - .frets = { 2, 1, 3, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 3, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F#m7", - .base_fret = 2, - .frets = { 1, 1, 3, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 3, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F#ø", - .base_fret = 2, - .frets = { 1, 1, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F#m7b5", - .base_fret = 2, - .frets = { 1, 1, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F#h", - .base_fret = 2, - .frets = { 1, 1, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F#0", - .base_fret = 1, - .frets = { 2, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 4, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F#dim", - .base_fret = 1, - .frets = { 2, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 4, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F#dim7", - .base_fret = 1, - .frets = { 2, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 4, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F#maj7", - .base_fret = 2, - .frets = { 2, 2, 3, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 3, 4, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F#6", - .base_fret = 1, - .frets = { 3, 1, 4, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 1, 4, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F#sus2", - .base_fret = 4, - .frets = { 3, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F#sus", - .base_fret = 2, - .frets = { 5, 3, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 4, 2, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F#sus4", - .base_fret = 2, - .frets = { 5, 3, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 4, 2, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F#+", - .base_fret = 3, - .frets = { 1, 2, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 2, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F#aug", - .base_fret = 3, - .frets = { 1, 2, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 2, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "F#9", - .base_fret = 1, - .frets = { 11, 8, 11, 0, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 3, 0, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gb", - .base_fret = 2, - .frets = { 2, 3, 3, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 3, 4, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gbm", - .base_fret = 2, - .frets = { 1, 3, 3, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 4, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gb7", - .base_fret = 2, - .frets = { 2, 1, 3, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 3, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gbm7", - .base_fret = 2, - .frets = { 1, 1, 3, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 3, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gbø", - .base_fret = 2, - .frets = { 1, 1, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gbm7b5", - .base_fret = 2, - .frets = { 1, 1, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gbh", - .base_fret = 2, - .frets = { 1, 1, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gb0", - .base_fret = 1, - .frets = { 2, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 4, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gbdim", - .base_fret = 1, - .frets = { 2, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 4, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gbdim7", - .base_fret = 1, - .frets = { 2, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 4, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gbmaj7", - .base_fret = 2, - .frets = { 2, 2, 3, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 3, 4, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gb6", - .base_fret = 1, - .frets = { 3, 1, 4, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 1, 4, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gbsus2", - .base_fret = 4, - .frets = { 3, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 3, 1, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gbsus", - .base_fret = 2, - .frets = { 5, 3, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 4, 2, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gbsus4", - .base_fret = 2, - .frets = { 5, 3, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 4, 2, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gb+", - .base_fret = 3, - .frets = { 1, 2, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 2, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gbaug", - .base_fret = 3, - .frets = { 1, 2, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 2, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gb9", - .base_fret = 7, - .frets = { 4, 1, 4, 0, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 3, 0, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G", - .base_fret = 1, - .frets = { 0, 0, 2, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gm", - .base_fret = 1, - .frets = { 0, 0, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G7", - .base_fret = 1, - .frets = { 0, 0, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gm7", - .base_fret = 1, - .frets = { 0, 0, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gø", - .base_fret = 3, - .frets = { 1, 1, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gm7b5", - .base_fret = 3, - .frets = { 1, 1, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gh", - .base_fret = 3, - .frets = { 1, 1, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G0", - .base_fret = 2, - .frets = { 2, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 4, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gdim", - .base_fret = 2, - .frets = { 2, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 4, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gdim7", - .base_fret = 2, - .frets = { 2, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 2, 1, 4, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gmaj7", - .base_fret = 1, - .frets = { 0, 0, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G6", - .base_fret = 1, - .frets = { 0, 0, 2, 0, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 2, 0, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gsus2", - .base_fret = 1, - .frets = { 0, 0, 0, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 0, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gsus", - .base_fret = 1, - .frets = { 0, 0, 3, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gsus4", - .base_fret = 1, - .frets = { 0, 0, 3, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 0, 1, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G+", - .base_fret = 1, - .frets = { 0, 1, 2, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 1, 2, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Gaug", - .base_fret = 1, - .frets = { 0, 1, 2, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 1, 2, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G9", - .base_fret = 1, - .frets = { 0, 3, 0, 7, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 0, 1, 0, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G#", - .base_fret = 1, - .frets = { 1, 1, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G#m", - .base_fret = 1, - .frets = { 1, 1, 2, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 2, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G#7", - .base_fret = 1, - .frets = { 1, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G#m7", - .base_fret = 1, - .frets = { 1, 1, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G#ø", - .base_fret = 1, - .frets = { 1, 0, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 0, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G#m7b5", - .base_fret = 1, - .frets = { 1, 0, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 0, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G#h", - .base_fret = 1, - .frets = { 1, 0, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 0, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G#0", - .base_fret = 1, - .frets = { 1, 0, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 0, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G#dim", - .base_fret = 1, - .frets = { 1, 0, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 0, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G#dim7", - .base_fret = 1, - .frets = { 1, 0, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 0, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G#maj7", - .base_fret = 1, - .frets = { 1, 1, 3, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 3, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G#6", - .base_fret = 1, - .frets = { 1, 1, 3, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 3, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G#sus2", - .base_fret = 1, - .frets = { 1, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G#sus", - .base_fret = 1, - .frets = { 1, 1, 4, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G#sus4", - .base_fret = 1, - .frets = { 1, 1, 4, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G#+", - .base_fret = 1, - .frets = { 1, 2, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 2, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G#aug", - .base_fret = 1, - .frets = { 1, 2, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 2, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "G#9", - .base_fret = 1, - .frets = { 1, 4, 3, 6, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 2, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ab", - .base_fret = 1, - .frets = { 1, 1, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Abm", - .base_fret = 1, - .frets = { 1, 1, 2, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 2, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ab7", - .base_fret = 1, - .frets = { 1, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Abm7", - .base_fret = 1, - .frets = { 1, 1, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Abø", - .base_fret = 1, - .frets = { 1, 0, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 0, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Abm7b5", - .base_fret = 1, - .frets = { 1, 0, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 0, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Abh", - .base_fret = 1, - .frets = { 1, 0, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 0, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ab0", - .base_fret = 1, - .frets = { 1, 0, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 0, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Abdim", - .base_fret = 1, - .frets = { 1, 0, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 0, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Abdim7", - .base_fret = 1, - .frets = { 1, 0, 2, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 0, 3, 2, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Abmaj7", - .base_fret = 1, - .frets = { 1, 1, 3, 3, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 3, 3, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ab6", - .base_fret = 1, - .frets = { 1, 1, 3, 1, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 3, 1, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Absus2", - .base_fret = 1, - .frets = { 1, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 1, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Absus", - .base_fret = 1, - .frets = { 1, 1, 4, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Absus4", - .base_fret = 1, - .frets = { 1, 1, 4, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 1, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ab+", - .base_fret = 1, - .frets = { 1, 2, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 2, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Abaug", - .base_fret = 1, - .frets = { 1, 2, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 2, 3, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - }, - { - .name = "Ab9", - .base_fret = 1, - .frets = { 1, 4, 3, 6, -2, -2, -2, -2, -2, -2, -2, -2 }, - .fingers = { 1, 3, 2, 4, -2, -2, -2, -2, -2, -2, -2, -2 } - } -}; diff --git a/lorid.c b/lorid.c @@ -1,117 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <stdbool.h> -#include <string.h> -#include <getopt.h> -#include "types.h" -#include "chordpro.h" -#include "config.h" -#include "out_pdf.h" -#include "util.h" - -int -main(int argc, char *argv[]) -{ - static struct option long_options[] = { - { "print-default-config", no_argument, 0, 'p' }, - { "config", required_argument, 0, 'c' }, - { "output", required_argument, 0, 'o' }, - { "verbose", no_argument, 0, 'v' }, - { "version", no_argument, 0, 'V' }, - { "help", no_argument, 0, 'h' }, - { 0, 0, 0, 0 } - }; - int o, option_index; - const char *chordpro_filepath = NULL; - char *config_filepath = NULL; - char *output = NULL; - struct ChoSong **so, **all_songs = NULL, **songs = NULL; - int s = 0; - FILE *fp; - while ((o = getopt_long(argc, argv, "pc:o:Vvh", long_options, &option_index)) != -1) { - switch(o) { - case 'p': - config_print_default(); - return 0; - case 'c': - config_filepath = emalloc((strlen(optarg)+1) * sizeof(char)); - strcpy(config_filepath, optarg); - break; - case 'o': - output = erealloc(output, (strlen(optarg)+1) * sizeof(char)); - strcpy(output, optarg); - break; - case 'V': - printf(VERSION"\n"); - return 0; - case 'v': - cho_log_enable_info_logs(); - util_log_enable_info_logs(); - break; - case 'h': - return system("man lorid"); - } - } - struct Config *config = config_load(config_filepath); - if (!config) { - LOG_DEBUG("config_load failed."); - return 1; - } - free(config_filepath); - if (argc == optind) { - fp = stdin; - all_songs = cho_songs_parse(fp, NULL, config); - if (!all_songs) { - LOG_DEBUG("cho_songs_parse failed."); - return 1; - } - } else if (argc == optind+1) { - fp = fopen(argv[argc-1], "r"); - if (!fp) { - LOG_DEBUG("fopen failed."); - return 1; - } - chordpro_filepath = argv[argc-1]; - all_songs = cho_songs_parse(fp, chordpro_filepath, config); - if (!all_songs) { - LOG_DEBUG("cho_songs_parse failed."); - return 1; - } - fclose(fp); - } else { - int file_count = argc - optind; - int i; - for (i = argc-file_count; i<argc; i++) { - fp = fopen(argv[i], "r"); - if (!fp) { - LOG_DEBUG("fopen failed."); - return 1; - } - songs = cho_songs_parse(fp, argv[i], config); - if (!songs) { - LOG_DEBUG("cho_songs_parse failed."); - return 1; - } - fclose(fp); - for (so = songs; *so; s++, so++) { - all_songs = erealloc(all_songs, (s+1) * sizeof(struct ChoSong *)); - all_songs[s] = *so; - } - free(songs); - } - all_songs = erealloc(all_songs, (s+1) * sizeof(struct ChoSong *)); - all_songs[s] = NULL; - qsort(all_songs, cho_song_count(all_songs), sizeof(struct ChoSong *), cho_song_compare); - } - char *pdf_filename = out_pdf_create(chordpro_filepath, output, all_songs, config); - if (!pdf_filename) { - LOG_DEBUG("out_pdf_new failed."); - return 1; - } - util_log(LOG_INFO, "Writing pdf to file: '%s'.", pdf_filename); - free(pdf_filename); - free(output); - cho_songs_free(all_songs); - config_free(config); - return 0; -} diff --git a/out_pdf.c b/out_pdf.c @@ -1,2819 +0,0 @@ -#include <stdbool.h> -#include <string.h> -#include <unistd.h> -#include <pdfio.h> -#include <pdfio-content.h> -#include <sys/stat.h> -#include <errno.h> -#include <fontconfig/fontconfig.h> -#include <grapheme.h> -#include <math.h> -#include "types.h" -#include "out_pdf.h" -#include "chordpro.h" -#include "config.h" -#include "util.h" -#include "chord_diagram.h" - -static struct Obj **g_fonts = NULL; -static char g_cho_dirpath[PATH_MAX]; -static char g_current_font_name[200]; -static double g_current_font_size; -static int g_current_page_index; -static pdfio_file_t *g_pdf_file = NULL; -static struct Config *g_config = NULL; - -static const char *g_base_fonts[] = { - "Courier", - "Courier-Bold", - "Courier-BoldItalic", - "Courier-Italic", - "Helvetica", - "Helvetica-Bold", - "Helvetica-BoldOblique", - "Helvetica-Oblique", - "Symbol", - "Times-Bold", - "Times-BoldItalic", - "Times-Italic", - "Times-Roman", - "ZapfDingbats" -}; - -static struct Obj * -obj_new(void) -{ - struct Obj *obj = emalloc(sizeof(struct Obj)); - obj->name = NULL; - obj->value = NULL; - return obj; -} - -static void -obj_free(struct Obj *obj) -{ - free(obj->name); - free(obj); -} - -static void -objs_free(struct Obj **objs) -{ - if (!objs) { - return; - } - struct Obj **start = objs; - for (; *objs; objs++) { - obj_free(*objs); - } - free(start); -} - -static pdfio_obj_t * -objs_get_obj(struct Obj **objs, const char *name) -{ - if (!objs) { - return NULL; - } - struct Obj **o; - for (o = objs; *o; o++) { - if (!strcmp((*o)->name, name)) { - return (*o)->value; - } - } - return NULL; -} - -static void -objs_add_obj(struct Obj ***array, struct Obj *obj) -{ - if (!*array) { - *array = erealloc(*array, 2 * sizeof(struct Obj *)); - (*array)[0] = obj; - (*array)[1] = NULL; - } else { - int a; - for (a = 0; (*array)[a]; a++); - *array = erealloc(*array, (a+2) * sizeof(struct Obj *)); - (*array)[a] = obj; - (*array)[a+1] = NULL; - } -} - -pdfio_obj_t * -out_pdf_fnt_obj_get_by_name(const char *name) -{ - int i; - for (i = 0; g_fonts[i]; i++) { - if (!strcmp(g_fonts[i]->name, name)) { - return g_fonts[i]->value; - } - } - printf("name '%s'\n", name); - return NULL; -} - -static char * -fnt_name_create(struct Font *font) -{ - char *name = str_normalize(font->name); - const char *family = cho_font_family_to_config_string(font->family); - const char *style = cho_font_style_to_config_string(font->style); - const char *weight = cho_font_weight_to_config_string(font->weight); - size_t len = strlen(name) + strlen(family) + strlen(style) + strlen(weight); - int n = 0; - int i; - char *fnt_name = emalloc((len + 4) * sizeof(char)); - for (i = 0; name[i]; i++, n++) { - fnt_name[n] = name[i]; - } - fnt_name[n] = '-'; - n++; - for (i = 0; family[i]; i++, n++) { - fnt_name[n] = family[i]; - } - fnt_name[n] = '-'; - n++; - for (i = 0; style[i]; i++, n++) { - fnt_name[n] = style[i]; - } - fnt_name[n] = '-'; - n++; - for (i = 0; weight[i]; i++, n++) { - fnt_name[n] = weight[i]; - } - fnt_name[n] = 0; - free(name); - return fnt_name; -} - -static bool -fonts_add_if_not_in(struct Font ***array, struct Font *font) -{ - if (!*array) { - *array = erealloc(*array, 2 * sizeof(struct Font *)); - (*array)[0] = font; - (*array)[1] = NULL; - return true; - } else { - int a; - for (a = 0; (*array)[a]; a++) { - if ( - !strcmp((*array)[a]->name, font->name) && - (*array)[a]->family == font->family && - (*array)[a]->style == font->style && - (*array)[a]->weight == font->weight - ) { - return false; - } - } - *array = erealloc(*array, (a+2) * sizeof(struct Font *)); - (*array)[a] = font; - (*array)[a+1] = NULL; - return true; - } -} - -static struct Font ** -fonts_get_all(struct ChoSong **songs, struct Config *config) -{ - struct Font **fonts = NULL; - struct Font *font; - struct ChoSong **so; - struct ChoMetadata **m; - struct ChoSection **se; - struct ChoLine **li; - struct ChoLineItemAbove **above; - struct ChoLineItem **it; - struct ChoStyle *style; - bool added; - int i; - for (so = songs; *so; so++) { - for (i = 0; i < TT_LENGTH; i++) { - if ((*so)->present_text_types[i]) { - font = cho_font_copy(config->output->styles[i]->font); - added = fonts_add_if_not_in(&fonts, font); - if (!added) { - cho_font_free(font); - } - } - } - for (m = (*so)->metadata; *m; m++) { - font = cho_font_copy((*m)->style->font); - added = fonts_add_if_not_in(&fonts, font); - if (!added) { - cho_font_free(font); - } - } - for (se = (*so)->sections; *se; se++) { - for (li = (*se)->lines; *li; li++) { - for (above = (*li)->text_above; *above; above++) { - if ((*above)->is_chord) { - style = (*above)->u.chord->style; - } else { - style = (*above)->u.annot->style; - } - if (style->font->name) { - font = cho_font_copy(style->font); - added = fonts_add_if_not_in(&fonts, font); - if (!added) { - cho_font_free(font); - } - } - } - for (it = (*li)->items; *it; it++) { - if ((*it)->is_text) { - style = (*it)->u.text->style; - if (style->font->name) { - font = cho_font_copy(style->font); - added = fonts_add_if_not_in(&fonts, font); - if (!added) { - cho_font_free(font); - } - } - } - } - } - } - } - return fonts; -} - -static int -fontpath_count_fonts(FcChar8 *path) -{ - int count; - FcFontSet *set; - set = FcFontSetCreate(); - if (!FcFileScan(set, NULL, NULL, NULL, path, false)) { - LOG_DEBUG("fontpath_count_fonts failed."); - return -1; - } - count = set->nfont; - FcFontSetDestroy(set); - return count; -} - -static char * -fontpath_find(struct Font *font, enum FontType font_type) -{ - FcObjectSet *obj; - FcPattern *pattern; - char *filepath = NULL; - obj = FcObjectSetBuild(FC_FAMILY, FC_SLANT, FC_WIDTH, FC_WEIGHT, FC_FONTFORMAT, FC_FONT_WRAPPER, FC_FILE, NULL); - pattern = FcPatternCreate(); - FcValue family = { .type = FcTypeString, .u.s = (FcChar8 *)font->name }; - FcPatternAdd(pattern, FC_FAMILY, family, FcFalse); - FcValue font_wrapper = { .type = FcTypeString, .u.s = (FcChar8 *)"SFNT" }; - FcPatternAdd(pattern, FC_FONT_WRAPPER, font_wrapper, FcFalse); - FcValue variable = { .type = FcTypeBool, .u.b = FcFalse }; - FcPatternAdd(pattern, FC_VARIABLE, variable, FcFalse); - FcValue width = { .type = FcTypeInteger, .u.i = FC_WIDTH_NORMAL }; - FcPatternAdd(pattern, FC_WIDTH, width, FcFalse); - /* TODO: Also handle FF_NORMAL, FF_SANS and FF_SERIF, but how? */ - if (font->family == FF_MONOSPACE) { - FcValue spacing = { .type = FcTypeInteger, .u.i = FC_MONO }; - FcPatternAdd(pattern, FC_SPACING, spacing, FcFalse); - } - FcValue type; - type.type = FcTypeString; - if (font_type == FT_OTF) - type.u.s = (FcChar8 *)"CFF"; - else - type.u.s = (FcChar8 *)"TrueType"; - FcPatternAdd(pattern, FC_FONTFORMAT, type, FcFalse); - FcValue style; - style.type = FcTypeInteger; - switch (font->style) { - case FS_ROMAN: - style.u.i = FC_SLANT_ROMAN; - break; - case FS_OBLIQUE: - style.u.i = FC_SLANT_OBLIQUE; - break; - case FS_ITALIC: - style.u.i = FC_SLANT_ITALIC; - break; - default: - util_log(LOG_ERR, "Invalid font style value '%d'.", font->style); - return NULL; - } - FcPatternAdd(pattern, FC_SLANT, style, FcFalse); - FcValue weight; - weight.type = FcTypeInteger; - switch (font->weight) { - case FW_REGULAR: - weight.u.i = FC_WEIGHT_REGULAR; - break; - case FW_BOLD: - weight.u.i = FC_WEIGHT_BOLD; - break; - default: - util_log(LOG_ERR, "Invalid font weight value '%d'.", font->weight); - return NULL; - } - FcPatternAdd(pattern, FC_WEIGHT, weight, FcFalse); - FcFontSet *set = FcFontList(NULL, pattern, obj); - FcChar8 *file; - int i; - for (i = 0; i<set->nfont; i++) { - if (FcPatternGetString(set->fonts[i], FC_FILE, 0, &file) == FcResultMatch) { - if ( - !file_extension_equals((const char *)file, "ttc") && - fontpath_count_fonts(file) == 1 - ) { - filepath = strdup((const char *)file); - break; - } - } - } - FcObjectSetDestroy(obj); - FcPatternDestroy(pattern); - FcFontSetDestroy(set); - return filepath; -} - -static bool -pdf_load_chord_diagram_fonts(void) -{ - struct Obj *fnt; - fnt = obj_new(); - fnt->name = strdup("chord-diagram-regular-font"); - fnt->value = pdfioFileCreateFontObjFromBase(g_pdf_file, "Helvetica"); - objs_add_obj(&g_fonts, fnt); - fnt = obj_new(); - fnt->name = strdup("chord-diagram-symbols"); - fnt->value = pdfioFileCreateFontObjFromFile(g_pdf_file, SYMBOLS_FILEPATH, true); - objs_add_obj(&g_fonts, fnt); - return true; -} - -static const char * -is_base_font(struct Font *font) -{ - int i; - for (i = 0; i<14; i++) { - if (!strcmp(font->name, g_base_fonts[i])) { - return g_base_fonts[i]; - } - } - return NULL; -} - -static bool -font_name_is_path(const char *name) -{ - if (strchr(name, '/')) { - return true; - } - if (file_extension_equals(name, "ttf") || file_extension_equals(name, "otf")) { - return true; - } - return false; -} - -static bool -pdf_load_fonts(struct Font **needed_fonts, struct Config *config) -{ - char *fontpath; - const char *name; - struct Font **f; - struct Obj *fnt; - for (f = needed_fonts; *f; f++) { - if (font_name_is_path((*f)->name)) { - fontpath = filepath_resolve_tilde((*f)->name); - fnt = obj_new(); - fnt->name = fnt_name_create(*f); - fnt->value = pdfioFileCreateFontObjFromFile(g_pdf_file, fontpath, true); - if (!fnt->value) { - LOG_DEBUG("pdfioFileCreateFontObjFromFile failed."); - return false; - } - free(fontpath); - util_log(LOG_INFO, "Loaded font from '%s'.", (*f)->name); - // cho_font_print(*f); - objs_add_obj(&g_fonts, fnt); - } else - if ((name = is_base_font(*f))) { - fnt = obj_new(); - fnt->name = fnt_name_create(*f); - fnt->value = pdfioFileCreateFontObjFromBase(g_pdf_file, name); - if (!fnt->value) { - LOG_DEBUG("pdfioFileCreateFontObjFromBase failed."); - return false; - } - objs_add_obj(&g_fonts, fnt); - } else { - fontpath = fontpath_find(*f, FT_TTF); - if (fontpath) { - fnt = obj_new(); - fnt->name = fnt_name_create(*f); - fnt->value = pdfioFileCreateFontObjFromFile(g_pdf_file, fontpath, true); - if (!fnt->value) { - LOG_DEBUG("pdfioFileCreateFontObjFromFile failed."); - return false; - } - util_log(LOG_INFO, "Loaded font from '%s'.", fontpath); - // cho_font_print(*f); - objs_add_obj(&g_fonts, fnt); - free(fontpath); - } else { - fontpath = fontpath_find(*f, FT_OTF); - if (fontpath) { - fnt = obj_new(); - fnt->name = fnt_name_create(*f); - fnt->value = pdfioFileCreateFontObjFromFile(g_pdf_file, fontpath, true); - if (!fnt->value) { - LOG_DEBUG("pdfioFileCreateFontObjFromFile failed."); - return false; - } - util_log(LOG_INFO, "Loaded font from '%s'.", fontpath); - // cho_font_print(*f); - objs_add_obj(&g_fonts, fnt); - free(fontpath); - } else { - util_log(LOG_ERR, "Didn't find font file for following font:"); - cho_font_print(*f); - return false; - } - } - } - } - if (config->output->diagram->show) { - if (!pdf_load_chord_diagram_fonts()) { - LOG_DEBUG("pdf_load_chord_diagram_fonts failed."); - return false; - } - } - return true; -} - -static char * -pdf_filename_generate_from_songs(struct ChoSong **songs) -{ - char *filename; - char *normalized_title; - const char *title; - int len = cho_song_count(songs); - if (len == 0) - return NULL; - if (len == 1) { - title = cho_metadata_get(songs[0]->metadata, "title"); - if (!title) { - /* INFO: unreachable because the parser already checks the presence of the 'title' directive */ - util_log(LOG_ERR, "Song has no title."); - return NULL; - } - normalized_title = str_normalize(title); - filename = file_extension_replace_or_add(normalized_title, "pdf"); - free(normalized_title); - return filename; - } - return strdup("collection-of-songs.pdf"); -} - -static char * -pdf_filepath_create(struct ChoSong **songs, const char *cho_filepath, const char *out) -{ - char *pdf_filepath = NULL; - char *pdf_filename; - char *tmp; - enum FileType type; - if (cho_filepath) { - pdf_filepath = file_extension_replace_or_add(cho_filepath, "pdf"); - pdf_filename = filepath_basename(pdf_filepath); - } else { - pdf_filename = pdf_filename_generate_from_songs(songs); - if (!pdf_filename) { - LOG_DEBUG("pdf_filename_generate_from_songs failed."); - return NULL; - } - } - if (out) { - type = file_type(out); - switch (type) { - case F_ERROR: - tmp = filepath_dirname(out); - if (file_type(tmp) == F_FOLDER) { - free(pdf_filepath); - free(pdf_filename); - free(tmp); - return strdup(out); - } else { - free(tmp); - util_log(LOG_ERR, "Invalid argument --output/-o value."); - return NULL; - } - break; - case F_REG_FILE: - free(pdf_filepath); - free(pdf_filename); - return strdup(out); - case F_FOLDER: - tmp = filepath_add_ending_slash_if_missing(out); - tmp = erealloc(tmp, (strlen(tmp)+strlen(pdf_filename)+1) * sizeof(char)); - strcat(tmp, pdf_filename); - free(pdf_filename); - free(pdf_filepath); - return tmp; - case F_OTHER: - util_log(LOG_ERR, "Invalid argument --output/-o value. It doesn't refer to a folder or regular file."); - return NULL; - default: - util_log(LOG_ERR, "Invalid enum FileType value '%d'.", type); - return NULL; - } - } else { - if (pdf_filepath) { - free(pdf_filename); - return pdf_filepath; - } - return pdf_filename; - } -} - -static bool -pdf_font_set(pdfio_stream_t *stream, struct Font *font) -{ - static int page_index = 0; - char *name = fnt_name_create(font); - if ( - !strcmp(name, g_current_font_name) && - font->size == g_current_font_size && - g_current_page_index == page_index - ) { - free(name); - return true; - } - if (!pdfioContentSetTextFont(stream, name, font->size)) { - LOG_DEBUG("pdfioContentSetTextFont failed."); - return false; - } - strcpy(g_current_font_name, name); - g_current_font_size = font->size; - page_index = g_current_page_index; - free(name); - return true; -} - -static double -text_width(const char *text, struct ChoStyle *style) -{ - char *name = 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, text, style->font->size); -} - -static double -text_above_width(struct ChoLineItemAbove *above) -{ - double width; - if (above->is_chord) { - char *name; - name = cho_chord_name_generate(above->u.chord); - width = text_width(name, above->u.chord->style); - if (width == ERROR) { - LOG_DEBUG("text_width failed."); - return ERROR; - } - free(name); - } else { - width = text_width(above->u.annot->text, above->u.annot->style); - if (width == ERROR) { - LOG_DEBUG("text_width failed."); - return ERROR; - } - } - return width; -} - -static int -find_whitespace(const char *str, size_t start) -{ - int i; - for (i = start; i>=0; i--) { - if (str[i] == ' ' || str[i] == '\t') { - return i; - } - } - return -1; -} - -static int -text_find_fitting( - const char *str, - struct ChoStyle *style, - double x, - double max_width -) -{ - size_t len = strlen(str); - size_t start = len - 1; - char tmp[len+1]; - strcpy((char *)&tmp, str); - double width; - int i; - do { - i = find_whitespace((const char *)&tmp, start); - if (i == -1) { - util_log(LOG_ERR, "Can't split text because no whitespace was found."); - return -1; - } - tmp[i] = 0; - width = text_width((const char *)&tmp, style); - if (width == ERROR) { - LOG_DEBUG("text_width failed."); - return -1; - } - start = i - 1; - } while (x + width > max_width); - return i; -} - -static bool -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 = 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 = 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 = 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, text->x, y)) { - LOG_DEBUG("pdfioContentPathMoveTo failed."); - return false; - } - if (!pdfioContentPathLineTo(stream, text->x + width, y)) { - LOG_DEBUG("pdfioContentPathLineTo failed."); - return false; - } - if (!pdfioContentSetStrokeColorRGB(stream, red, green, blue)) { - LOG_DEBUG("pdfioContentSetStrokeColorRGB failed."); - return false; - } - if (!pdfioContentStroke(stream)) { - LOG_DEBUG("pdfioContentStroke failed."); - return false; - } - return true; -} - -static bool -pdf_draw_rectangle( - pdfio_stream_t *stream, - struct PDFText *text -) -{ - double height; - double red, green, blue; - red = text->style->background_color->red / 255.0; - green = text->style->background_color->green / 255.0; - blue = text->style->background_color->blue / 255.0; - if (!pdfioContentSetFillColorRGB(stream, red, green, blue)) { - fprintf(stderr, "pdfioContentSetFillColorRGB failed.\n"); - return false; - } - height = (8.0 + text->style->font->size) - text->style->font->size * 0.8; - if (!pdfioContentPathRect(stream, text->x, text->y, text->width, height)) { - fprintf(stderr, "pdfioContentPathRect failed.\n"); - return false; - } - if (!pdfioContentFill(stream, true)) { - fprintf(stderr, "pdfioContentFill failed.\n"); - return false; - } - return true; -} - -static bool -is_purest_white(struct RGBColor *color) -{ - if (color->red == 255 && color->green == 255 && color->blue == 255) { - return true; - } - return false; -} - -static bool -pdf_text_show(pdfio_stream_t *stream, struct PDFText *text) -{ - double red, green, blue; - bool unicode; - if (!pdf_font_set(stream, text->style->font)) { - LOG_DEBUG("pdf_font_set failed."); - return false; - } - unicode = !is_base_font(text->style->font); - if (!is_purest_white(text->style->background_color)) { - if (!pdf_draw_rectangle(stream, text)) { - LOG_DEBUG("draw_rectangle failed."); - return false; - } - } - 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; - } - if (!pdfioContentTextBegin(stream)) { - LOG_DEBUG("pdfioContentTextBegin failed."); - return false; - } - if (!pdfioContentTextMoveTo(stream, text->x, text->y)) { - LOG_DEBUG("pdfioContentTextMoveTo failed."); - return false; - } - if (!pdfioContentSetTextRise(stream, text->style->rise)) { - LOG_DEBUG("pdfioContentSetTextRise failed."); - return false; - } - if (!pdfioContentTextShow(stream, unicode, text->text)) { - LOG_DEBUG("pdfioContentTextShow failed."); - return false; - } - if (!pdfioContentTextEnd(stream)) { - LOG_DEBUG("pdfioContentTextEnd failed."); - return false; - } - if (text->style->underline_style == LS_SINGLE) { - pdf_draw_line(stream, text, text->y, text->width, LL_UNDER); - } else if (text->style->underline_style == LS_DOUBLE) { - pdf_draw_line(stream, text, text->y, text->width, LL_UNDER); - pdf_draw_line(stream, text, text->y-1.5, text->width, LL_UNDER); - } - if (text->style->strikethrough) { - pdf_draw_line(stream, text, text->y, text->width, LL_STRIKETHROUGH); - } - if (text->style->overline_style == LS_SINGLE) { - pdf_draw_line(stream, text, text->y, text->width, LL_OVER); - } else if (text->style->overline_style == LS_DOUBLE) { - pdf_draw_line(stream, text, text->y, text->width, LL_OVER); - pdf_draw_line(stream, text, text->y+1.5, text->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, text->x - 2.0, text->y - 2.0, text->width + 4.0, text->style->font->size * 0.8 + 4.0)) { - LOG_DEBUG("pdfioContentPathRect failed."); - return false; - } - if (!pdfioContentStroke(stream)) { - LOG_DEBUG("pdfioContentStroke failed."); - return false; - } - } - return true; -} - -static bool -annot_page_link_add( - struct PDFContext *ctx, - struct TocEntry *entry, - int toc_page_count, - int line_count, - double font_size -) -{ - int page_index; - pdfio_rect_t rect; - pdfio_dict_t *annot; - pdfio_array_t *destination; - rect.x1 = MARGIN_HORIZONTAL; - rect.x2 = MARGIN_HORIZONTAL + LINE_WIDTH; - rect.y1 = ctx->y - 2.0; - rect.y2 = ctx->y + (8.0 + font_size) * line_count - font_size * 0.8; - page_index = entry->page_index + toc_page_count; - annot = pdfioDictCreate(g_pdf_file); - if (!pdfioDictSetName(annot, "Subtype", "Link")) { - LOG_DEBUG("pdfioDictSetName failed."); - return false; - } - if (!pdfioDictSetRect(annot, "Rect", &rect)) { - LOG_DEBUG("pdfioDictSetRect failed."); - return false; - } - destination = pdfioArrayCreate(g_pdf_file); - if (!pdfioArrayAppendNumber(destination, page_index)) { - LOG_DEBUG("pdfioArrayAppendNumber failed."); - return false; - } - if (!pdfioArrayAppendName(destination, "FitH")) { - LOG_DEBUG("pdfioArrayAppendName failed."); - return false; - } - // TODO: Is this constant '30.0' correct with different font sizes, etc. ? - // clicking the annotation should show the song including the song title at the top - if (!pdfioArrayAppendNumber(destination, entry->page_y + 30.0)) { - LOG_DEBUG("pdfioArrayAppendNumber failed."); - return false; - } - if (!pdfioDictSetArray(annot, "Dest", destination)) { - LOG_DEBUG("pdfioDictSetArray failed."); - return false; - } - if (!pdfioArrayAppendDict(ctx->content->pages[ctx->page]->annots, annot)) { - LOG_DEBUG("pdfioArrayAppendDict failed."); - return false; - } - return true; -} - -static bool -annot_url_link_add(struct PDFContext *ctx, struct ChoStyle *style, double width) -{ - 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(g_pdf_file); - if (!pdfioDictSetName(annot, "Subtype", "Link")) { - LOG_DEBUG("pdfioDictSetName failed."); - return false; - } - if (!pdfioDictSetRect(annot, "Rect", &rect)) { - LOG_DEBUG("pdfioDictSetRect failed."); - return false; - } - action = pdfioDictCreate(g_pdf_file); - if (!pdfioDictSetName(action, "S", "URI")) { - LOG_DEBUG("pdfioDictSetName failed."); - return false; - } - if (!pdfioDictSetString(action, "URI", style->href)) { - LOG_DEBUG("pdfioDictSetString failed."); - return false; - } - if (!pdfioDictSetDict(annot, "A", action)) { - LOG_DEBUG("pdfioDictSetDict failed."); - return false; - } - if (!pdfioArrayAppendDict(ctx->content->pages[ctx->page]->annots, annot)) { - LOG_DEBUG("pdfioArrayAppendDict failed."); - return false; - } - return true; -} - -static bool -pdf_set_title(pdfio_file_t *pdf, struct ChoSong **songs) -{ - // INFO: Set pdf title only if a single song exist - if (songs[0] && !songs[1]) { - const char *title; - title = cho_metadata_get(songs[0]->metadata, "title"); - if (title) { - pdfioFileSetTitle(pdf, title); - return true; - } - return false; - } - return true; -} - -static pdfio_stream_t * -pdf_page_create( - pdfio_file_t *pdf, - struct PDFImage **imgs, - pdfio_array_t *annots -) -{ - pdfio_dict_t *page_dict; - pdfio_stream_t *page_stream; - pdfio_array_t *color_array = pdfioArrayCreateColorFromStandard(pdf, 3, PDFIO_CS_ADOBE); - struct Obj **f; - struct PDFImage **i; - 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 NULL; - } - if (imgs) { - for (i = imgs; *i; i++) { - if (!pdfioPageDictAddImage(page_dict, (*i)->name, (*i)->obj)) { - LOG_DEBUG("pdfioPageDictAddImage failed."); - return NULL; - } - } - } - for (f = g_fonts; *f; f++) { - if (!pdfioPageDictAddFont(page_dict, (*f)->name, (*f)->value)) { - LOG_DEBUG("pdfioPageDictAddFont failed."); - return NULL; - } - } - page_stream = pdfioFileCreatePage(pdf, page_dict); - if (!pdfioContentSetFillColorSpace(page_stream, "rgbcolorspace")) { - LOG_DEBUG("pdfioContentSetFillColorSpace failed."); - return NULL; - } - if (!pdfioContentSetStrokeColorSpace(page_stream, "rgbcolorspace")) { - LOG_DEBUG("pdfioContentSetStrokeColorSpace failed."); - return NULL; - } - return page_stream; -} - -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."); - util_log(LOG_ERR, "%s: %s", image_path, strerror(errno)); - return NULL; - } - memset(tmp, 0, PATH_MAX); - sprintf((char *)&tmp, "%ld", s.st_ino); - return strdup(tmp); -} - -static bool -pdf_load_images(struct Obj ***images, pdfio_file_t *file, struct ChoSong **songs) -{ - struct ChoSong **s; - struct ChoSection **se; - struct ChoLine **li; - struct ChoLineItem **it; - int i = 0; - char filepath[PATH_MAX]; - char *name; - char *image_filepath; - for (s = songs; *s; s++) { - for (se = (*s)->sections; *se; se++) { - for (li = (*se)->lines; *li; li++) { - for (it = (*li)->items; *it; it++) { - if (!(*it)->is_text) { - memset(filepath, 0, PATH_MAX); - strcpy((char *)&filepath, g_cho_dirpath); - strcat((char *)&filepath, "/"); - strcat((char *)&filepath, (*it)->u.image->src); - name = image_name((*it)->u.image); - if (!name) { - LOG_DEBUG("image_name failed."); - return false; - } - if (!objs_get_obj(*images, name)) { - *images = erealloc(*images, (i+2) * sizeof(struct Obj *)); - (*images)[i] = obj_new(); - (*images)[i]->name = strdup(name); - if (strchr((*it)->u.image->src, '/')) { - image_filepath = (*it)->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; - } - util_log(LOG_INFO, "Loaded image from '%s'.", image_filepath); - (*images)[i+1] = NULL; - } - free(name); - i++; - } - } - } - } - } - return true; -} - -static bool -pdf_get_chords(struct ChoSong *song, struct ChoChord ***chords) -{ - struct ChoSection **se; - struct ChoLine **li; - struct ChoLineItemAbove **above; - for (se = song->sections; *se; se++) { - for (li = (*se)->lines; *li; li++) { - for (above = (*li)->text_above; *above; above++) { - if ((*above)->is_chord) { - if (!cho_chords_has(*chords, (*above)->u.chord)) { - cho_chords_add(chords, (*above)->u.chord); - } - } - } - } - } - return true; -} - -static double -line_width_until_text_above( - struct ChoLineItem **items, - struct ChoLineItemAbove *above, - struct Obj **img_objs, - struct SpaceNeeded *space -) -{ - int i, save_i, save_k; - int k = EMPTY_INT; - 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 = 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 ERROR; - } - 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); - if (!name) { - LOG_DEBUG("image_name failed."); - return ERROR; - } - obj = objs_get_obj(img_objs, name); - if (!obj) { - LOG_DEBUG("objs_get_obj failed."); - return ERROR; - } - width += image_width(items[i]->u.image, obj); - free(name); - } - } - return width; -} - -static struct PDFImage * -pdf_image_new(void) -{ - struct PDFImage *image = emalloc(sizeof(struct PDFImage)); - 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 -pdf_image_free(struct PDFImage *image) -{ - if (!image) { - return; - } - free(image->name); - free(image); -} - -static struct PDFText * -pdf_text_new(void) -{ - struct PDFText *t = emalloc(sizeof(struct PDFText)); - t->text = NULL; - t->style = NULL; - t->x = 0.0; - t->y = 0.0; - t->width = 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 void -toc_entry_free(struct TocEntry *entry) -{ - free(entry->title); - free(entry); -} - -static struct PDFPage * -pdf_page_new(void) -{ - struct PDFPage *page = emalloc(sizeof(struct PDFPage)); - page->texts = NULL; - page->images = NULL; - page->diagrams = NULL; - page->annots = pdfioArrayCreate(g_pdf_file); - return page; -} - -static void -pdf_page_free(struct PDFPage *page) -{ - if (!page) { - return; - } - struct PDFText **t; - struct PDFImage **i; - struct ChordDiagram **d; - if ((t = page->texts)) { - for (; *t; t++) { - pdf_text_free(*t); - } - free(page->texts); - } - if ((i = page->images)) { - for (; *i; i++) { - pdf_image_free(*i); - } - free(page->images); - } - if ((d = page->diagrams)) { - for (; *d; d++) { - chord_diagram_free(*d); - } - free(page->diagrams); - } - free(page); -} - -static void -pdf_context_init(struct PDFContext *ctx) -{ - ctx->content = NULL; - ctx->x = MARGIN_HORIZONTAL; - ctx->y = MEDIABOX_HEIGHT - MARGIN_TOP; - ctx->text = 0; - ctx->image = 0; - ctx->diagram = 0; - ctx->page = 0; - ctx->toc_entry = 0; - ctx->spaces = NULL; - ctx->biggest_font_size = 0.0; - ctx->consumed_lyrics = 0; - ctx->prev_added_space = 0.0; - ctx->margin_bottom = MARGIN_BOTTOM; -} - -static struct PDFContent * -pdf_content_new(void) -{ - struct PDFContent *content = emalloc(sizeof(struct PDFContent)); - content->pages = NULL; - content->toc = NULL; - return content; -} - -static void -pdf_content_free(struct PDFContent *content) -{ - if (!content) { - return; - } - struct PDFPage **p; - struct TocEntry **toc; - for (p = content->pages; *p; p++) { - pdf_page_free(*p); - } - free(content->pages); - if ((toc = content->toc)) { - for (; *toc; toc++) { - toc_entry_free(*toc); - } - free(content->toc); - } - free(content); -} - -static void -spaces_free(struct SpaceNeeded **spaces) -{ - if (!spaces) { - return; - } - struct SpaceNeeded **s; - for (s = spaces; *s; s++) { - free(*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 == ERROR) { - 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 == ERROR) { - LOG_DEBUG("line_width_until_text_above failed."); - return false; - } - prev_width = text_above_width(text_above[i]); - if (prev_width == ERROR) { - LOG_DEBUG("text_above_width failed."); - return false; - } - i++; - width_between = width_until_cur - width_until_prev; - if (prev_width + MIN_CHORD_GAP_WIDTH >= width_between) { - space.text_above_index = i; - space.amount = prev_width - width_between + MIN_CHORD_GAP_WIDTH; - *spaces = erealloc(*spaces, (sp+1) * sizeof(struct SpaceNeeded *)); - (*spaces)[sp] = emalloc(sizeof(struct SpaceNeeded)); - *((*spaces)[sp]) = space; - sp++; - } else { - } - } - *spaces = erealloc(*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; - if (item->is_text) { - width = text_width(item->u.text->text, item->u.text->style); - if (width == ERROR) { - LOG_DEBUG("text_width failed."); - return ERROR; - } - struct SpaceNeeded **s; - if ((s = spaces)) { - for (; *s; s++) { - if ((*s)->line_item_index == i) { - width += (*s)->amount; - } - } - } - } else { - char *name; - pdfio_obj_t *obj; - name = image_name(item->u.image); - if (!name) { - LOG_DEBUG("image_name failed."); - return ERROR; - } - obj = objs_get_obj(img_objs, name); - if (!obj) { - LOG_DEBUG("objs_get_obj failed."); - return ERROR; - } - 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 = emalloc(sizeof(struct CharPosition)); - pos->line_item_index = EMPTY_INT; - pos->text_index = EMPTY_INT; - 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 == ERROR) { - 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, - LINE_WIDTH - ); - if (pos->text_index == EMPTY_INT) { - 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 **it; - 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; - } - for (it = left_items; *it && i <= end; it++, i++) { - if (!(*it)->is_text) { - name = image_name((*it)->u.image); - if (!name) { - LOG_DEBUG("image_name failed."); - return ERROR; - } - obj = objs_get_obj(img_objs, name); - if (!obj) { - LOG_DEBUG("objs_get_obj failed."); - return ERROR; - } - height = image_height((*it)->u.image, obj); - if (height > biggest) { - biggest = height; - } - free(name); - } - } - 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; - for (a = aboves; *a; a++) { - (*a)->position -= consumed_lyrics + 1; // why plus one? - } -} - -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 = erealloc(*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(item->u.text->text, item->u.text->style); - if (width == ERROR) { - LOG_DEBUG("text_width failed."); - return false; - } - (*texts)[ctx->text]->width = width; - if (item->u.text->style->href) { - if (!annot_url_link_add(ctx, item->u.text->style, width)) { - LOG_DEBUG("annot_url_link_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 = erealloc((*texts)[ctx->text]->text, (t+1) * sizeof(char)); - (*texts)[ctx->text]->text[t] = item->u.text->text[c]; - t++; - for (sp = ctx->spaces; *sp; sp++) { - if ((*sp)->line_item_index == i && (*sp)->text_index == c) { - size_t len = grapheme_next_character_break_utf8(&item->u.text->text[c], 10); - size_t i; - for (i = 0; i<len-1; i++, t++) { - c++; - (*texts)[ctx->text]->text = erealloc((*texts)[ctx->text]->text, (t+1) * sizeof(char)); - (*texts)[ctx->text]->text[t] = item->u.text->text[c]; - } - (*texts)[ctx->text]->text = erealloc((*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 == ERROR) { - LOG_DEBUG("text_width failed."); - return false; - } - (*texts)[ctx->text]->width = width; - if (item->u.text->style->href) { - if (!annot_url_link_add(ctx, item->u.text->style, width)) { - LOG_DEBUG("annot_url_link_add failed."); - return false; - } - } - ctx->x += width; - ctx->x += (*sp)->amount; - t = 0; - ctx->consumed_lyrics += strlen((*texts)[ctx->text]->text); - ctx->text++; - *texts = erealloc(*texts, (ctx->text+1) * sizeof(struct PDFText *)); - (*texts)[ctx->text] = pdf_text_new(); - } - } - } - (*texts)[ctx->text]->text = erealloc((*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 == ERROR) { - LOG_DEBUG("text_width failed."); - return false; - } - (*texts)[ctx->text]->width = width; - if (item->u.text->style->href) { - if (!annot_url_link_add(ctx, item->u.text->style, width)) { - LOG_DEBUG("annot_url_link_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 const char * -handle_index(int index, char a, char b, char c) -{ - int i = 0; - static char str[16]; - memset(&str, 0, sizeof(str)); - switch (index) { - case 1: - str[i++] = a; - break; - case 2: - str[i++] = a; - str[i++] = a; - break; - case 3: - str[i++] = a; - str[i++] = a; - str[i++] = a; - break; - case 4: - str[i++] = a; - str[i++] = b; - break; - case 5: - str[i++] = b; - break; - case 6: - str[i++] = b; - str[i++] = a; - break; - case 7: - str[i++] = b; - str[i++] = a; - str[i++] = a; - break; - case 8: - str[i++] = b; - str[i++] = a; - str[i++] = a; - str[i++] = a; - break; - case 9: - str[i++] = a; - str[i++] = c; - } - str[i] = 0; - return str; -} - -static const char * -numeral_system_western_arabic_to_roman(unsigned int n) -{ - if (n > 999) { - util_log(LOG_ERR, "Converting numbers higher than 999 is not supported."); - return NULL; - } - const char *str; - static char roman[64]; - int k; - int r = 0; - memset(&str, 0, sizeof(str)); - char i = 'I'; - char v = 'V'; - char x = 'X'; - char l = 'L'; - char c = 'C'; - char d = 'D'; - char m = 'M'; - int index_0 = n - n / 10 * 10; - int index_1 = (n - n / 100 * 100 - index_0) / 10; - int index_2 = (n - n / 1000 * 1000 - index_0 - index_1) / 100; - if (index_2 > 0) { - str = handle_index(index_2, c, d, m); - for (k = 0; str[k]; k++, r++) { - roman[r] = str[k]; - } - } - if (index_1 > 0) { - str = handle_index(index_1, x, l, c); - for (k = 0; str[k]; k++, r++) { - roman[r] = str[k]; - } - } - if (index_0 > 0) { - str = handle_index(index_0, i, v, x); - for (k = 0; str[k]; k++, r++) { - roman[r] = str[k]; - } - } - roman[r] = 0; - return roman; -} - -static const char * -numeral_system_number_to_str(enum NumeralSystem system, int n) -{ - if (system == NUS_ROMAN) { - const char *str = numeral_system_western_arabic_to_roman((unsigned int)n); - if (!str) { - LOG_DEBUG("numeral_system_western_arabic_to_roman failed."); - return NULL; - } - return str; - } else { - static char str[11+1]; - sprintf((char *)&str, "%d", n); - return str; - } -} - -static bool -pdf_page_add_page_no(struct PDFContext *ctx, enum NumeralSystem numeral_system) -{ - struct PDFText ***texts; - struct ChoStyle *style; - const char *page_no; - double width, x; - - style = cho_style_new(); - style->font->name = strdup(DEFAULT_FONT_FAMILY); - texts = &ctx->content->pages[ctx->page]->texts; - page_no = numeral_system_number_to_str(numeral_system, ctx->page+1); - if (!page_no) { - LOG_DEBUG("numeral_system_number_to_str failed."); - return false; - } - width = text_width(page_no, style); - if (width == ERROR) { - LOG_DEBUG("text_width failed."); - return false; - } - *texts = erealloc(*texts, (ctx->text+1) * sizeof(struct PDFText *)); - (*texts)[ctx->text] = pdf_text_new(); - (*texts)[ctx->text]->text = strdup(page_no); - (*texts)[ctx->text]->style = style; - (*texts)[ctx->text]->x = MEDIABOX_WIDTH - MARGIN_HORIZONTAL / 2 - width; - (*texts)[ctx->text]->y = ctx->margin_bottom / 2; - (*texts)[ctx->text]->width = width; - switch (g_config->output->page_no->align) { - case A_LEFT: - x = MARGIN_HORIZONTAL / 2; - break; - case A_CENTER: - x = MARGIN_HORIZONTAL + (LINE_WIDTH - width) / 2; - break; - case A_RIGHT: - x = MEDIABOX_WIDTH - MARGIN_HORIZONTAL / 2 - width; - break; - default: - util_log(LOG_ERR, "Invalid Alignment enum value '%d'.", g_config->output->page_no->align); - return false; - } - (*texts)[ctx->text]->x = x; - ctx->text++; - return true; -} - -static bool -pdf_page_close_then_add(struct PDFContext *ctx, enum NumeralSystem numeral_system) -{ - ctx->content->pages[ctx->page]->texts = erealloc( - ctx->content->pages[ctx->page]->texts, - (ctx->text+1) * sizeof(struct PDFText *) - ); - ctx->content->pages[ctx->page]->texts[ctx->text] = NULL; - ctx->content->pages[ctx->page]->images = erealloc( - ctx->content->pages[ctx->page]->images, - (ctx->image+1) * sizeof(struct PDFImage *) - ); - ctx->content->pages[ctx->page]->images[ctx->image] = NULL; - ctx->content->pages[ctx->page]->diagrams = erealloc( - ctx->content->pages[ctx->page]->diagrams, - (ctx->diagram+1) * sizeof(struct ChordDiagram *) - ); - ctx->content->pages[ctx->page]->diagrams[ctx->diagram] = NULL; - ctx->text = 0; - ctx->image = 0; - ctx->diagram = 0; - ctx->page++; - ctx->content->pages = erealloc(ctx->content->pages, (ctx->page+1) * sizeof(struct PDFPage *)); - ctx->content->pages[ctx->page] = pdf_page_new(); - ctx->y = MEDIABOX_HEIGHT - MARGIN_TOP; - if (g_config->output->page_no->show) { - if (!pdf_page_add_page_no(ctx, numeral_system)) { - LOG_DEBUG("pdf_page_add_page_no failed."); - return false; - } - } - return true; -} - -static bool -pdf_texts_add_text( - struct PDFContext *ctx, - const char *text, - struct ChoStyle *style, - enum Alignment align, - enum NumeralSystem numeral_system -) -{ - 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 == ERROR) { - LOG_DEBUG("text_width failed."); - return false; - } - if (width > LINE_WIDTH) { - char *t = (char *)&str; - while (width > LINE_WIDTH) { - if (ctx->y < ctx->margin_bottom) { - if (!pdf_page_close_then_add(ctx, numeral_system)) { - LOG_DEBUG("pdf_page_close_then_add failed."); - return false; - } - texts = &ctx->content->pages[ctx->page]->texts; - } - index = text_find_fitting(t, style, 0.0, LINE_WIDTH); - if (index == EMPTY_INT) { - LOG_DEBUG("text_find_fitting failed."); - return false; - } - t[index] = 0; - width = text_width(t, style); - if (width == ERROR) { - LOG_DEBUG("text_width failed."); - return false; - } - ctx->x = calc_x(width, align); - *texts = erealloc(*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; - (*texts)[ctx->text]->width = width; - if (style->href) { - if (!annot_url_link_add(ctx, style, width)) { - LOG_DEBUG("annot_url_link_add failed."); - return false; - } - } - ctx->text++; - ctx->y -= 8.0 + style->font->size; - t += index+1; - width = text_width(t, style); - if (width == ERROR) { - LOG_DEBUG("text_width failed."); - return false; - } - } - width = text_width(t, style); - if (width == ERROR) { - LOG_DEBUG("text_width failed."); - return false; - } - ctx->x = calc_x(width, align); - *texts = erealloc(*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; - (*texts)[ctx->text]->width = width; - if (style->href) { - if (!annot_url_link_add(ctx, style, width)) { - LOG_DEBUG("annot_url_link_add failed."); - return false; - } - } - ctx->text++; - ctx->y -= 8.0 + style->font->size; - } else { - if (ctx->y < ctx->margin_bottom) { - if (!pdf_page_close_then_add(ctx, numeral_system)) { - LOG_DEBUG("pdf_page_close_then_add failed."); - return false; - } - texts = &ctx->content->pages[ctx->page]->texts; - } - ctx->x = calc_x(width, align); - *texts = erealloc(*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; - (*texts)[ctx->text]->width = width; - if (style->href) { - if (!annot_url_link_add(ctx, style, width)) { - LOG_DEBUG("annot_url_link_add failed."); - return false; - } - } - ctx->text++; - ctx->y -= 8.0 + style->font->size; - } - return true; -} - -static int -pdf_toc_page_count( - struct TocEntry **entries, - struct ChoStyle *style, - double max_title_width -) -{ - int page = 0; - double y = MEDIABOX_HEIGHT - MARGIN_TOP; - double width; - struct TocEntry **toc; - for (toc = entries; *toc; toc++) { - if (y < MARGIN_BOTTOM) { - y = MEDIABOX_HEIGHT - MARGIN_TOP; - page++; - } - int index; - char tmp[strlen((*toc)->title)+1]; - strcpy((char *)&tmp, (*toc)->title); - width = text_width((*toc)->title, style); - if (width == ERROR) { - LOG_DEBUG("text_width failed."); - return -1; - } - width += MARGIN_HORIZONTAL; - if (width > max_title_width) { - char *t = (char *)&tmp; - while (width > max_title_width) { - index = text_find_fitting(t, style, MARGIN_HORIZONTAL, max_title_width); - if (index == EMPTY_INT) { - LOG_DEBUG("text_find_fitting failed."); - return -1; - } - t[index] = 0; - y -= 8.0 + style->font->size; - t += index + 1; - width = text_width(t, style); - if (width == ERROR) { - LOG_DEBUG("text_width failed."); - return -1; - } - } - y -= 8.0 + style->font->size; - } else { - y -= 8.0 + style->font->size; - } - } - return page + 1; -} - -static const char * -toc_dots_create(double available_width, double dot_width) -{ - available_width -= MARGIN_HORIZONTAL; - int dot_count; - static char dots[1024]; - memset(&dots, 0, sizeof(dots)); - dot_count = (int)floor(available_width / dot_width); - if (dot_count > 1023) { - return NULL; - } - memset(&dots, (int)'.', dot_count); - dots[dot_count+1] = 0; - return dots; -} - -static bool -pdf_texts_add_toc_entry( - struct PDFContext *ctx, - struct TocEntry *entry, - struct ChoStyle *style, - double max_song_title_width, - int toc_page_count, - double dot_width -) -{ - struct PDFText ***texts; - texts = &ctx->content->pages[ctx->page]->texts; - double width, page_no_x, end_of_title_x, width_between_title_and_page_no, available_dots_width; - double page_no_width, dots_width; - int index, line_count; - char tmp[strlen(entry->title)+1]; - char page_no[11+1]; - strcpy((char *)&tmp, entry->title); - width = text_width(entry->title, style); - if (width == ERROR) { - LOG_DEBUG("text_width failed."); - return false; - } - if (width+MARGIN_HORIZONTAL > max_song_title_width) { - char *t = (char *)&tmp; - line_count = 0; - while (width+MARGIN_HORIZONTAL > max_song_title_width) { - if (ctx->y < MARGIN_BOTTOM) { - if (!pdf_page_close_then_add(ctx, NUS_ROMAN)) { - LOG_DEBUG("pdf_page_close_then_add failed."); - return false; - } - texts = &ctx->content->pages[ctx->page]->texts; - } - index = text_find_fitting(t, style, MARGIN_HORIZONTAL, max_song_title_width); - if (index == EMPTY_INT) { - LOG_DEBUG("text_find_fitting failed."); - return false; - } - t[index] = 0; - *texts = erealloc(*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 = MARGIN_HORIZONTAL; - (*texts)[ctx->text]->y = ctx->y; - ctx->y -= 8.0 + style->font->size; - line_count++; - width = text_width(t, style); - if (width == ERROR) { - LOG_DEBUG("text_width failed."); - return false; - } - (*texts)[ctx->text]->width = width; - t += index + 1; - width = text_width(t, style); - if (width == ERROR) { - LOG_DEBUG("text_width failed."); - return false; - } - ctx->text++; - } - if (ctx->y < MARGIN_BOTTOM) { - if (!pdf_page_close_then_add(ctx, NUS_ROMAN)) { - LOG_DEBUG("pdf_page_close_then_add failed."); - return false; - } - texts = &ctx->content->pages[ctx->page]->texts; - } - end_of_title_x = width; - *texts = erealloc(*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 = MARGIN_HORIZONTAL; - (*texts)[ctx->text]->y = ctx->y; - (*texts)[ctx->text]->width = width; - ctx->text++; - sprintf((char *)&page_no, "%d", entry->page_index+1); - width = text_width(page_no, style); - if (width == ERROR) { - LOG_DEBUG("text_width failed."); - return false; - } - - page_no_x = MEDIABOX_WIDTH - width - MARGIN_HORIZONTAL; - width_between_title_and_page_no = page_no_x - end_of_title_x; - available_dots_width = width_between_title_and_page_no - TOC_DOTS_GAP_WIDTH*2; - const char *dots = toc_dots_create(available_dots_width, dot_width); - if (!dots) { - LOG_DEBUG("toc_dots_create failed."); - return false; - } - - *texts = erealloc(*texts, (ctx->text+1) * sizeof(struct PDFText *)); - (*texts)[ctx->text] = pdf_text_new(); - (*texts)[ctx->text]->text = strdup(dots); - (*texts)[ctx->text]->style = cho_style_copy(style); - (*texts)[ctx->text]->x = MARGIN_HORIZONTAL + end_of_title_x + TOC_DOTS_GAP_WIDTH; - (*texts)[ctx->text]->y = ctx->y; - ctx->text++; - - *texts = erealloc(*texts, (ctx->text+1) * sizeof(struct PDFText *)); - (*texts)[ctx->text] = pdf_text_new(); - (*texts)[ctx->text]->text = strdup(page_no); - (*texts)[ctx->text]->style = cho_style_copy(style); - (*texts)[ctx->text]->x = page_no_x; - (*texts)[ctx->text]->y = ctx->y; - (*texts)[ctx->text]->width = width; - line_count++; - if (!annot_page_link_add(ctx, entry, toc_page_count, line_count, style->font->size)) { - LOG_DEBUG("annot_page_link_add"); - return false; - } - ctx->text++; - ctx->y -= 8.0 + style->font->size; - } else { - if (ctx->y < MARGIN_BOTTOM) { - if (!pdf_page_close_then_add(ctx, NUS_ROMAN)) { - LOG_DEBUG("pdf_page_close_then_add failed."); - return false; - } - texts = &ctx->content->pages[ctx->page]->texts; - } - end_of_title_x = width; - *texts = erealloc(*texts, (ctx->text+1) * sizeof(struct PDFText *)); - (*texts)[ctx->text] = pdf_text_new(); - (*texts)[ctx->text]->text = strdup(entry->title); - (*texts)[ctx->text]->style = cho_style_copy(style); - (*texts)[ctx->text]->x = MARGIN_HORIZONTAL; - (*texts)[ctx->text]->y = ctx->y; - width = text_width(entry->title, style); - if (width == ERROR) { - LOG_DEBUG("text_width failed."); - return false; - } - (*texts)[ctx->text]->width = width; - ctx->text++; - sprintf((char *)&page_no, "%d", entry->page_index+1); - page_no_width = text_width(page_no, style); - if (page_no_width == ERROR) { - LOG_DEBUG("text_width failed."); - return false; - } - - page_no_x = MEDIABOX_WIDTH - page_no_width - MARGIN_HORIZONTAL; - width_between_title_and_page_no = page_no_x - end_of_title_x; - available_dots_width = width_between_title_and_page_no - TOC_DOTS_GAP_WIDTH*2; - const char *dots = toc_dots_create(available_dots_width, dot_width); - if (!dots) { - LOG_DEBUG("toc_dots_create failed."); - return false; - } - dots_width = text_width(dots, style); - if (dots_width == ERROR) { - LOG_DEBUG("text_width failed."); - return false; - } - - *texts = erealloc(*texts, (ctx->text+1) * sizeof(struct PDFText *)); - (*texts)[ctx->text] = pdf_text_new(); - (*texts)[ctx->text]->text = strdup(dots); - (*texts)[ctx->text]->style = cho_style_copy(style); - (*texts)[ctx->text]->x = MARGIN_HORIZONTAL + end_of_title_x + TOC_DOTS_GAP_WIDTH; - (*texts)[ctx->text]->y = ctx->y; - (*texts)[ctx->text]->width = dots_width; - ctx->text++; - - *texts = erealloc(*texts, (ctx->text+1) * sizeof(struct PDFText *)); - (*texts)[ctx->text] = pdf_text_new(); - (*texts)[ctx->text]->text = strdup(page_no); - (*texts)[ctx->text]->style = cho_style_copy(style); - (*texts)[ctx->text]->x = page_no_x; - (*texts)[ctx->text]->y = ctx->y; - (*texts)[ctx->text]->width = page_no_width; - if (!annot_page_link_add(ctx, entry, toc_page_count, 1, style->font->size)) { - LOG_DEBUG("annot_page_link_add"); - return false; - } - ctx->text++; - ctx->y -= 8.0 + style->font->size; - } - return true; -} - -static bool -pdf_toc_create( - struct PDFContent **toc_content, - struct PDFContent *pdf_content, - struct Config *config -) -{ - double max_song_title_width, dot_width; - int toc_page_count; - struct PDFContext ctx; - struct PDFText ***texts; - struct ChoStyle *toc_style, *title_style; - struct TocEntry **toc; - - pdf_context_init(&ctx); - ctx.content = pdf_content_new(); - ctx.content->pages = emalloc(sizeof(struct PDFPage *)); - ctx.content->pages[ctx.page] = pdf_page_new(); - texts = &ctx.content->pages[ctx.page]->texts; - if (config->output->page_no->show) { - if (!pdf_page_add_page_no(&ctx, NUS_ROMAN)) { - LOG_DEBUG("pdf_page_add_page_no failed."); - return false; - } - } - toc_style = config->output->styles[TT_TOC]; - toc = pdf_content->toc; - max_song_title_width = LINE_WIDTH * 0.85; - toc_page_count = pdf_toc_page_count(toc, toc_style, max_song_title_width); - if (toc_page_count == -1) { - LOG_DEBUG("pdf_toc_page_count failed."); - return false; - } - title_style = config->output->styles[TT_TOC_TITLE]; - if (!pdf_texts_add_text(&ctx, config->output->toc->title, title_style, A_CENTER, NUS_ROMAN)) { - LOG_DEBUG("pdf_texts_add_text failed."); - return false; - } - ctx.y -= 30.0; - dot_width = text_width(".", toc_style); - if (dot_width == ERROR) { - LOG_DEBUG("text_width failed."); - return false; - } - for (; *toc; toc++) { - if (!pdf_texts_add_toc_entry(&ctx, *toc, toc_style, max_song_title_width, toc_page_count, dot_width)) { - LOG_DEBUG("pdf_texts_add_toc_entry failed."); - return false; - } - } - texts = &ctx.content->pages[ctx.page]->texts; - *texts = erealloc(*texts, (ctx.text+1) * sizeof(struct PDFText *)); - (*texts)[ctx.text] = NULL; - ctx.page++; - ctx.content->pages = erealloc(ctx.content->pages, (ctx.page+1) * sizeof(struct PDFPage *)); - ctx.content->pages[ctx.page] = NULL; - *toc_content = ctx.content; - return true; -} - -static bool -pdf_content_create( - struct PDFContent **out, - struct ChoSong **songs, - struct Config *config, - struct Obj **img_objs -) -{ - struct ChoMetadata **m; - struct ChoSection **se; - struct ChoLine **li; - struct PDFText ***texts; - struct PDFImage ***imgs; - struct PDFContext ctx; - struct ChordDiagram ***diagrams, **dgrams, **dgrams_begin; - bool show_diagram = config->output->diagram->show; - bool start_song_on_new_page = config->output->start_song_on_new_page; - double width, height; - - pdf_context_init(&ctx); - if (show_diagram) { - ctx.margin_bottom = 150.0; - } - ctx.content = pdf_content_new(); - ctx.content->pages = emalloc(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; - diagrams = &ctx.content->pages[ctx.page]->diagrams; - if (config->output->page_no->show) { - if (!pdf_page_add_page_no(&ctx, NUS_WESTERN_ARABIC)) { - LOG_DEBUG("pdf_page_add_page_no failed."); - return false; - } - } - int s; - for (s = 0; songs[s]; s++) { - if (show_diagram) { - struct ChoChord **chords = NULL; - if (!pdf_get_chords(songs[s], &chords)) { - LOG_DEBUG("pdf_get_chords failed."); - return false; - } - if (chords) { - qsort(chords, cho_chord_count(chords), sizeof(struct ChoChord *), cho_chord_compare); - dgrams = chord_diagrams_create(config, &chords, songs[s]->diagrams); - dgrams_begin = dgrams; - while (*dgrams) { - // debug_chord_diagram_print(*dgrams); - *diagrams = erealloc(*diagrams, (ctx.diagram+1) * sizeof(struct ChordDiagram *)); - (*diagrams)[ctx.diagram] = *dgrams; - ctx.diagram++; - dgrams++; - } - free(dgrams_begin); - } - cho_chords_free(chords); - } - for (m = songs[s]->metadata; *m; m++) { - if (!strcmp((*m)->name, "title")) { - ctx.content->toc = erealloc(ctx.content->toc, (ctx.toc_entry+1) * sizeof(struct TocEntry *)); - ctx.content->toc[ctx.toc_entry] = emalloc(sizeof(struct TocEntry)); - ctx.content->toc[ctx.toc_entry]->title = strdup((*m)->value); - ctx.content->toc[ctx.toc_entry]->page_index = ctx.page; - ctx.content->toc[ctx.toc_entry]->page_y = ctx.y; - ctx.toc_entry++; - if (!pdf_texts_add_text(&ctx, (*m)->value, (*m)->style, A_CENTER, NUS_WESTERN_ARABIC)) { - LOG_DEBUG("pdf_texts_add_text failed."); - return false; - } - texts = &ctx.content->pages[ctx.page]->texts; - imgs = &ctx.content->pages[ctx.page]->images; - diagrams = &ctx.content->pages[ctx.page]->diagrams; - } - } - for (m = songs[s]->metadata; *m; 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 ChoStyle *output_style; - output_style = config->output->styles[TT_SUBTITLE]; - if (!pdf_texts_add_text(&ctx, (*m)->value, output_style, A_CENTER, NUS_WESTERN_ARABIC)) { - LOG_DEBUG("pdf_texts_add_text failed."); - return false; - } - texts = &ctx.content->pages[ctx.page]->texts; - imgs = &ctx.content->pages[ctx.page]->images; - diagrams = &ctx.content->pages[ctx.page]->diagrams; - } - } - ctx.y -= 30.0; - for (se = songs[s]->sections; *se; se++) { - if ((*se)->label) { - if (!pdf_texts_add_text(&ctx, (*se)->label->text, (*se)->label->style, A_LEFT, NUS_WESTERN_ARABIC)) { - LOG_DEBUG("pdf_texts_add_text failed."); - return false; - } - texts = &ctx.content->pages[ctx.page]->texts; - imgs = &ctx.content->pages[ctx.page]->images; - diagrams = &ctx.content->pages[ctx.page]->diagrams; - } - for (li = (*se)->lines; *li; 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 ChoStyle *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; - } - } - bool text_above_exist; - 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 == ERROR) { - LOG_DEBUG("line_width_until_text_aboave failed."); - return false; - } - ctx.x = MARGIN_HORIZONTAL + width + ctx.prev_added_space; - struct SpaceNeeded **sp; - for (sp = ctx.spaces; *sp; sp++) { - if ((*sp)->text_above_index == i) { - ctx.x += (*sp)->amount; - ctx.prev_added_space += (*sp)->amount; - } - } - *texts = erealloc(*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; - (*texts)[ctx.text]->width = text_width(string, style); - if ((*texts)[ctx.text]->width == ERROR) { - LOG_DEBUG("text_width failed."); - return false; - } - if (style->href) { - if (!annot_url_link_add(&ctx, style, (*texts)[ctx.text]->width)) { - LOG_DEBUG("annot_url_link_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 == ERROR) { - LOG_DEBUG("images_find_biggest_height failed."); - return false; - } - text_above_exist = i > 0; - if (text_above_exist) { - 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 < ctx.margin_bottom) { - struct PDFText **tmp = NULL; - int tm = 0; - ctx.y = MEDIABOX_HEIGHT - MARGIN_TOP; - if (text_above_exist) { - /* INFO: chords/annotations and their corresponding lyrics won't be splitted */ - double prev_y = (*texts)[ctx.text-1]->y; - for (int p = ctx.text-1; prev_y == (*texts)[p]->y; p--) { - (*texts)[p]->y = ctx.y; - tmp = erealloc(tmp, (tm+1) * sizeof(struct PDFText *)); - tmp[tm] = (*texts)[p]; - tm++; - *texts = erealloc(*texts, (--ctx.text) * sizeof(struct PDFText *)); - } - } - if (!pdf_page_close_then_add(&ctx, NUS_WESTERN_ARABIC)) { - LOG_DEBUG("pdf_page_close_then_add failed."); - return false; - } - texts = &ctx.content->pages[ctx.page]->texts; - imgs = &ctx.content->pages[ctx.page]->images; - diagrams = &ctx.content->pages[ctx.page]->diagrams; - if (text_above_exist) { - for (int i=0; i<tm; i++) { - *texts = erealloc(*texts, (ctx.text+1) * sizeof(struct PDFText *)); - (*texts)[ctx.text] = tmp[i]; - ctx.text++; - } - free(tmp); - } - if (text_above_exist) { - 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) { - if (!pdf_texts_add_lyrics(*left_items, &ctx, i)) { - LOG_DEBUG("pdf_texts_add_lyrics failed."); - return false; - } - texts = &ctx.content->pages[ctx.page]->texts; - imgs = &ctx.content->pages[ctx.page]->images; - diagrams = &ctx.content->pages[ctx.page]->diagrams; - } else { - *imgs = erealloc(*imgs, (ctx.image+1) * sizeof(struct PDFImage *)); - (*imgs)[ctx.image] = pdf_image_new(); - (*imgs)[ctx.image]->name = image_name((*left_items)->u.image); - if (!(*imgs)[ctx.image]->name) { - LOG_DEBUG("image_name failed."); - return false; - } - (*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]); - if (!pdf_texts_add_lyrics(*left_items, &ctx, i)) { - LOG_DEBUG("pdf_texts_add_lyrics failed."); - return false; - } - free((*left_items)->u.text->text); - (*left_items)->u.text->text = tmp; - texts = &ctx.content->pages[ctx.page]->texts; - imgs = &ctx.content->pages[ctx.page]->images; - diagrams = &ctx.content->pages[ctx.page]->diagrams; - } - } else - if (pos->line_item_index != -1 && pos->text_index == -1) { - if (!(*left_items)->is_text) { - *imgs = erealloc(*imgs, (ctx.image+1) * sizeof(struct PDFImage *)); - (*imgs)[ctx.image] = pdf_image_new(); - (*imgs)[ctx.image]->name = image_name((*left_items)->u.image); - if (!(*imgs)[ctx.image]->name) { - LOG_DEBUG("image_name failed."); - return false; - } - (*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 < ctx.margin_bottom) { - if (!pdf_page_close_then_add(&ctx, NUS_WESTERN_ARABIC)) { - LOG_DEBUG("pdf_page_close_then_add failed."); - return false; - } - texts = &ctx.content->pages[ctx.page]->texts; - imgs = &ctx.content->pages[ctx.page]->images; - diagrams = &ctx.content->pages[ctx.page]->diagrams; - } - 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) { - if (!pdf_page_close_then_add(&ctx, NUS_WESTERN_ARABIC)) { - LOG_DEBUG("pdf_page_close_then_add failed."); - return false; - } - texts = &ctx.content->pages[ctx.page]->texts; - imgs = &ctx.content->pages[ctx.page]->images; - diagrams = &ctx.content->pages[ctx.page]->diagrams; - } - } - ctx.y -= SECTION_GAP_WIDTH; - } - if (start_song_on_new_page) { - if (!pdf_page_close_then_add(&ctx, NUS_WESTERN_ARABIC)) { - LOG_DEBUG("pdf_page_close_then_add failed."); - return false; - } - texts = &ctx.content->pages[ctx.page]->texts; - imgs = &ctx.content->pages[ctx.page]->images; - diagrams = &ctx.content->pages[ctx.page]->diagrams; - } - } - *texts = erealloc(*texts, (ctx.text+1) * sizeof(struct PDFText *)); - (*texts)[ctx.text] = NULL; - *imgs = erealloc(*imgs, (ctx.image+1) * sizeof(struct PDFImage *)); - (*imgs)[ctx.image] = NULL; - *diagrams = erealloc(*diagrams, (ctx.diagram+1) * sizeof(struct ChordDiagram *)); - (*diagrams)[ctx.diagram] = NULL; - if (start_song_on_new_page) { - pdf_page_free(ctx.content->pages[ctx.page]); - ctx.content->pages[ctx.page] = NULL; - } else { - ctx.page++; - ctx.content->pages = erealloc(ctx.content->pages, (ctx.page+1) * sizeof(struct PDFPage *)); - ctx.content->pages[ctx.page] = NULL; - } - ctx.content->toc = erealloc(ctx.content->toc, (ctx.toc_entry+1) * sizeof(struct TocEntry *)); - ctx.content->toc[ctx.toc_entry] = NULL; - *out = ctx.content; - return true; -} - -static bool -pdf_toc_render(struct PDFContent *content, pdfio_file_t *file) -{ - struct PDFPage **pages; - struct PDFText **texts; - pdfio_stream_t *stream; - pages = content->pages; - int p; - for (p = 0; pages[p]; p++) { - g_current_page_index = p; - stream = pdf_page_create(file, NULL, pages[p]->annots); - if (!stream) { - LOG_DEBUG("pdf_page_create failed."); - return false; - } - for (texts = pages[p]->texts; *texts; texts++) { - if (!pdf_text_show(stream, *texts)) { - LOG_DEBUG("pdf_text_show failed."); - return false; - } - } - if (!pdfioStreamClose(stream)) { - LOG_DEBUG("pdfioStreamClose failed."); - return false; - } - } - return true; -} - -static bool -pdf_content_render(struct PDFContent *content, pdfio_file_t *file) -{ - int p; - struct PDFPage **pages; - struct PDFText **texts; - struct PDFImage **imgs; - pdfio_stream_t *stream; - pages = content->pages; - for (p = 0; pages[p]; p++) { - g_current_page_index = p; - stream = pdf_page_create(file, pages[p]->images, pages[p]->annots); - if (!stream) { - LOG_DEBUG("pdf_page_create failed."); - return false; - } - for (texts = pages[p]->texts; *texts; texts++) { - if (!pdf_text_show(stream, *texts)) { - LOG_DEBUG("pdf_text_show failed."); - return false; - } - } - for (imgs = pages[p]->images; *imgs; imgs++) { - if ( - !pdfioContentDrawImage( - stream, - (*imgs)->name, - (*imgs)->x, - (*imgs)->y, - (*imgs)->width, - (*imgs)->height - ) - ) { - LOG_DEBUG("pdfioContentDrawImage failed."); - return false; - } - } - if (pages[p]->diagrams) { - double x = MARGIN_HORIZONTAL; - double y = 40.0; - double size = 50.0; - double padding = 30.0; - struct ChordDiagram **d; - /* TODO: Handle line break when too long */ - for (d = pages[p]->diagrams; *d; d++) { - debug_chord_diagram_print(*d); - if ((*d)->show) { - if (!chord_diagram_draw(stream, *d, x, y, size)) { - LOG_DEBUG("chord_diagram_draw failed."); - return false; - } - x += size + padding; - } - } - } - if (!pdfioStreamClose(stream)) { - LOG_DEBUG("pdfioStreamClose failed."); - return false; - } - } - return true; -} - -char * -out_pdf_create( - const char *cho_filepath, - const char *output_folder_or_file, - struct ChoSong **songs, - struct Config *config -) -{ - struct Font **needed_fonts; - struct Obj **img_objs = NULL; - struct PDFContent *pdf_content = NULL; - struct PDFContent *toc_content = NULL; - pdfio_rect_t media_box_a4 = { 0.0, 0.0, MEDIABOX_WIDTH, MEDIABOX_HEIGHT }; - pdfio_rect_t crop_box = { 0.0, 0.0, MEDIABOX_WIDTH, MEDIABOX_HEIGHT }; - char *dirpath, *pdf_filepath; - - g_config = config; - - memset(&g_current_font_name, 0, sizeof(g_current_font_name)); - memset(&g_cho_dirpath, 0, PATH_MAX); - - if (cho_filepath) { - dirpath = filepath_dirname(cho_filepath); - } else { - dirpath = getcwd(NULL, 0); - } - strcpy((char *)&g_cho_dirpath, dirpath); - free(dirpath); - pdf_filepath = pdf_filepath_create(songs, cho_filepath, output_folder_or_file); - if (!pdf_filepath) { - LOG_DEBUG("pdf_filepath_create failed."); - return NULL; - } - g_pdf_file = pdfioFileCreate(pdf_filepath, "2.0", &media_box_a4, &crop_box, NULL, NULL); - if (!g_pdf_file) { - LOG_DEBUG("pdfioFileCreate failed."); - return NULL; - } - if (!pdf_set_title(g_pdf_file, songs)) { - LOG_DEBUG("pdf_set_title failed."); - goto CLEAN; - } - needed_fonts = fonts_get_all(songs, config); - if (!pdf_load_fonts(needed_fonts, config)) { - LOG_DEBUG("pdf_load_fonts failed."); - goto CLEAN; - } - cho_fonts_free(needed_fonts); - if (!pdf_load_images(&img_objs, g_pdf_file, songs)) { - LOG_DEBUG("pdf_load_images failed."); - goto CLEAN; - } - if (!pdf_content_create(&pdf_content, songs, config, img_objs)) { - LOG_DEBUG("pdf_content_create failed."); - goto CLEAN; - } - if (config->output->toc->show) { - if (!pdf_toc_create(&toc_content, pdf_content, config)) { - LOG_DEBUG("pdf_toc_create failed."); - goto CLEAN; - } - if (!pdf_toc_render(toc_content, g_pdf_file)) { - LOG_DEBUG("pdf_toc_render failed."); - goto CLEAN; - } - } - if (!pdf_content_render(pdf_content, g_pdf_file)) { - LOG_DEBUG("pdf_content_render failed."); - goto CLEAN; - } - objs_free(img_objs); - pdf_content_free(toc_content); - pdf_content_free(pdf_content); - if (!pdfioFileClose(g_pdf_file)) { - LOG_DEBUG("pdfioFileClose failed."); - goto CLEAN; - } - g_pdf_file = NULL; - objs_free(g_fonts); - g_fonts = NULL; - g_current_font_size = 0.0; - g_current_page_index = 0; - g_config = NULL; - return pdf_filepath; - CLEAN: - if (unlink(pdf_filepath)) { - LOG_DEBUG("unlink failed."); - } - return NULL; -} diff --git a/out_pdf.h b/out_pdf.h @@ -1,103 +0,0 @@ -#include <pdfio.h> -#include "types.h" -#include "chordpro.h" - -#define MEDIABOX_HEIGHT 841.995 -#define MEDIABOX_WIDTH 595.35 -#define MARGIN_TOP 50.0 -#define MARGIN_BOTTOM 50.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 TOC_DOTS_GAP_WIDTH 10.0 - -#define PATH_MAX 4096 - -enum LineLocation { - LL_OVER, - LL_STRIKETHROUGH, - LL_UNDER -}; - -enum FontType { - FT_TTF, - FT_OTF -}; - -enum NumeralSystem { - NUS_WESTERN_ARABIC, - NUS_ROMAN -}; - -struct CharPosition { - int line_item_index; - int text_index; -}; - -struct Obj { - char *name; - pdfio_obj_t *value; -}; - -struct PDFText { - char *text; - struct ChoStyle *style; - double x; - double y; - double width; -}; - -struct PDFImage { - double x; - double y; - double width; - double height; - char *name; - pdfio_obj_t *obj; -}; - -struct PDFPage { - struct PDFText **texts; - struct PDFImage **images; - pdfio_array_t *annots; - struct ChordDiagram **diagrams; -}; - -struct TocEntry { - int page_index; - double page_y; - char *title; -}; - -struct PDFContent { - struct PDFPage **pages; - struct TocEntry **toc; -}; - -struct SpaceNeeded { - int line_item_index; - int text_index; - int text_above_index; - double amount; -}; - -struct PDFContext { - struct PDFContent *content; - double x; - double y; - int text; - int image; - int diagram; - int page; - int toc_entry; - struct SpaceNeeded **spaces; - double biggest_font_size; - size_t consumed_lyrics; - double prev_added_space; - double margin_bottom; -}; - -char *out_pdf_create(const char *cho_filename, const char *output_folder_or_file, struct ChoSong **songs, struct Config *config); -pdfio_obj_t *out_pdf_fnt_obj_get_by_name(const char *name); diff --git a/types.h b/types.h @@ -1,338 +0,0 @@ -#include <stdint.h> - -#ifndef _TYPES_H_ -#define _TYPES_H_ - -enum TextType : int8_t { - TT_CHORD, - TT_ANNOT, - TT_CHORUS, - TT_FOOTER, - TT_GRID, - TT_TAB, - TT_TOC, - TT_TOC_TITLE, - TT_TEXT, - TT_TITLE, - TT_SUBTITLE, - TT_LABEL, - TT_COMMENT, - TT_COMMENT_ITALIC, - TT_COMMENT_BOX, - TT_LENGTH -}; - -enum Alignment : int8_t { - A_LEFT, - A_CENTER, - A_RIGHT -}; - -enum Anchor { - AN_PAPER, - AN_PAGE, - AN_COLUMN, - AN_LINE, - AN_FLOAT -}; - -enum BreakType : int8_t { - BT_LINE, - BT_PAGE, - BT_COLUMN -}; - -enum ChordDiagramContent : int8_t { - CDC_STRING, - CDC_KEYBOARD -}; - -enum ChordQualifier { - CQ_MIN, - CQ_MAJ, - CQ_AUG, - CQ_DIM -}; - -enum SectionType { - ST_NEWSONG, - ST_CHORUS, - ST_VERSE, - ST_BRIDGE, - ST_TAB, - ST_GRID, - ST_CUSTOM -}; - -enum SizeType { - ST_POINT, - ST_PERCENT, - ST_EM, - ST_EX -}; - -enum FontFamily : int8_t { - FF_NORMAL, - FF_SANS, - FF_SERIF, - FF_MONOSPACE -}; - -enum FontStyle : int8_t { - FS_ROMAN, - FS_OBLIQUE, - FS_ITALIC -}; - -enum FontWeight : int8_t { - FW_REGULAR, - FW_BOLD -}; - -enum LineStyle : int8_t { - LS_SINGLE, - LS_DOUBLE, - LS_NONE -}; - -enum NoteType { - NT_NOTE, - NT_SHARP, - NT_FLAT -}; - -struct FontPresence { - bool name; - bool family; - bool style; - bool weight; - bool size; -}; - -struct ChoStylePresence { - struct FontPresence font; - bool foreground_color; - bool background_color; - bool underline_style; - bool underline_color; - bool overline_style; - bool overline_color; - bool strikethrough; - bool strikethrough_color; - bool boxed; - bool boxed_color; - bool rise; - bool href; -}; - -struct Font { - char *name; - enum FontFamily family; - enum FontStyle style; - enum FontWeight weight; - double size; -}; - -struct RGBColor { - uint8_t red; - uint8_t green; - uint8_t blue; -}; - -struct ChoStyle { - struct Font *font; - struct RGBColor *foreground_color; - struct RGBColor *background_color; - enum LineStyle underline_style; - struct RGBColor *underline_color; - enum LineStyle overline_style; - struct RGBColor *overline_color; - bool strikethrough; - struct RGBColor *strikethrough_color; - bool boxed; - struct RGBColor *boxed_color; - double rise; - char *href; -}; - -struct ChoChord { - struct ChoStyle *style; - bool is_canonical; - char *name; - char *root; - enum ChordQualifier qual; - char *ext; - char *bass; -}; - -struct ChoText { - struct ChoStyle *style; - char *text; -}; - -struct ChoLineItem { - bool is_text; - union { - struct ChoImage *image; - struct ChoText *text; - } u; -}; - -struct ChoLineItemAbove { - int position; - bool is_chord; - union { - struct ChoChord *chord; - struct ChoText *annot; - } u; -}; - -struct ChoLine { - struct ChoLineItemAbove **text_above; - struct ChoLineItem **items; - enum BreakType btype; -}; - -struct ChoMetadata { - char *name; - char *value; - struct ChoStyle *style; -}; - -struct ChoSection { - enum SectionType type; - struct ChoText *label; - struct ChoLine **lines; -}; - -struct ChoSong { - struct ChoMetadata **metadata; - struct ChoSection **sections; - struct ChordDiagram **diagrams; - bool present_text_types[TT_LENGTH]; -}; - -struct ChoImage { - bool is_asset; - char *id; - char *src; - struct Size *width; - struct Size *height; - struct Size *width_scale; - struct Size *height_scale; - enum Alignment align; - double border; - struct Size *spread_space; - char *href; - struct Size *x; - struct Size *y; - enum Anchor anchor; - struct Size *dx; - struct Size *dy; - struct Size *w; - struct Size *h; - bool bbox; -}; - -struct Size { - enum SizeType type; - double d; -}; - -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 show; - struct RGBColor *color; - bool is_string_instrument; - union { - struct StringDiagram *sd; - struct KeyboardDiagram *kd; - } u; -}; - -enum NotationSystem { - NS_COMMON, - NS_GERMAN, - NS_SCANDINAVIAN, - NS_LATIN, - NS_ROMAN, - NS_NASHVILLE, - NS_CUSTOM -}; - -enum ParseMode { - PM_STRICT, - PM_RELAXED -}; - -enum Instrument : int8_t { - INS_GUITAR, - INS_KEYBOARD, - INS_MANDOLIN, - INS_UKULELE -}; - -struct Note { - char *note; - char *sharp; - char *flat; -}; - -struct ConfigChords { - enum NotationSystem notation_system; - enum ParseMode mode; -}; - -struct ConfigChorus { - char *label; - bool quote; -}; - -struct ConfigParser { - struct ConfigChords *chords; - struct Note **notes; -}; - -struct ConfigChordDiagram { - bool show; - enum Instrument instrument; -}; - -struct ConfigToc { - bool show; - char *title; -}; - -struct ConfigPageNo { - bool show; - enum Alignment align; -}; - -struct ConfigOutput { - struct ConfigChorus *chorus; - struct ConfigToc *toc; - struct ConfigChordDiagram *diagram; - struct ConfigPageNo *page_no; - struct ChoStyle **styles; // TODO: Make array of size 7 - struct Note **notes; - enum NotationSystem notation_system; - bool start_song_on_new_page; -}; - -struct Config { - struct ConfigOutput *output; - struct ConfigParser *parser; -}; - -#endif /* _TYPES_H_ */ diff --git a/util.c b/util.c @@ -1,502 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <stdbool.h> -#include <stdarg.h> -#include <ctype.h> -#include <string.h> -#include <sys/stat.h> -#include <errno.h> -#include <assert.h> -#include <limits.h> -#include "types.h" -#include "util.h" - -static bool g_show_info_logs = false; - -void -util_log_enable_info_logs(void) -{ - g_show_info_logs = true; -} - -void * -emalloc(size_t size) -{ - void *ptr = malloc(size); - if (!ptr) { - perror("malloc failed"); - exit(1); - } - return ptr; -} - -void * -erealloc(void *ptr, size_t size) -{ - void *tmp = realloc(ptr, size); - if (!tmp) { - perror("realloc failed"); - exit(1); - } - return tmp; -} - -void -util_log(enum LogLevel level, const char *msg, ...) -{ - if (level == LOG_INFO && !g_show_info_logs) { - return; - } -#if COLOR == 1 - const char *log_level = ""; - const char *color = ""; - switch (level) { - case LOG_INFO: - log_level = "INFO"; - color = "37"; - break; - case LOG_WARN: - log_level = "WARN"; - color = "33"; - break; - case LOG_ERR: - 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; - va_start(va, msg); - vfprintf(stderr, msg, va); - fprintf(stderr, "\n"); -#else - const char *log_level = ""; - switch (level) { - case LOG_INFO: - log_level = "INFO"; - break; - case LOG_WARN: - log_level = "WARN"; - break; - case LOG_ERR: - log_level = " ERR"; - break; - case LOG_TODO: - log_level = "TODO"; - break; - } - fprintf(stderr, "%s: ", log_level); - va_list va; - va_start(va, msg); - vfprintf(stderr, msg, va); - fprintf(stderr, "\n"); -#endif -} - -bool -str_starts_with(const char *str, const char *part) -{ - unsigned int i; - size_t part_len = strlen(part); - if (part_len > strlen(str)) - return false; - for (i=0; i<part_len; i++) { - if (str[i] != part[i]) - return false; - } - return true; -} - -char * -str_normalize(const char *str) -{ - char *normalized = NULL; - char c; - int n = 0; - int i; - for (i = 0; str[i]; i++) { - if (str[i] == ' ' || str[i] == '/' || str[i] == '.') { - normalized = erealloc(normalized, (n+1) * sizeof(char)); - normalized[n] = '-'; - n++; - continue; - } - if (str[i] == '\'' || str[i] == ',') - continue; - c = (char)tolower(str[i]); - normalized = erealloc(normalized, (n+1) * sizeof(char)); - normalized[n] = c; - n++; - } - normalized = erealloc(normalized, (n+1) * sizeof(char)); - normalized[n] = 0; - return normalized; -} - -char * -str_trim(const char *str) -{ - char *trimmed = NULL; - int begin = 0; - int end = 0; - int len = (int)strlen(str); - for (int i=0; i<len; i++) { - if ( - str[i] == ' ' || - str[i] == '\n' || - str[i] == '\t' || - str[i] == '\r' - ) - begin++; - else - break; - } - for (int i=len-1; i>=0; i--) { - if ( - str[i] == ' '|| - str[i] == '\n' || - str[i] == '\t' || - str[i] == '\r' - ) - end++; - else - break; - } - int k = 0; - for (int i=0; i<len; i++) { - if (i >= begin && i < len - end) { - trimmed = erealloc(trimmed, (k+1) * sizeof(char)); - trimmed[k] = str[i]; - k++; - } - } - trimmed = erealloc(trimmed, (k+1) * sizeof(char)); - trimmed[k] = 0; - return trimmed; -} - -char * -str_remove_leading_whitespace(const char *str) -{ - int i = 0; - while (str[i] == ' ' || str[i] == '\t') { - i++; - } - return strdup(&str[i]); -} - -int -str_compare(const char *a, const char *b) -{ - if (a && b) { - return strcmp(a, b); - } else - if (!a && !b) { - return 0; - } else - if (a && !b) { - return 1; - } else - if (!a && b) { - return -1; - } - assert(false); -} - -bool -strs_has(char **strs, const char *str) -{ - if (!strs) { - return false; - } - char **s; - for (s = strs; *s; s++) { - if (!strcmp(*s, str)) { - return true; - } - } - return false; -} - -void -strs_add(char ***strs, const char *str) -{ - int i = 0; - if (*strs) { - char **s; - for (s = *strs; *s; s++, i++); - } - *strs = erealloc(*strs, (i+2) * sizeof(char *)); - (*strs)[i] = strdup(str); - (*strs)[i+1] = NULL; -} - -void -strs_free(char **strs) -{ - if (!strs) { - return; - } - char **s; - for (s = strs; *s; s++) { - free(*s); - } - free(strs); -} - -long -str_to_number(const char *str) -{ - long n; - char *endptr; - n = strtol(str, &endptr, 10); - if (str == endptr) { - return -1; - } - if ((n == LONG_MIN || n == LONG_MAX) && errno == ERANGE) { - return -1; - } - return n; -} - -enum FileType -file_type(const char *path) -{ - struct stat s; - if (stat(path, &s) != 0) { - return F_ERROR; - } - if (S_ISDIR(s.st_mode)) { - return F_FOLDER; - } - if (S_ISREG(s.st_mode)) { - return F_REG_FILE; - } - return F_OTHER; -} - -char * -file_read(const char *filepath) -{ - char *str = NULL; - char buf; - size_t read; - int i = 0; - FILE *fp = fopen(filepath, "r"); - if (!fp) { - LOG_DEBUG("fopen failed."); - return NULL; - } - while (1) { - read = fread(&buf, 1, 1, fp); - if (read == 1) { - str = erealloc(str, (i+1) * sizeof(char)); - str[i] = buf; - i++; - } else { - str = erealloc(str, (i+1) * sizeof(char)); - str[i] = 0; - break; - } - } - fclose(fp); - return str; -} - -char * -file_extension_replace_or_add(const char *filepath, const char *extension) -{ - size_t extension_len = strlen(extension); - char *new = NULL; - int mark = -1; - int i, k; - int path_len; - for (i = 0; filepath[i]; i++) { - if (filepath[i] == '.') { - mark = i; - } - } - if (mark == -1) { - path_len = (int)strlen(filepath); - new = emalloc((path_len+2+extension_len) * sizeof(char)); - for (i = 0; i < path_len; i++) { - new[i] = filepath[i]; - } - new[i] = '.'; - i++; - for (k = 0; extension[k]; k++, i++) { - new[i] = extension[k]; - } - new[i] = 0; - } else { - new = emalloc((mark+2+extension_len) * sizeof(char)); - for (i = 0; i <= mark; i++) { - new[i] = filepath[i]; - } - for (k = 0; extension[k]; k++, i++) { - new[i] = extension[k]; - } - new[i] = 0; - } - return new; -} - -bool -file_extension_equals(const char *filepath, const char *extension) -{ - int mark = -1; - int i; - for (i = strlen(filepath)-1; i >= 0; i--) { - if (filepath[i] == '.') { - mark = i; - break; - } - } - if (!strcmp(&filepath[mark+1], extension)) { - return true; - } - return false; -} - -char * -filepath_add_ending_slash_if_missing(const char *path) -{ - size_t len = strlen(path); - if (path[len-1] == '/') { - return strdup(path); - } else { - char *path_with_slash = emalloc((len+2) * sizeof(char)); - strcpy(path_with_slash, path); - path_with_slash[len] = '/'; - path_with_slash[len+1] = 0; - return path_with_slash; - } -} - -char * -filepath_basename(const char *path) -{ - int begin = 0; - int i; - for (i = 0; path[i]; i++) { - if (path[i] == '/') { - begin = i+1; - } - } - return strdup(&path[begin]); -} - -char * -filepath_dirname(const char *path) -{ - char *dirname; - int i, end = 0; - for (i = 0; path[i]; i++) { - if (path[i] == '/') { - end = i; - } - } - if (end == 0) { - dirname = emalloc(2 * sizeof(char)); - dirname[0] = '.'; - dirname[1] = 0; - return dirname; - } - dirname = emalloc((end+1)* sizeof(char)); - for (i = 0; i < end; i++) { - dirname[i] = path[i]; - } - dirname[i] = 0; - return dirname; -} - -char * -filepath_resolve_tilde(const char *path) -{ - char *home; - char *str = NULL; - if (*path == '~') { - home = getenv("HOME"); - if (!home) { - LOG_DEBUG("getenv failed."); - return NULL; - } - str = erealloc(str, (strlen(home)+strlen(path)) * sizeof(char)); - strcpy(str, home); - strcat(str, &path[1]); - return str; - } else { - return strdup(path); - } -} - -static const char * -size_type_to_string(enum SizeType type) -{ - switch (type) { - case ST_PERCENT: - return "%"; - case ST_EM: - return "em"; - case ST_EX: - return "ex"; - default: - return ""; - } -} - -struct Size * -size_create(const char *str) -{ - size_t len = strlen(str); - struct Size *size = emalloc(sizeof(struct Size)); - char *endptr; - double d; - d = strtod(str, &endptr); - if (str == endptr || errno == ERANGE) { - LOG_DEBUG("strtod failed."); - return NULL; - } - size->d = d; - size->type = ST_POINT; - if (len > 1 && str[len-1] == '%') { - if (size->d < 1.0 || size->d > 100.0) { - 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') { - size->type = ST_EM; - } else - if (len > 2 && str[len-2] == 'e' && str[len-1] == 'x') { - size->type = ST_EX; - } - return size; -} - -struct Size * -size_copy(struct Size *size) -{ - struct Size *copy = emalloc(sizeof(struct Size)); - copy->type = size->type; - copy->d = size->d; - return copy; -} - -const char * -size_to_string(struct Size *size) -{ - static char str[10+1]; - if (size->d > 999999) { - sprintf((char *)&str, ">999.999"); - } else { - sprintf((char *)&str, "%.1f%s", size->d, size_type_to_string(size->type)); - } - return str; -} diff --git a/util.h b/util.h @@ -1,59 +0,0 @@ -#include "types.h" - -#ifdef DEBUG -#define LOG_DEBUG(msg) fprintf(stderr, msg"\n") -#else -#define LOG_DEBUG(msg) -#endif - -#define LENGTH(x) (sizeof x / sizeof x[0]) - -#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_TODO -}; - -enum FileType { - F_ERROR, - F_FOLDER, - F_REG_FILE, - F_OTHER -}; - -void util_log_enable_info_logs(void); - -void *emalloc(size_t size); -void *erealloc(void *ptr, size_t size); -void util_log(enum LogLevel level, const char *msg, ...); - -bool str_starts_with(const char *str, const char *part); -char *str_normalize(const char *str); -char *str_trim(const char *str); -char *str_remove_leading_whitespace(const char *str); -void strs_free(char **strs); -bool strs_has(char **strs, const char *str); -void strs_add(char ***strs, const char *str); -int str_compare(const char *a, const char *b); -long str_to_number(const char *str); - -enum FileType file_type(const char *path); -char *file_read(const char *filepath); -char *file_extension_replace_or_add(const char *filepath, const char *extension); -bool file_extension_equals(const char *filepath, const char *extension); - -char *filepath_add_ending_slash_if_missing(const char *path); -char *filepath_basename(const char *path); -char *filepath_dirname(const char *path); -char *filepath_resolve_tilde(const char *path); - -struct Size *size_create(const char *str); -struct Size *size_copy(struct Size *size); -const char *size_to_string(struct Size *size);