lorid

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

chord_diagram.c (20905B)


      1 #include <string.h>
      2 #include <ctype.h>
      3 #include <pdfio.h>
      4 #include <pdfio-content.h>
      5 #include "core.h"
      6 #include "out_pdf.h"
      7 #include "config.h"
      8 #include "chordpro.h"
      9 #include "chord_diagram.h"
     10 #include "diagrams.h"
     11 
     12 static bool
     13 text_show(
     14 	pdfio_stream_t *stream,
     15 	bool unicode,
     16 	const char *text,
     17 	double x,
     18 	double y
     19 )
     20 {
     21 	if (!pdfioContentSetTextRenderingMode(stream, PDFIO_TEXTRENDERING_FILL)) {
     22 		fprintf(stderr, "pdfioContentSetTextRenderingMode failed.\n");
     23 		return false;
     24 	}
     25 	if (!pdfioContentTextBegin(stream)) {
     26 		fprintf(stderr, "pdfioContentTextBegin failed.\n");
     27 		return false;
     28 	}
     29 	if (!pdfioContentTextMoveTo(stream, x, y)) {
     30 		fprintf(stderr, "pdfioContentTextMoveTo failed.\n");
     31 		return false;
     32 	}
     33 	if (!pdfioContentTextShow(stream, unicode, text)) {
     34 		fprintf(stderr, "pdfioContentTextShow failed.\n");
     35 		return false;
     36 	}
     37 	if (!pdfioContentTextEnd(stream)) {
     38 		fprintf(stderr, "pdfioContentTextEnd failed.\n");
     39 		return false;
     40 	}
     41 	return true;
     42 }
     43 
     44 static bool
     45 draw_rectangle(
     46 	pdfio_stream_t *stream,
     47 	double x,
     48 	double y,
     49 	double width,
     50 	double height,
     51 	enum TextRendering type
     52 )
     53 {
     54 	if (!pdfioContentSetFillColorRGB(stream, 0.0, 0.0, 0.0)) {
     55 		fprintf(stderr, "pdfioContentSetFillColorRGB failed.\n");
     56 		return false;
     57 	}
     58 	if (!pdfioContentSetStrokeColorRGB(stream, 0.0, 0.0, 0.0)) {
     59 		fprintf(stderr, "pdfioContentSetStrokeColorRGB failed.\n");
     60 		return false;
     61 	}
     62 	if (!pdfioContentPathRect(stream, x, y, width, height)) {
     63 		fprintf(stderr, "pdfioContentPathRect failed.\n");
     64 		return false;
     65 	}
     66 	switch (type) {
     67 	case FILL_AND_STROKE:
     68 		if (!pdfioContentFillAndStroke(stream, true)) {
     69 			fprintf(stderr, "pdfioContentFillAndStroke failed.\n");
     70 			return false;
     71 		}
     72 		break;
     73 	case FILL:
     74 		if (!pdfioContentFill(stream, true)) {
     75 			fprintf(stderr, "pdfioContentFill failed.\n");
     76 			return false;
     77 		}
     78 		break;
     79 	case STROKE:
     80 		if (!pdfioContentStroke(stream)) {
     81 			fprintf(stderr, "pdfioContentStroke failed.\n");
     82 			return false;
     83 		}
     84 		break;
     85 	}
     86 	return true;
     87 }
     88 
     89 static bool
     90 draw_line(
     91 	pdfio_stream_t *stream,
     92 	double x,
     93 	double y,
     94 	double width,
     95 	enum Direction direction
     96 )
     97 {
     98 	if (!pdfioContentPathMoveTo(stream, x, y)) {
     99 		fprintf(stderr, "pdfioContentPathMoveTo failed.\n");
    100 		return false;
    101 	}
    102 	if (direction == HORIZONTAL) {
    103 		if (!pdfioContentPathLineTo(stream, x+width, y)) {
    104 			fprintf(stderr, "pdfioContentPathLineTo failed.\n");
    105 			return false;
    106 		}
    107 	} else {
    108 		if (!pdfioContentPathLineTo(stream, x, y+width)) {
    109 			fprintf(stderr, "pdfioContentPathLineTo failed.\n");
    110 			return false;
    111 		}
    112 	}
    113 	if (!pdfioContentSetStrokeColorRGB(stream, 0.0, 0.0, 0.0)) {
    114 		fprintf(stderr, "pdfioContentSetStrokeColorRGB failed.\n");
    115 		return false;
    116 	}
    117 	if (!pdfioContentStroke(stream)) {
    118 		fprintf(stderr, "pdfioContentFill failed.\n");
    119 		return false;
    120 	}
    121 	return true;
    122 }
    123 
    124 static bool
    125 draw_bezier_oval_quarter(
    126 	pdfio_stream_t *stream,
    127 	double center_x,
    128 	double center_y,
    129 	double size_x,
    130 	double size_y
    131 )
    132 {
    133 	if (!pdfioContentPathMoveTo(stream, center_x - size_x, center_y)) {
    134 		fprintf(stderr, "pdfioContentPathMoveTo failed.\n");
    135 		return false;
    136 	}
    137 	if (!pdfioContentPathCurve(
    138 			stream,
    139 			center_x - size_x,
    140 			center_y - 0.552 * size_y,
    141 			center_x - 0.552 * size_x,
    142 			center_y - size_y,
    143 			center_x,
    144 			center_y - size_y
    145 	)) {
    146 		fprintf(stderr, "pdfioContentPathCurve failed.\n");
    147 		return false;
    148 	}
    149 	return true;
    150 }
    151 
    152 static bool
    153 draw_bezier_oval(
    154 	pdfio_stream_t *stream,
    155 	double center_x,
    156 	double center_y,
    157 	double size_x,
    158 	double size_y
    159 )
    160 {
    161 	if (!pdfioContentSetStrokeColorRGB(stream, 0.0, 0.0, 0.0)) {
    162 		fprintf(stderr, "pdfioContentSetStrokeColorRGB failed.\n");
    163 		return false;
    164 	}
    165 	if (!draw_bezier_oval_quarter(stream, center_x, center_y, -size_x, size_y)) {
    166 		fprintf(stderr, "draw_bezier_oval_quarter failed.");
    167 		return false;
    168 	}
    169 	if (!draw_bezier_oval_quarter(stream, center_x, center_y, size_x, size_y)) {
    170 		fprintf(stderr, "draw_bezier_oval_quarter failed.");
    171 		return false;
    172 	}
    173 	if (!draw_bezier_oval_quarter(stream, center_x, center_y, size_x, -size_y)) {
    174 		fprintf(stderr, "draw_bezier_oval_quarter failed.");
    175 		return false;
    176 	}
    177 	if (!draw_bezier_oval_quarter(stream, center_x, center_y, -size_x, -size_y)) {
    178 		fprintf(stderr, "draw_bezier_oval_quarter failed.");
    179 		return false;
    180 	}
    181 	if (!pdfioContentStroke(stream)) {
    182 		fprintf(stderr, "pdfioContentStroke failed.\n");
    183 		return false;
    184 	}
    185 	return true;
    186 }
    187 
    188 static bool
    189 draw_bezier_circle(
    190 	pdfio_stream_t *stream,
    191 	double center_x,
    192 	double center_y,
    193 	double size
    194 )
    195 {
    196 	if (!pdfioContentSetLineWidth(stream, size * 0.4)) {
    197 		fprintf(stderr, "pdfioContentSetLineWidth failed.\n");
    198 		return false;
    199 	}
    200 	if (!draw_bezier_oval(stream, center_x, center_y, size, size)) {
    201 		fprintf(stderr, "draw_bezier_oval failed.");
    202 		return false;
    203 	}
    204 	return true;
    205 }
    206 
    207 static bool
    208 draw_x(pdfio_stream_t *stream, double x, double y, double size)
    209 {
    210 	if (!pdfioContentSetLineWidth(stream, size * 0.4)) {
    211 		fprintf(stderr, "pdfioContentSetLineWidth failed.\n");
    212 		return false;
    213 	}
    214 	if (!pdfioContentSetStrokeColorRGB(stream, 0.0, 0.0, 0.0)) {
    215 		fprintf(stderr, "pdfioContentSetStrokeColorRGB failed.\n");
    216 		return false;
    217 	}
    218 	if (!pdfioContentPathMoveTo(stream, x - size, y - size)) {
    219 		fprintf(stderr, "pdfioContentPathMoveTo failed.\n");
    220 		return false;
    221 	}
    222 	if (!pdfioContentPathLineTo(stream, x + size, y + size)) {
    223 		fprintf(stderr, "pdfioContentPathLineTo failed.\n");
    224 		return false;
    225 	}
    226 	if (!pdfioContentStroke(stream)) {
    227 		fprintf(stderr, "pdfioContentFill failed.\n");
    228 		return false;
    229 	}
    230 	if (!pdfioContentPathMoveTo(stream, x - size, y + size)) {
    231 		fprintf(stderr, "pdfioContentPathMoveTo failed.\n");
    232 		return false;
    233 	}
    234 	if (!pdfioContentPathLineTo(stream, x + size, y - size)) {
    235 		fprintf(stderr, "pdfioContentPathLineTo failed.\n");
    236 		return false;
    237 	}
    238 	if (!pdfioContentStroke(stream)) {
    239 		fprintf(stderr, "pdfioContentFill failed.\n");
    240 		return false;
    241 	}
    242 	return true;
    243 }
    244 
    245 static bool
    246 is_valid_circle_char(char c)
    247 {
    248 	if (c >= '/' && c <= '9') {
    249 		return true;
    250 	}
    251 	if (c >= 'A' && c <= 'Z') {
    252 		return true;
    253 	}
    254 	return false;
    255 }
    256 
    257 static bool
    258 draw_circle_with_char_inside(
    259 	pdfio_stream_t *stream,
    260 	double x,
    261 	double y,
    262 	double field_width,
    263 	char c
    264 )
    265 {
    266 	if (!is_valid_circle_char(c)) {
    267 		fprintf(stderr, "is_valid_circle_char failed.\n");
    268 		return false;
    269 	}
    270 	char str[2];
    271 	str[0] = c;
    272 	str[1] = 0;
    273 	if (!pdfioContentSetTextFont(stream, "chord-diagram-symbols", field_width)) {
    274 		fprintf(stderr, "pdfioContentSetTextFont failed.\n");
    275 		return false;
    276 	}
    277 	/* INFO: Needs to be the color of the page background. This cannot be set at the moment. It will always be white. */
    278 	if (!pdfioContentSetFillColorRGB(stream, 1.0, 1.0, 1.0)) {
    279 		fprintf(stderr, "pdfioContentSetFillColorRGB failed.\n");
    280 		return false;
    281 	}
    282 	if (!text_show(stream, true, "/", x-field_width/2, y-field_width/2.8)) {
    283 		fprintf(stderr, "text_show failed.");
    284 		return false;
    285 	}
    286 	if (!pdfioContentSetFillColorRGB(stream, 0.0, 0.0, 0.0)) {
    287 		fprintf(stderr, "pdfioContentSetFillColorRGB failed.\n");
    288 		return false;
    289 	}
    290 	if (!text_show(stream, true, (char *)&str, x-field_width/2, y-field_width/2.8)) {
    291 		fprintf(stderr, "text_show failed.");
    292 		return false;
    293 	}
    294 	return true;
    295 }
    296 
    297 struct ChordMap *
    298 chord_map_new(void)
    299 {
    300 	struct ChordMap *map = emalloc(sizeof(struct ChordMap));
    301 	map->name = NULL;
    302 	map->display = NULL;
    303 	return map;
    304 }
    305 
    306 void
    307 chord_map_free(struct ChordMap *map)
    308 {
    309 	if (!map) {
    310 		return;
    311 	}
    312 	free(map->name);
    313 	free(map->display);
    314 	free(map);
    315 }
    316 
    317 struct StringDiagram *
    318 string_diagram_new(void)
    319 {
    320 	struct StringDiagram *d = emalloc(sizeof(struct StringDiagram));
    321 	d->name = NULL;
    322 	d->base_fret = -2;
    323 	memset(d->frets, -2, sizeof(d->frets));
    324 	memset(d->fingers, -2, sizeof(d->fingers));
    325 	return d;
    326 }
    327 
    328 static int
    329 string_diagram_string_count(struct StringDiagram *d)
    330 {
    331 	int i;
    332 	for (i = 0; d->frets[i] != -2 && i < 12; i++);
    333 	return i;
    334 }
    335 
    336 static void
    337 string_diagram_free(struct StringDiagram *d)
    338 {
    339 	if (!d) {
    340 		return;
    341 	}
    342 	free(d->name);
    343 	free(d);
    344 }
    345 
    346 static struct StringDiagram *
    347 string_diagram_copy_all_but_name(struct StringDiagram *diagram)
    348 {
    349 	struct StringDiagram *copy = emalloc(sizeof(struct StringDiagram));
    350 	copy->base_fret = diagram->base_fret;
    351 	int i;
    352 	for (i = 0; i<MAX_STRINGS; i++) {
    353 		copy->frets[i] = diagram->frets[i];
    354 	}
    355 	for (i = 0; i<MAX_STRINGS; i++) {
    356 		copy->fingers[i] = diagram->fingers[i];
    357 	}
    358 	return copy;
    359 }
    360 
    361 /* static bool
    362 string_diagram_is_valid(struct StringDiagram *d)
    363 {
    364 	int i;
    365 	size_t fret_count, finger_count;
    366 	if (d->base_fret < 1 || d->base_fret > 99) {
    367 		return false;
    368 	}
    369 	i = 0;
    370 	while (d->frets[i] != -2 && i < 12) {
    371 		i++;
    372 	}
    373 	fret_count = i;
    374 	i = 0;
    375 	while (d->fingers[i] != -2 && i < 12) {
    376 		i++;
    377 	}
    378 	finger_count = i;
    379 	if (finger_count > 0 && fret_count != finger_count) {
    380 		return false;
    381 	}
    382 	return true;
    383 } */
    384 
    385 static size_t
    386 string_diagram_fret_count(struct StringDiagram *d)
    387 {
    388 	int i;
    389 	for (i = 0; d->frets[i] != -2 && i < MAX_STRINGS; i++);
    390 	return i;
    391 }
    392 
    393 static char
    394 finger_to_char(int8_t finger)
    395 {
    396 	switch (finger) {
    397 	case 1:
    398 		return '1';
    399 	case 2:
    400 		return '2';
    401 	case 3:
    402 		return '3';
    403 	case 4:
    404 		return '4';
    405 	default:
    406 		return '/';
    407 	}
    408 }
    409 
    410 static bool
    411 string_diagram_draw(
    412 	struct PDFContext *ctx,
    413 	pdfio_stream_t *stream,
    414 	struct StringDiagram *diagram,
    415 	double x,
    416 	double y,
    417 	double width
    418 )
    419 {
    420 	int i;
    421 	int instrument_string_count = string_diagram_string_count(diagram);
    422 	double field_width = width / (instrument_string_count - 1);
    423 	double height = field_width * 4;
    424 	double y_above_diagram = y + height + field_width / 2 + field_width / 2.5;
    425 	double vertical_lines_height = height + field_width / 2;
    426 	if (!pdfioContentSetLineWidth(stream, field_width * 0.09)) {
    427 		fprintf(stderr, "pdfioContentSetLineWidth failed.\n");
    428 		return false;
    429 	}
    430 	if (!draw_rectangle(stream, x, y, width, height, STROKE)) {
    431 		fprintf(stderr, "draw_rectangle failed.\n");
    432 		return false;
    433 	}
    434 	// from bottom to top draw horizontal lines
    435 	for (i = 0; i<4; i++) {
    436 		if (!draw_line(stream, x, y+field_width * (i+1), width, HORIZONTAL)) {
    437 			fprintf(stderr, "draw_line failed.\n");
    438 			return false;
    439 		}
    440 	}
    441 	// from left to right draw vertical lines
    442 	for (i = 0; i<instrument_string_count; i++) {
    443 		if (!draw_line(stream, x+field_width*i, y, vertical_lines_height, VERTICAL)) {
    444 			fprintf(stderr, "draw_line failed.\n");
    445 			return false;
    446 		}
    447 	}
    448 	if (diagram->base_fret == 1) {
    449 		if (!draw_rectangle(stream, x, y+height, width, field_width/2, FILL_AND_STROKE)) {
    450 			fprintf(stderr, "draw_rectangle failed.\n");
    451 			return false;
    452 		}
    453 	} else {
    454 		double base_pos_x;
    455 		size_t base_size = 5;
    456 		char base_position[base_size];
    457 		base_position[4] = 0;
    458 		snprintf((char *)&base_position, base_size, "%d", diagram->base_fret);
    459 		if (diagram->base_fret > 9) {
    460 			base_pos_x = x - field_width - field_width;
    461 		} else {
    462 			base_pos_x = x - field_width - field_width / 3;
    463 		}
    464 		if (!pdfioContentSetTextCharacterSpacing(stream, -1.5)) {
    465 			fprintf(stderr, "pdfioContentSetTextCharacterSpacing failed.\n");
    466 			return false;
    467 		}
    468 		if (!pdfioContentSetTextFont(stream, "chord-diagram-regular-font", field_width*1.15)) {
    469 			fprintf(stderr, "pdfioContentSetTextFont failed.\n");
    470 			return false;
    471 		}
    472 		if (!text_show(stream, !ctx->diagram_font_is_base_font, base_position, base_pos_x, y+field_width*3+field_width*0.1)) {
    473 			fprintf(stderr, "text_show failed.\n");
    474 			return false;
    475 		}
    476 	}
    477 	int8_t fret, finger;
    478 	for (i = 0; i<instrument_string_count; i++) {
    479 		fret = diagram->frets[i];
    480 		finger = diagram->fingers[i];
    481 		if (fret == -1) {
    482 			if (!draw_x(stream, x+(i*field_width), y_above_diagram, field_width / 4)) {
    483 				fprintf(stderr, "draw_x failed.\n");
    484 				return false;
    485 			}
    486 		} else
    487 		if (fret == 0) {
    488 			if (!draw_bezier_circle(stream, x+(i*field_width), y_above_diagram, field_width / 4)) {
    489 				fprintf(stderr, "draw_circle_with_char_inside failed.\n");
    490 				return false;
    491 			}
    492 		} else
    493 		if (fret < 5) {
    494 			if (!draw_circle_with_char_inside(stream,
    495 				x + i * field_width,
    496 				y + field_width/2 + field_width * (4 - fret),
    497 				field_width,
    498 				finger_to_char(finger)
    499 			)) {
    500 				fprintf(stderr, "draw_circle_with_char_inside failed.\n");
    501 				return false;
    502 			}
    503 		}
    504 	}
    505 	pdfio_obj_t *font_obj;
    506 	double name_width, centered_x;
    507 	font_obj = out_pdf_fnt_obj_get_by_name(ctx, "chord-diagram-regular-font");
    508 	if (!font_obj) {
    509 		DEBUG("out_pdf_fnt_obj_get_by_name failed.");
    510 		return false;
    511 	}
    512 	name_width = pdfioContentTextMeasure(font_obj, diagram->name, field_width*2.0);
    513 	centered_x = (width - name_width) / 2;
    514 	if (!pdfioContentSetTextFont(stream, "chord-diagram-regular-font", field_width*2.0)) {
    515 		fprintf(stderr, "pdfioContentSetTextFont failed.\n");
    516 		return false;
    517 	}
    518 	if (!text_show(stream, !ctx->diagram_font_is_base_font, diagram->name, x+centered_x, y_above_diagram + 7.0)) {
    519 		fprintf(stderr, "text_show failed.\n");
    520 		return false;
    521 	}
    522 	return true;
    523 }
    524 
    525 struct KeyboardDiagram *
    526 keyboard_diagram_new(void)
    527 {
    528 	struct KeyboardDiagram *d = emalloc(sizeof(struct KeyboardDiagram));
    529 	d->name = NULL;
    530 	memset(d->keys, -2, sizeof(d->keys));
    531 	return d;
    532 }
    533 
    534 static struct KeyboardDiagram *
    535 keyboard_diagram_copy_all_but_name(struct KeyboardDiagram *diagram)
    536 {
    537 	struct KeyboardDiagram *copy = keyboard_diagram_new();
    538 	memcpy(copy->keys, diagram->keys, 24);
    539 	return copy;
    540 }
    541 
    542 static void
    543 keyboard_diagram_free(struct KeyboardDiagram *d)
    544 {
    545 	if (!d) {
    546 		return;
    547 	}
    548 	free(d->name);
    549 	free(d);
    550 }
    551 
    552 struct ChordDiagram *
    553 chord_diagram_new()
    554 {
    555 	struct ChordDiagram *d = emalloc(sizeof(struct ChordDiagram));
    556 	d->show = true;
    557 	d->color = cho_rgbcolor_new(20, 20, 20);
    558 	return d;
    559 }
    560 
    561 void
    562 chord_diagram_free(struct ChordDiagram *d)
    563 {
    564 	if (!d) {
    565 		return;
    566 	}
    567 	free(d->color);
    568 	switch (d->type) {
    569 	case CHORD_DIAGRAM_CONTENT_STRING:
    570 		string_diagram_free(d->u.sd);
    571 		break;
    572 	case CHORD_DIAGRAM_CONTENT_KEYBOARD:
    573 		keyboard_diagram_free(d->u.kd);
    574 		break;
    575 	case CHORD_DIAGRAM_CONTENT_CHORD_MAP:
    576 		chord_map_free(d->u.cm);
    577 		break;
    578 	default:
    579 	}
    580 	free(d);
    581 }
    582 
    583 static struct ChordDiagram *
    584 chord_diagram_copy_all_but_name(struct ChordDiagram *diagram)
    585 {
    586 	struct ChordDiagram *copy = emalloc(sizeof(struct ChordDiagram));
    587 	copy->show = diagram->show;
    588 	copy->color = cho_color_copy(diagram->color);
    589 	copy->type = diagram->type;
    590 	switch (diagram->type) {
    591 	case CHORD_DIAGRAM_CONTENT_STRING:
    592 		copy->u.sd = string_diagram_copy_all_but_name(diagram->u.sd);
    593 		break;
    594 	case CHORD_DIAGRAM_CONTENT_KEYBOARD:
    595 		copy->u.kd = keyboard_diagram_copy_all_but_name(diagram->u.kd);
    596 		break;
    597 	default:
    598 		util_log(NULL, 0, LOG_ERR, "Invalid ChordDiagram type '%d'", diagram->type);
    599 		free(copy->color);
    600 		free(copy);
    601 		return NULL;
    602 	}
    603 	return copy;
    604 }
    605 
    606 enum ChordDiagramContent
    607 chord_diagram_duplicate(
    608 	struct ChordDiagram *diagram,
    609 	struct ChordDiagram **custom_diagrams,
    610 	int custom_diagrams_len,
    611 	const char *name,
    612 	const char *chord_to_copy,
    613 	enum Instrument instrument
    614 )
    615 {
    616 	int d;
    617 	for (d = custom_diagrams_len-1; d >= 0; d--) {
    618 		switch (custom_diagrams[d]->type) {
    619 		case CHORD_DIAGRAM_CONTENT_STRING:
    620 			if (!strcmp(custom_diagrams[d]->u.sd->name, chord_to_copy)) {
    621 				diagram->type = CHORD_DIAGRAM_CONTENT_STRING;
    622 				diagram->u.sd = string_diagram_copy_all_but_name(custom_diagrams[d]->u.sd);
    623 				diagram->u.sd->name = strdup(name);
    624 				return CHORD_DIAGRAM_CONTENT_STRING;
    625 			}
    626 			break;
    627 		case CHORD_DIAGRAM_CONTENT_KEYBOARD:
    628 			if (!strcmp(custom_diagrams[d]->u.kd->name, chord_to_copy)) {
    629 				diagram->type = CHORD_DIAGRAM_CONTENT_KEYBOARD;
    630 				diagram->u.kd = keyboard_diagram_copy_all_but_name(custom_diagrams[d]->u.kd);
    631 				diagram->u.kd->name = strdup(name);
    632 				return CHORD_DIAGRAM_CONTENT_KEYBOARD;
    633 			}
    634 			break;
    635 		case CHORD_DIAGRAM_CONTENT_CHORD_MAP:
    636 			if (!strcmp(custom_diagrams[d]->u.cm->name, chord_to_copy)) {
    637 				diagram->type = CHORD_DIAGRAM_CONTENT_CHORD_MAP;
    638 				diagram->u.cm = chord_map_new();
    639 				diagram->u.cm->display = strdup(custom_diagrams[d]->u.cm->display);
    640 				diagram->u.cm->name = strdup(name);
    641 				return CHORD_DIAGRAM_CONTENT_CHORD_MAP;
    642 			}
    643 			break;
    644 		default:
    645 		}
    646 	}
    647 	switch (instrument) {
    648 	case INSTRUMENT_GUITAR: {
    649 		size_t i;
    650 		for (i = 0; i<LENGTH(guitar_diagrams); i++) {
    651 			if (!strcmp(guitar_diagrams[i].name, chord_to_copy)) {
    652 				diagram->type = CHORD_DIAGRAM_CONTENT_STRING;
    653 				diagram->u.sd = string_diagram_copy_all_but_name(&guitar_diagrams[i]);
    654 				diagram->u.sd->name = strdup(name);
    655 				return CHORD_DIAGRAM_CONTENT_STRING;
    656 			}
    657 		}
    658 		break;
    659 	}
    660 	case INSTRUMENT_KEYBOARD: {
    661 		break;
    662 	}
    663 	case INSTRUMENT_MANDOLIN: {
    664 		break;
    665 	}
    666 	case INSTRUMENT_UKULELE: {
    667 		break;
    668 	}
    669 	}
    670 	return -1;
    671 }
    672 
    673 void
    674 chord_diagrams_free(struct ChordDiagram **diagrams)
    675 {
    676 	if (!diagrams) {
    677 		return;
    678 	}
    679 	struct ChordDiagram **d;
    680 	for (d = diagrams; *d; d++) {
    681 		chord_diagram_free(*d);
    682 	}
    683 	free(diagrams);
    684 }
    685 
    686 #ifdef ENABLE_DEBUG
    687 void
    688 debug_chord_diagram_print(struct ChordDiagram *diagram)
    689 {
    690 	int i;
    691 	printf("---- CHORD DIAGRAM BEGIN ----\n");
    692 	printf("show: %s\n", diagram->show ? "true" : "false");
    693 
    694 	size_t size = 8;
    695 	char str[size];
    696 	// str[7] = 0;
    697 	snprintf((char *)&str, size, "#%02X%02X%02X", diagram->color->red, diagram->color->green, diagram->color->blue);
    698 
    699 	printf("color: %s\n", str);
    700 
    701 	switch (diagram->type) {
    702 	case CHORD_DIAGRAM_CONTENT_STRING:
    703 		printf("name: %s\n", diagram->u.sd->name);
    704 		printf("base-fret: %d\n", diagram->u.sd->base_fret);
    705 		printf("frets: ");
    706 		for (i = 0; i<12; i++) {
    707 			printf("%d ", diagram->u.sd->frets[i]);
    708 		}
    709 		printf("\n");
    710 		printf("fingers: ");
    711 		for (i = 0; i<12; i++) {
    712 			printf("%d ", diagram->u.sd->fingers[i]);
    713 		}
    714 		printf("\n");
    715 		break;
    716 	case CHORD_DIAGRAM_CONTENT_KEYBOARD:
    717 		printf("name: %s\n", diagram->u.kd->name);
    718 		printf("keys: ");
    719 		for (i = 0; i<24; i++) {
    720 			printf("%d ", diagram->u.kd->keys[i]);
    721 		}
    722 		printf("\n");
    723 		break;
    724 	case CHORD_DIAGRAM_CONTENT_CHORD_MAP:
    725 		printf("name: %s\n", diagram->u.cm->name);
    726 		printf("display: %s\n", diagram->u.cm->display);
    727 		break;
    728 	default:
    729 	}
    730 	printf("---- CHORD DIAGRAM END ------\n");
    731 }
    732 #endif /* ENABLE_DEBUG */
    733 
    734 struct ChordDiagram **
    735 chord_diagrams_create(
    736 	struct Config *config,
    737 	struct ChoChord ***chords,
    738 	struct ChordDiagram **custom_diagrams
    739 )
    740 {
    741 	struct ChordDiagram **diagrams = NULL;
    742 	struct ChordDiagram **cd;
    743 	struct ChoChord **c;
    744 	int d = 0;
    745 	size_t i;
    746 	char **names = NULL;
    747 	char *chord_name = NULL;
    748 	char *common_chord_name = NULL;
    749 	char *parsed_chord_name;
    750 
    751 	switch (config->output->diagram->instrument) {
    752 	case INSTRUMENT_GUITAR:
    753 		for (c = *chords; *c; c++) {
    754 			// cho_debug_chord_print(*c);
    755 			parsed_chord_name = (*c)->name;
    756 			common_chord_name = cho_chord_name_generate_common(*c, config);
    757 			chord_name = cho_chord_name_generate(*c);
    758 			for (cd = custom_diagrams; *cd; cd++) {
    759 				if (
    760 					(*cd)->type == CHORD_DIAGRAM_CONTENT_STRING &&
    761 					string_diagram_fret_count((*cd)->u.sd) == 6 &&
    762 					!strcmp(parsed_chord_name, (*cd)->u.sd->name) &&
    763 					!strs_has(names, chord_name)
    764 				) {
    765 					strs_add(&names, chord_name);
    766 					diagrams = erealloc(diagrams, (d+1) * sizeof(struct ChordDiagram *));
    767 					diagrams[d] = chord_diagram_copy_all_but_name(*cd);
    768 					if (!diagrams[d]) {
    769 						DEBUG("chord_diagram_copy_all_but_name failed.");
    770 						goto ERR;
    771 					}
    772 					diagrams[d]->u.sd->name = strdup(chord_name);
    773 					d++;
    774 					goto NEXT_CHORD;
    775 				}
    776 			}
    777 			for (i = 0; i<LENGTH(guitar_diagrams); i++) {
    778 				if (
    779 					!strcmp(common_chord_name, guitar_diagrams[i].name) &&
    780 					!strs_has(names, chord_name)
    781 				) {
    782 					strs_add(&names, chord_name);
    783 					diagrams = erealloc(diagrams, (d+1) * sizeof(struct ChordDiagram *));
    784 					diagrams[d] = chord_diagram_new();
    785 					diagrams[d]->type = CHORD_DIAGRAM_CONTENT_STRING;
    786 					diagrams[d]->u.sd = string_diagram_copy_all_but_name(&guitar_diagrams[i]);
    787 					diagrams[d]->u.sd->name = strdup(chord_name);
    788 					d++;
    789 					break;
    790 				}
    791 			}
    792 			NEXT_CHORD:
    793 			free(common_chord_name);
    794 			free(chord_name);
    795 			common_chord_name = NULL;
    796 			chord_name = NULL;
    797 		}
    798 		break;
    799 	case INSTRUMENT_KEYBOARD:
    800 		break;
    801 	case INSTRUMENT_MANDOLIN:
    802 		break;
    803 	case INSTRUMENT_UKULELE:
    804 		break;
    805 	default:
    806 		util_log(NULL, 0, LOG_ERR, "Invalid Instrument enum value '%d'.", config->output->diagram->instrument);
    807 	}
    808 	diagrams = erealloc(diagrams, (d+1) * sizeof(struct ChordDiagram *));
    809 	diagrams[d] = NULL;
    810 	strs_free(names);
    811 	return diagrams;
    812 	ERR:
    813 	free(chord_name);
    814 	free(common_chord_name);
    815 	strs_free(names);
    816 	for (int k = 0; k<d; k++) {
    817 		chord_diagram_free(diagrams[k]);
    818 	}
    819 	free(diagrams);
    820 	return NULL;
    821 }
    822 
    823 bool
    824 chord_diagram_draw(
    825 	struct PDFContext *ctx,
    826 	pdfio_stream_t *stream,
    827 	struct ChordDiagram *diagram,
    828 	double x,
    829 	double y,
    830 	double width
    831 )
    832 {
    833 	switch (diagram->type) {
    834 	case CHORD_DIAGRAM_CONTENT_STRING:
    835 		if (!string_diagram_draw(ctx, stream, diagram->u.sd, x, y, width)) {
    836 			DEBUG("draw_string_chord_diagram failed.");
    837 			return false;
    838 		}
    839 		break;
    840 	/* case CHORD_DIAGRAM_CONTENT_KEYBOARD:
    841 		util_log(NULL, 0, LOG_TODO, "draw_keyboard_chord_diagram()");
    842 		break;
    843 	case CHORD_DIAGRAM_CONTENT_CHORD_MAP:
    844 		util_log(NULL, 0, LOG_TODO, "well");
    845 		break; */
    846 	default:
    847 		util_log(NULL, 0, LOG_ERR, "These 'enum ChordDiagramContent' values are not yet implemented.");
    848 		return false;
    849 	}
    850 	return true;
    851 }