out_pdf.c (82519B)
1 #include <stdbool.h> 2 #include <string.h> 3 #include <unistd.h> 4 #include <pdfio.h> 5 #include <pdfio-content.h> 6 #include <sys/stat.h> 7 #include <errno.h> 8 #include <fontconfig/fontconfig.h> 9 #include <grapheme.h> 10 #include <math.h> 11 #include "core.h" 12 #include "out_pdf.h" 13 #include "chordpro.h" 14 #include "config.h" 15 #include "chord_diagram.h" 16 17 static struct Obj * 18 obj_new(void) 19 { 20 struct Obj *obj = emalloc(sizeof(struct Obj)); 21 obj->name = NULL; 22 obj->value = NULL; 23 return obj; 24 } 25 26 static void 27 obj_free(struct Obj *obj) 28 { 29 if (!obj) { 30 return; 31 } 32 free(obj->name); 33 free(obj); 34 } 35 36 static void 37 objs_free(struct Obj **objs) 38 { 39 if (!objs) { 40 return; 41 } 42 struct Obj **start = objs; 43 for (; *objs; objs++) { 44 obj_free(*objs); 45 } 46 free(start); 47 } 48 49 static pdfio_obj_t * 50 objs_get_obj(struct Obj **objs, const char *name) 51 { 52 if (!objs) { 53 return NULL; 54 } 55 struct Obj **o; 56 for (o = objs; *o; o++) { 57 if (!strcmp((*o)->name, name)) { 58 return (*o)->value; 59 } 60 } 61 return NULL; 62 } 63 64 static void 65 objs_add_obj(struct Obj ***array, struct Obj *obj) 66 { 67 if (!*array) { 68 *array = erealloc(*array, 2 * sizeof(struct Obj *)); 69 (*array)[0] = obj; 70 (*array)[1] = NULL; 71 } else { 72 int a; 73 for (a = 0; (*array)[a]; a++); 74 *array = erealloc(*array, (a+2) * sizeof(struct Obj *)); 75 (*array)[a] = obj; 76 (*array)[a+1] = NULL; 77 } 78 } 79 80 pdfio_obj_t * 81 out_pdf_fnt_obj_get_by_name(struct PDFContext *ctx, const char *name) 82 { 83 int i; 84 for (i = 0; ctx->fonts[i]; i++) { 85 if (!strcmp(ctx->fonts[i]->name, name)) { 86 return ctx->fonts[i]->value; 87 } 88 } 89 return NULL; 90 } 91 92 static char * 93 fnt_name_create(struct Font *font) 94 { 95 const char *c; 96 char *f; 97 char *family = str_normalize(font->family); 98 const char *style = cho_font_style_to_config_string(font->style); 99 const char *weight = cho_font_weight_to_config_string(font->weight); 100 size_t len = strlen(family) + strlen(style) + strlen(weight); 101 char *fnt_name = emalloc((len + 4) * sizeof(char)); 102 103 f = fnt_name; 104 for (c = family; *c; c++, f++) { 105 *f = *c; 106 } 107 *f = '-'; 108 f++; 109 for (c = style; *c; c++, f++) { 110 *f = *c; 111 } 112 *f = '-'; 113 f++; 114 for (c = weight; *c; c++, f++) { 115 *f = *c; 116 } 117 *f = '\0'; 118 free(family); 119 return fnt_name; 120 } 121 122 static bool 123 fonts_add_if_not_in(struct Font ***array, struct Font *font) 124 { 125 if (!*array) { 126 *array = erealloc(*array, 2 * sizeof(struct Font *)); 127 (*array)[0] = font; 128 (*array)[1] = NULL; 129 return true; 130 } else { 131 int a; 132 for (a = 0; (*array)[a]; a++) { 133 if ( 134 !strcmp((*array)[a]->family, font->family) && 135 (*array)[a]->style == font->style && 136 (*array)[a]->weight == font->weight 137 ) { 138 return false; 139 } 140 } 141 *array = erealloc(*array, (a+2) * sizeof(struct Font *)); 142 (*array)[a] = font; 143 (*array)[a+1] = NULL; 144 return true; 145 } 146 } 147 148 static struct Font ** 149 fonts_get_all(struct ChoSong **songs, struct Config *config) 150 { 151 struct Font **fonts = NULL; 152 struct Font *font; 153 struct ChoSong **so; 154 struct ChoMetadata **m; 155 struct ChoSection **se; 156 struct ChoLine **li; 157 struct ChoLineItemAbove **above; 158 struct ChoLineItem **it; 159 struct ChoStyle *style; 160 bool added; 161 int i; 162 163 for (so = songs; *so; so++) { 164 for (i = 0; i < TEXT_TYPE_LENGTH; i++) { 165 if ((*so)->present_text_types[i]) { 166 font = cho_font_copy(config->output->styles[i]->font); 167 added = fonts_add_if_not_in(&fonts, font); 168 if (!added) { 169 cho_font_free(font); 170 } 171 } 172 } 173 for (m = (*so)->metadata; *m; m++) { 174 font = cho_font_copy((*m)->style->font); 175 added = fonts_add_if_not_in(&fonts, font); 176 if (!added) { 177 cho_font_free(font); 178 } 179 } 180 for (se = (*so)->sections; *se; se++) { 181 if ((*se)->label && (*se)->label->style->font->family) { 182 font = cho_font_copy((*se)->label->style->font); 183 added = fonts_add_if_not_in(&fonts, font); 184 if (!added) { 185 cho_font_free(font); 186 } 187 } 188 for (li = (*se)->lines; *li; li++) { 189 for (above = (*li)->text_above; *above; above++) { 190 if ((*above)->is_chord) { 191 style = (*above)->u.chord->style; 192 } else { 193 style = (*above)->u.annot->style; 194 } 195 if (style->font->family) { 196 font = cho_font_copy(style->font); 197 added = fonts_add_if_not_in(&fonts, font); 198 if (!added) { 199 cho_font_free(font); 200 } 201 } 202 } 203 for (it = (*li)->items; *it; it++) { 204 if ((*it)->is_text) { 205 style = (*it)->u.text->style; 206 if (style->font->family) { 207 font = cho_font_copy(style->font); 208 added = fonts_add_if_not_in(&fonts, font); 209 if (!added) { 210 cho_font_free(font); 211 } 212 } 213 } 214 } 215 } 216 } 217 } 218 return fonts; 219 } 220 221 static int 222 fontpath_count_fonts(FcChar8 *path) 223 { 224 int count; 225 FcFontSet *set; 226 227 set = FcFontSetCreate(); 228 if (!FcFileScan(set, NULL, NULL, NULL, path, false)) { 229 DEBUG("fontpath_count_fonts failed."); 230 FcFontSetDestroy(set); 231 return -1; 232 } 233 count = set->nfont; 234 FcFontSetDestroy(set); 235 return count; 236 } 237 238 static FcPattern * 239 fontconfig_pattern_create(struct Font *font, enum FontType font_type) 240 { 241 FcPattern *pattern; 242 FcValue family, wrapper, variable, width, type, style, weight; 243 244 pattern = FcPatternCreate(); 245 if (!pattern) { 246 DEBUG("FcPatternCreate failed.\n"); 247 return NULL; 248 } 249 family.type = FcTypeString; 250 family.u.s = (FcChar8 *)font->family; 251 wrapper.type = FcTypeString; 252 wrapper.u.s = (FcChar8 *)"SFNT"; 253 variable.type = FcTypeBool; 254 variable.u.b = FcFalse; 255 width.type = FcTypeInteger; 256 width.u.i = FC_WIDTH_NORMAL; 257 type.type = FcTypeString; 258 type.u.s = (FcChar8 *)(font_type == FONT_TYPE_OTF ? "CFF" : "TrueType"); 259 style.type = FcTypeInteger; 260 switch (font->style) { 261 case FONT_STYLE_ROMAN: 262 style.u.i = FC_SLANT_ROMAN; 263 break; 264 case FONT_STYLE_OBLIQUE: 265 style.u.i = FC_SLANT_OBLIQUE; 266 break; 267 case FONT_STYLE_ITALIC: 268 style.u.i = FC_SLANT_ITALIC; 269 break; 270 } 271 weight.type = FcTypeInteger; 272 weight.u.i = font->weight == FONT_WEIGHT_REGULAR ? FC_WEIGHT_REGULAR : FC_WEIGHT_BOLD; 273 FcPatternAdd(pattern, FC_FAMILY, family, FcFalse); 274 FcPatternAdd(pattern, FC_FONT_WRAPPER, wrapper, FcFalse); 275 FcPatternAdd(pattern, FC_VARIABLE, variable, FcFalse); 276 FcPatternAdd(pattern, FC_WIDTH, width, FcFalse); 277 FcPatternAdd(pattern, FC_FONTFORMAT, type, FcFalse); 278 FcPatternAdd(pattern, FC_SLANT, style, FcFalse); 279 FcPatternAdd(pattern, FC_WEIGHT, weight, FcFalse); 280 return pattern; 281 } 282 283 static char * 284 fontpath_find(struct Font *font, enum FontType font_type) 285 { 286 FcChar8 *file; 287 FcFontSet *set; 288 FcObjectSet *obj; 289 FcPattern *pattern, *match; 290 FcResult result; 291 int i; 292 char *filepath = NULL; 293 294 pattern = fontconfig_pattern_create(font, font_type); 295 if (!pattern) { 296 DEBUG("fontconfig_pattern_create failed.\n"); 297 return NULL; 298 } 299 if ( 300 !strcmp(font->family, "sans") || 301 !strcmp(font->family, "serif") || 302 !strcmp(font->family, "monospace") 303 ) { 304 // pattern = FcNameParse((const FcChar8 *)font->family); 305 FcConfigSubstitute(0, pattern, FcMatchPattern); 306 FcDefaultSubstitute(pattern); 307 match = FcFontMatch(NULL, pattern, &result); 308 if (FcPatternGetString(match, FC_FILE, 0, &file) == FcResultMatch) { 309 if ( 310 !file_extension_equals((const char *)file, "ttc") && 311 fontpath_count_fonts(file) == 1 312 ) { 313 filepath = strdup((const char *)file); 314 } 315 } 316 FcPatternDestroy(match); 317 } else { 318 obj = FcObjectSetBuild(FC_FAMILY, FC_SLANT, FC_WIDTH, FC_WEIGHT, FC_FONTFORMAT, FC_FONT_WRAPPER, FC_FILE, NULL); 319 set = FcFontList(NULL, pattern, obj); 320 for (i = 0; i<set->nfont; i++) { 321 if (FcPatternGetString(set->fonts[i], FC_FILE, 0, &file) == FcResultMatch) { 322 if ( 323 !file_extension_equals((const char *)file, "ttc") && 324 fontpath_count_fonts(file) == 1 325 ) { 326 filepath = strdup((const char *)file); 327 break; 328 } 329 } 330 } 331 FcObjectSetDestroy(obj); 332 FcFontSetDestroy(set); 333 } 334 FcPatternDestroy(pattern); 335 return filepath; 336 } 337 338 static bool 339 pdf_load_chord_diagram_fonts(struct PDFContext *ctx) 340 { 341 char *fontpath; 342 const char *font_name; 343 struct Obj *fnt; 344 struct Font font = { 345 .family = DEFAULT_FONT, 346 .style = FONT_STYLE_ROMAN, 347 .weight = FONT_WEIGHT_REGULAR 348 }; 349 350 fnt = obj_new(); 351 fnt->name = strdup("chord-diagram-regular-font"); 352 if ((font_name = is_base_font(&font))) { 353 fnt->value = pdfioFileCreateFontObjFromBase(ctx->pdf_file, font_name); 354 ctx->diagram_font_is_base_font = true; 355 } else { 356 fontpath = fontpath_find(&font, FONT_TYPE_TTF); 357 if (!fontpath) { 358 fontpath = fontpath_find(&font, FONT_TYPE_OTF); 359 if (!fontpath) { 360 DEBUG("fontpath_find failed."); 361 obj_free(fnt); 362 return false; 363 } 364 } 365 fnt->value = pdfioFileCreateFontObjFromFile(ctx->pdf_file, fontpath, true); 366 free(fontpath); 367 } 368 objs_add_obj(&ctx->fonts, fnt); 369 fnt = obj_new(); 370 fnt->name = strdup("chord-diagram-symbols"); 371 fnt->value = pdfioFileCreateFontObjFromFile(ctx->pdf_file, SYMBOLS_FILEPATH, true); 372 objs_add_obj(&ctx->fonts, fnt); 373 return true; 374 } 375 376 static bool 377 font_name_is_path(const char *name) 378 { 379 if (strchr(name, '/')) { 380 return true; 381 } 382 if (file_extension_equals(name, "ttf") || file_extension_equals(name, "otf")) { 383 return true; 384 } 385 return false; 386 } 387 388 static bool 389 pdf_load_fonts(struct PDFContext *ctx, struct Font **needed_fonts) 390 { 391 char *fontpath = NULL; 392 char **fontpaths = NULL; 393 const char *name; 394 int index; 395 struct Font **f; 396 struct Obj *fnt = NULL; 397 398 for (f = needed_fonts; *f; f++) { 399 if (font_name_is_path((*f)->family)) { 400 fontpath = filepath_resolve_tilde((*f)->family); 401 fnt = obj_new(); 402 fnt->name = fnt_name_create(*f); 403 index = strs_get_index_if_in(fontpaths, fontpath); 404 if (index == -1) { 405 fnt->value = pdfioFileCreateFontObjFromFile(ctx->pdf_file, fontpath, true); 406 if (!fnt->value) { 407 DEBUG("pdfioFileCreateFontObjFromFile failed."); 408 goto ERR; 409 } 410 strs_add(&fontpaths, fontpath); 411 util_log(NULL, 0, LOG_INFO, "Loaded font from '%s'.", (*f)->family); 412 } else { 413 fnt->value = ctx->fonts[index]->value; 414 } 415 free(fontpath); 416 // cho_font_print(*f); 417 objs_add_obj(&ctx->fonts, fnt); 418 } else 419 if ((name = is_base_font(*f))) { 420 fnt = obj_new(); 421 fnt->name = fnt_name_create(*f); 422 fnt->value = pdfioFileCreateFontObjFromBase(ctx->pdf_file, name); 423 if (!fnt->value) { 424 DEBUG("pdfioFileCreateFontObjFromBase failed."); 425 goto ERR; 426 } 427 objs_add_obj(&ctx->fonts, fnt); 428 } else { 429 fontpath = fontpath_find(*f, FONT_TYPE_TTF); 430 if (fontpath) { 431 fnt = obj_new(); 432 fnt->name = fnt_name_create(*f); 433 index = strs_get_index_if_in(fontpaths, fontpath); 434 if (index == -1) { 435 fnt->value = pdfioFileCreateFontObjFromFile(ctx->pdf_file, fontpath, true); 436 if (!fnt->value) { 437 DEBUG("pdfioFileCreateFontObjFromFile failed."); 438 goto ERR; 439 } 440 strs_add(&fontpaths, fontpath); 441 util_log(NULL, 0, LOG_INFO, "Loaded font from '%s'.", fontpath); 442 } else { 443 fnt->value = ctx->fonts[index]->value; 444 } 445 // cho_font_print(*f); 446 objs_add_obj(&ctx->fonts, fnt); 447 free(fontpath); 448 } else { 449 fontpath = fontpath_find(*f, FONT_TYPE_OTF); 450 if (fontpath) { 451 fnt = obj_new(); 452 fnt->name = fnt_name_create(*f); 453 index = strs_get_index_if_in(fontpaths, fontpath); 454 if (index == -1) { 455 fnt->value = pdfioFileCreateFontObjFromFile(ctx->pdf_file, fontpath, true); 456 if (!fnt->value) { 457 DEBUG("pdfioFileCreateFontObjFromFile failed."); 458 goto ERR; 459 } 460 strs_add(&fontpaths, fontpath); 461 util_log(NULL, 0, LOG_INFO, "Loaded font from '%s'.", fontpath); 462 } else { 463 fnt->value = ctx->fonts[index]->value; 464 } 465 // cho_font_print(*f); 466 objs_add_obj(&ctx->fonts, fnt); 467 free(fontpath); 468 } else { 469 util_log(NULL, 0, LOG_ERR, "Didn't find font file for following font:"); 470 cho_font_print(*f); 471 goto ERR; 472 } 473 } 474 } 475 } 476 if (ctx->config->output->diagram->show) { 477 if (!pdf_load_chord_diagram_fonts(ctx)) { 478 DEBUG("pdf_load_chord_diagram_fonts failed."); 479 goto ERR; 480 } 481 } 482 strs_free(fontpaths); 483 return true; 484 ERR: 485 strs_free(fontpaths); 486 objs_free(ctx->fonts); 487 obj_free(fnt); 488 free(fontpath); 489 return false; 490 } 491 492 static char * 493 pdf_filename_generate_from_songs(struct PDFContext *ctx, struct ChoSong **songs) 494 { 495 char *filename, *normalized_title, *title; 496 struct ChoStyle *unused; 497 int len; 498 499 len = cho_song_count(songs); 500 if (len == 0) { 501 return NULL; 502 } 503 if (len == 1) { 504 if (!cho_metadata_value(songs[0]->metadata, "title", ctx->config->metadata_separator, &title, &unused)) { 505 DEBUG("cho_metadata_value failed."); 506 return NULL; 507 } 508 normalized_title = str_normalize(title); 509 filename = file_extension_replace_or_add(normalized_title, "pdf"); 510 free(normalized_title); 511 free(title); 512 return filename; 513 } 514 return strdup("collection-of-songs.pdf"); 515 } 516 517 static char * 518 pdf_filepath_create( 519 struct PDFContext *ctx, 520 struct ChoSong **songs, 521 const char *cho_filepath, 522 const char *out 523 ) 524 { 525 char *pdf_filepath = NULL; 526 char *filepath = NULL; 527 char *filename = NULL; 528 char *tmp = NULL; 529 enum FileType type; 530 531 if (cho_filepath) { 532 filepath = file_extension_replace_or_add(cho_filepath, "pdf"); 533 filename = filepath_basename(filepath); 534 } else { 535 filename = pdf_filename_generate_from_songs(ctx, songs); 536 if (!filename) { 537 DEBUG("pdf_filename_generate_from_songs failed."); 538 return NULL; 539 } 540 } 541 if (out) { 542 type = file_type(out); 543 switch (type) { 544 case FILE_TYPE_ERROR: { 545 tmp = filepath_dirname(out); 546 type = file_type(tmp); 547 switch (type) { 548 case FILE_TYPE_FOLDER: 549 pdf_filepath = strdup(out); 550 break; 551 default: 552 util_log(NULL, 0, LOG_ERR, "The -o/--output value '%s' is not an existing folder.", out); 553 } 554 break; 555 } 556 case FILE_TYPE_REG_FILE: { 557 pdf_filepath = strdup(out); 558 break; 559 } 560 case FILE_TYPE_FOLDER: { 561 pdf_filepath = filepath_add_ending_slash_if_missing(out); 562 pdf_filepath = erealloc(pdf_filepath, (strlen(pdf_filepath)+strlen(filename)+1) * sizeof(char)); 563 strcat(pdf_filepath, filename); 564 break; 565 } 566 case FILE_TYPE_OTHER: { 567 util_log(NULL, 0, LOG_ERR, "Invalid argument --output/-o value. It doesn't refer to a folder or regular file."); 568 break; 569 } 570 } 571 } else { 572 if (filepath) { 573 pdf_filepath = strdup(filepath); 574 } else { 575 pdf_filepath = strdup(filename); 576 } 577 } 578 free(filepath); 579 free(filename); 580 free(tmp); 581 return pdf_filepath; 582 } 583 584 static bool 585 pdf_font_set(struct PDFContext *ctx, pdfio_stream_t *stream, struct Font *font) 586 { 587 static int page_index = 0; 588 char *name = fnt_name_create(font); 589 if ( 590 !strcmp(name, ctx->current_font_name) && 591 font->size == ctx->current_font_size && 592 ctx->current_page_index == page_index 593 ) { 594 free(name); 595 return true; 596 } 597 if (!pdfioContentSetTextFont(stream, name, font->size)) { 598 DEBUG("pdfioContentSetTextFont failed."); 599 free(name); 600 return false; 601 } 602 strcpy(ctx->current_font_name, name); 603 ctx->current_font_size = font->size; 604 page_index = ctx->current_page_index; 605 free(name); 606 return true; 607 } 608 609 static double 610 text_width(struct PDFContext *ctx, const char *text, struct ChoStyle *style) 611 { 612 char *name = fnt_name_create(style->font); 613 pdfio_obj_t *font_obj = out_pdf_fnt_obj_get_by_name(ctx, name); 614 if (!font_obj) { 615 DEBUG("out_pdf_fnt_obj_get_by_name failed."); 616 free(name); 617 return -1.0; 618 } 619 free(name); 620 return pdfioContentTextMeasure(font_obj, text, style->font->size); 621 } 622 623 static double 624 text_above_width(struct PDFContext *ctx, struct ChoLineItemAbove *above) 625 { 626 double width; 627 if (above->is_chord) { 628 char *name; 629 name = cho_chord_name_generate(above->u.chord); 630 width = text_width(ctx, name, above->u.chord->style); 631 if (width == ERROR) { 632 DEBUG("text_width failed."); 633 free(name); 634 return ERROR; 635 } 636 free(name); 637 } else { 638 width = text_width(ctx, above->u.annot->text, above->u.annot->style); 639 if (width == ERROR) { 640 DEBUG("text_width failed."); 641 return ERROR; 642 } 643 } 644 return width; 645 } 646 647 static int 648 find_whitespace(const char *str, size_t start) 649 { 650 int i; 651 for (i = start; i>=0; i--) { 652 if (str[i] == ' ' || str[i] == '\t') { 653 return i; 654 } 655 } 656 return -1; 657 } 658 659 static int 660 text_find_fitting( 661 struct PDFContext *ctx, 662 const char *str, 663 struct ChoStyle *style, 664 double x, 665 double max_width 666 ) 667 { 668 size_t len = strlen(str); 669 size_t start = len - 1; 670 char tmp[len+1]; 671 strcpy((char *)&tmp, str); 672 double width; 673 int i; 674 675 do { 676 i = find_whitespace((const char *)&tmp, start); 677 if (i == -1) { 678 util_log(NULL, 0, LOG_ERR, "Can't split text because no whitespace was found."); 679 return -1; 680 } 681 tmp[i] = 0; 682 width = text_width(ctx, (const char *)&tmp, style); 683 if (width == ERROR) { 684 DEBUG("text_width failed."); 685 return -1; 686 } 687 start = i - 1; 688 } while (x + width > max_width); 689 return i; 690 } 691 692 static bool 693 pdf_draw_line( 694 pdfio_stream_t *stream, 695 struct PDFText *text, 696 double y, 697 double width, 698 enum LineLocation line_location 699 ) 700 { 701 double red, green, blue; 702 703 switch (line_location) { 704 case LINE_LOCATION_UNDER: 705 red = text->style->underline_color->red / 255.0; 706 green = text->style->underline_color->green / 255.0; 707 blue = text->style->underline_color->blue / 255.0; 708 y -= 2.0; 709 break; 710 case LINE_LOCATION_STRIKETHROUGH: 711 red = text->style->strikethrough_color->red / 255.0; 712 green = text->style->strikethrough_color->green / 255.0; 713 blue = text->style->strikethrough_color->blue / 255.0; 714 y += text->style->font->size * 0.3; 715 break; 716 case LINE_LOCATION_OVER: 717 red = text->style->overline_color->red / 255.0; 718 green = text->style->overline_color->green / 255.0; 719 blue = text->style->overline_color->blue / 255.0; 720 y += text->style->font->size * 0.8; 721 break; 722 } 723 if (!pdfioContentPathMoveTo(stream, text->x, y)) { 724 DEBUG("pdfioContentPathMoveTo failed."); 725 return false; 726 } 727 if (!pdfioContentPathLineTo(stream, text->x + width, y)) { 728 DEBUG("pdfioContentPathLineTo failed."); 729 return false; 730 } 731 if (!pdfioContentSetStrokeColorRGB(stream, red, green, blue)) { 732 DEBUG("pdfioContentSetStrokeColorRGB failed."); 733 return false; 734 } 735 if (!pdfioContentStroke(stream)) { 736 DEBUG("pdfioContentStroke failed."); 737 return false; 738 } 739 return true; 740 } 741 742 static bool 743 pdf_draw_rectangle( 744 pdfio_stream_t *stream, 745 struct PDFText *text 746 ) 747 { 748 double height; 749 double red, green, blue; 750 751 red = text->style->background_color->red / 255.0; 752 green = text->style->background_color->green / 255.0; 753 blue = text->style->background_color->blue / 255.0; 754 if (!pdfioContentSetFillColorRGB(stream, red, green, blue)) { 755 fprintf(stderr, "pdfioContentSetFillColorRGB failed.\n"); 756 return false; 757 } 758 height = (8.0 + text->style->font->size) - text->style->font->size * 0.8; 759 if (!pdfioContentPathRect(stream, text->x, text->y, text->width, height)) { 760 fprintf(stderr, "pdfioContentPathRect failed.\n"); 761 return false; 762 } 763 if (!pdfioContentFill(stream, true)) { 764 fprintf(stderr, "pdfioContentFill failed.\n"); 765 return false; 766 } 767 return true; 768 } 769 770 static bool 771 is_purest_white(struct RGBColor *color) 772 { 773 if (color->red == 255 && color->green == 255 && color->blue == 255) { 774 return true; 775 } 776 return false; 777 } 778 779 static bool 780 pdf_text_show(struct PDFContext *ctx, pdfio_stream_t *stream, struct PDFText *text) 781 { 782 double red, green, blue; 783 bool unicode; 784 785 if (!pdf_font_set(ctx, stream, text->style->font)) { 786 DEBUG("pdf_font_set failed."); 787 return false; 788 } 789 unicode = !is_base_font(text->style->font); 790 if (!is_purest_white(text->style->background_color)) { 791 if (!pdf_draw_rectangle(stream, text)) { 792 DEBUG("draw_rectangle failed."); 793 return false; 794 } 795 } 796 red = text->style->foreground_color->red / 255.0; 797 green = text->style->foreground_color->green / 255.0; 798 blue = text->style->foreground_color->blue / 255.0; 799 if (!pdfioContentSetFillColorRGB(stream, red, green, blue)) { 800 DEBUG("pdfioContentSetFillColorRGB failed."); 801 return false; 802 } 803 if (!pdfioContentTextBegin(stream)) { 804 DEBUG("pdfioContentTextBegin failed."); 805 return false; 806 } 807 if (!pdfioContentTextMoveTo(stream, text->x, text->y)) { 808 DEBUG("pdfioContentTextMoveTo failed."); 809 return false; 810 } 811 if (!pdfioContentSetTextRise(stream, text->style->rise)) { 812 DEBUG("pdfioContentSetTextRise failed."); 813 return false; 814 } 815 if (!pdfioContentTextShow(stream, unicode, text->text)) { 816 DEBUG("pdfioContentTextShow failed."); 817 return false; 818 } 819 if (!pdfioContentTextEnd(stream)) { 820 DEBUG("pdfioContentTextEnd failed."); 821 return false; 822 } 823 if (text->style->underline_style == LINE_STYLE_SINGLE) { 824 pdf_draw_line(stream, text, text->y, text->width, LINE_LOCATION_UNDER); 825 } else if (text->style->underline_style == LINE_STYLE_DOUBLE) { 826 pdf_draw_line(stream, text, text->y, text->width, LINE_LOCATION_UNDER); 827 pdf_draw_line(stream, text, text->y-1.5, text->width, LINE_LOCATION_UNDER); 828 } 829 if (text->style->strikethrough) { 830 pdf_draw_line(stream, text, text->y, text->width, LINE_LOCATION_STRIKETHROUGH); 831 } 832 if (text->style->overline_style == LINE_STYLE_SINGLE) { 833 pdf_draw_line(stream, text, text->y, text->width, LINE_LOCATION_OVER); 834 } else if (text->style->overline_style == LINE_STYLE_DOUBLE) { 835 pdf_draw_line(stream, text, text->y, text->width, LINE_LOCATION_OVER); 836 pdf_draw_line(stream, text, text->y+1.5, text->width, LINE_LOCATION_OVER); 837 } 838 if (text->style->boxed) { 839 red = text->style->boxed_color->red / 255.0; 840 green = text->style->boxed_color->green / 255.0; 841 blue = text->style->boxed_color->blue / 255.0; 842 if (!pdfioContentSetStrokeColorRGB(stream, red, green, blue)) { 843 DEBUG("pdfioContentSetFillColorRGB failed."); 844 return false; 845 } 846 if (!pdfioContentPathRect( 847 stream, 848 text->x - 2.0, 849 text->y - 2.0, 850 text->width + 4.0, 851 text->style->font->size * 0.8 + 4.0 852 )) { 853 DEBUG("pdfioContentPathRect failed."); 854 return false; 855 } 856 if (!pdfioContentStroke(stream)) { 857 DEBUG("pdfioContentStroke failed."); 858 return false; 859 } 860 } 861 return true; 862 } 863 864 static bool 865 annot_page_link_add( 866 struct PDFContext *ctx, 867 struct TocEntry *entry, 868 int toc_page_count, 869 int line_count, 870 double font_size 871 ) 872 { 873 int page_index; 874 pdfio_rect_t rect; 875 pdfio_dict_t *annot; 876 pdfio_array_t *destination; 877 878 rect.x1 = MARGIN_HORIZONTAL; 879 rect.x2 = MARGIN_HORIZONTAL + LINE_WIDTH; 880 rect.y1 = ctx->t_ctx.y - 2.0; 881 rect.y2 = ctx->t_ctx.y + (8.0 + font_size) * line_count - font_size * 0.8; 882 page_index = entry->page_index + toc_page_count; 883 annot = pdfioDictCreate(ctx->pdf_file); 884 if (!pdfioDictSetName(annot, "Subtype", "Link")) { 885 DEBUG("pdfioDictSetName failed."); 886 return false; 887 } 888 if (!pdfioDictSetRect(annot, "Rect", &rect)) { 889 DEBUG("pdfioDictSetRect failed."); 890 return false; 891 } 892 destination = pdfioArrayCreate(ctx->pdf_file); 893 if (!pdfioArrayAppendNumber(destination, page_index)) { 894 DEBUG("pdfioArrayAppendNumber failed."); 895 return false; 896 } 897 if (!pdfioArrayAppendName(destination, "FitH")) { 898 DEBUG("pdfioArrayAppendName failed."); 899 return false; 900 } 901 // TODO: Is this constant '30.0' correct with different font sizes, etc. ? 902 // clicking the annotation should show the song including the song title at the top 903 if (!pdfioArrayAppendNumber(destination, entry->page_y + 30.0)) { 904 DEBUG("pdfioArrayAppendNumber failed."); 905 return false; 906 } 907 if (!pdfioDictSetArray(annot, "Dest", destination)) { 908 DEBUG("pdfioDictSetArray failed."); 909 return false; 910 } 911 if (!pdfioArrayAppendDict(ctx->t_ctx.content->pages[ctx->t_ctx.page]->annots, annot)) { 912 DEBUG("pdfioArrayAppendDict failed."); 913 return false; 914 } 915 return true; 916 } 917 918 static bool 919 annot_url_link_add( 920 struct PDFContext *ctx, 921 struct PDFContentContext *c_ctx, 922 struct ChoStyle *style, 923 double width 924 ) 925 { 926 pdfio_rect_t rect; 927 pdfio_dict_t *annot, *action; 928 929 rect.x1 = c_ctx->x; 930 rect.x2 = c_ctx->x + width; 931 rect.y1 = c_ctx->y - 2.0; 932 rect.y2 = c_ctx->y + style->font->size * 0.8; 933 annot = pdfioDictCreate(ctx->pdf_file); 934 if (!pdfioDictSetName(annot, "Subtype", "Link")) { 935 DEBUG("pdfioDictSetName failed."); 936 return false; 937 } 938 if (!pdfioDictSetRect(annot, "Rect", &rect)) { 939 DEBUG("pdfioDictSetRect failed."); 940 return false; 941 } 942 action = pdfioDictCreate(ctx->pdf_file); 943 if (!pdfioDictSetName(action, "S", "URI")) { 944 DEBUG("pdfioDictSetName failed."); 945 return false; 946 } 947 if (!pdfioDictSetString(action, "URI", style->href)) { 948 DEBUG("pdfioDictSetString failed."); 949 return false; 950 } 951 if (!pdfioDictSetDict(annot, "A", action)) { 952 DEBUG("pdfioDictSetDict failed."); 953 return false; 954 } 955 if (!pdfioArrayAppendDict(c_ctx->content->pages[c_ctx->page]->annots, annot)) { 956 DEBUG("pdfioArrayAppendDict failed."); 957 return false; 958 } 959 return true; 960 } 961 962 static bool 963 pdf_set_title(struct PDFContext *ctx, struct ChoSong **songs) 964 { 965 // INFO: Set pdf title only if a single song exist 966 if (songs[0] && !songs[1]) { 967 char *title; 968 struct ChoStyle *unused; 969 if (!cho_metadata_value(songs[0]->metadata, "title", ctx->config->metadata_separator, &title, &unused)) { 970 DEBUG("cho_metadata_value failed."); 971 return false; 972 } 973 pdfioFileSetTitle(ctx->pdf_file, title); 974 free(title); 975 } 976 return true; 977 } 978 979 static pdfio_stream_t * 980 pdf_page_create( 981 struct PDFContext *ctx, 982 struct PDFImage **imgs, 983 pdfio_array_t *annots 984 ) 985 { 986 pdfio_dict_t *page_dict; 987 pdfio_stream_t *page_stream; 988 pdfio_array_t *color_array; 989 struct Obj **f; 990 struct PDFImage **i; 991 992 color_array = pdfioArrayCreateColorFromStandard(ctx->pdf_file, 3, PDFIO_CS_ADOBE); 993 page_dict = pdfioDictCreate(ctx->pdf_file); 994 if (!pdfioPageDictAddColorSpace(page_dict, "rgbcolorspace", color_array)) { 995 DEBUG("pdfioPageDictAddColorSpace failed."); 996 return NULL; 997 } 998 if (!pdfioDictSetArray(page_dict, "Annots", annots)) { 999 DEBUG("pdfioDictSetArray failed."); 1000 return NULL; 1001 } 1002 if (imgs) { 1003 for (i = imgs; *i; i++) { 1004 if (!pdfioPageDictAddImage(page_dict, (*i)->name, (*i)->obj)) { 1005 DEBUG("pdfioPageDictAddImage failed."); 1006 return NULL; 1007 } 1008 } 1009 } 1010 for (f = ctx->fonts; *f; f++) { 1011 if (!pdfioPageDictAddFont(page_dict, (*f)->name, (*f)->value)) { 1012 DEBUG("pdfioPageDictAddFont failed."); 1013 return NULL; 1014 } 1015 } 1016 page_stream = pdfioFileCreatePage(ctx->pdf_file, page_dict); 1017 if (!pdfioContentSetFillColorSpace(page_stream, "rgbcolorspace")) { 1018 DEBUG("pdfioContentSetFillColorSpace failed."); 1019 return NULL; 1020 } 1021 if (!pdfioContentSetStrokeColorSpace(page_stream, "rgbcolorspace")) { 1022 DEBUG("pdfioContentSetStrokeColorSpace failed."); 1023 return NULL; 1024 } 1025 return page_stream; 1026 } 1027 1028 static double 1029 image_width(struct ChoImage *image, pdfio_obj_t *obj) 1030 { 1031 double width; 1032 width = pdfioImageGetWidth(obj); 1033 if (image->width) { 1034 if (image->width->type == SIZE_TYPE_POINT) { 1035 width = image->width->d; 1036 } else 1037 if (image->width->type == SIZE_TYPE_PERCENT) { 1038 width = LINE_WIDTH * image->width->d; 1039 } 1040 } 1041 if (image->width_scale) { 1042 width *= image->width_scale->d; 1043 } 1044 width += image->border; 1045 return width; 1046 } 1047 1048 static double 1049 image_height(struct ChoImage *image, pdfio_obj_t *obj) 1050 { 1051 double height; 1052 height = pdfioImageGetHeight(obj); 1053 if (image->height) { 1054 if (image->height->type == SIZE_TYPE_POINT) { 1055 height = image->height->d; 1056 } else 1057 if (image->height->type == SIZE_TYPE_PERCENT) { 1058 height = LINE_WIDTH * image->height->d; 1059 } 1060 } 1061 if (image->height_scale) { 1062 height *= image->height_scale->d; 1063 } 1064 height += image->border; 1065 return height; 1066 } 1067 1068 static char * 1069 image_name(struct PDFContext *ctx, struct ChoImage *image) 1070 { 1071 char tmp[PATH_MAX]; 1072 char *image_path; 1073 struct stat s; 1074 1075 if (strchr(image->src, '/')) { 1076 image_path = image->src; 1077 } else { 1078 strcpy((char *)&tmp, ctx->cho_dirpath); 1079 strcat((char *)&tmp, "/"); 1080 strcat((char *)&tmp, image->src); 1081 image_path = (char *)&tmp; 1082 } 1083 if (stat(image_path, &s)) { 1084 DEBUG("stat failed."); 1085 util_log(NULL, 0, LOG_ERR, "%s: %s", image_path, strerror(errno)); 1086 return NULL; 1087 } 1088 memset(tmp, 0, PATH_MAX); 1089 snprintf((char *)&tmp, PATH_MAX, "%ld", s.st_ino); 1090 return strdup(tmp); 1091 } 1092 1093 static bool 1094 pdf_load_images(struct PDFContext *ctx, struct Obj ***images, struct ChoSong **songs) 1095 { 1096 struct ChoSong **s; 1097 struct ChoSection **se; 1098 struct ChoLine **li; 1099 struct ChoLineItem **it; 1100 int i = 0; 1101 char filepath[PATH_MAX]; 1102 char *name; 1103 char *image_filepath; 1104 1105 for (s = songs; *s; s++) { 1106 for (se = (*s)->sections; *se; se++) { 1107 for (li = (*se)->lines; *li; li++) { 1108 for (it = (*li)->items; *it; it++) { 1109 if (!(*it)->is_text) { 1110 memset(filepath, 0, PATH_MAX); 1111 strcpy((char *)&filepath, ctx->cho_dirpath); 1112 strcat((char *)&filepath, "/"); 1113 strcat((char *)&filepath, (*it)->u.image->src); 1114 name = image_name(ctx, (*it)->u.image); 1115 if (!name) { 1116 DEBUG("image_name failed."); 1117 return false; 1118 } 1119 if (!objs_get_obj(*images, name)) { 1120 *images = erealloc(*images, (i+2) * sizeof(struct Obj *)); 1121 (*images)[i] = obj_new(); 1122 (*images)[i]->name = strdup(name); 1123 if (strchr((*it)->u.image->src, '/')) { 1124 image_filepath = (*it)->u.image->src; 1125 } else { 1126 image_filepath = (char *)&filepath; 1127 } 1128 (*images)[i]->value = pdfioFileCreateImageObjFromFile(ctx->pdf_file, image_filepath, true); 1129 if (!(*images)[i]->value) { 1130 DEBUG("pdfioFileCreateImageObjFromFile failed."); 1131 free(name); 1132 return false; 1133 } 1134 util_log(NULL, 0, LOG_INFO, "Loaded image from '%s'.", image_filepath); 1135 (*images)[i+1] = NULL; 1136 } 1137 free(name); 1138 i++; 1139 } 1140 } 1141 } 1142 } 1143 } 1144 return true; 1145 } 1146 1147 static struct ChoChord ** 1148 pdf_get_chords(struct ChoSong *song) 1149 { 1150 struct ChoSection **se; 1151 struct ChoLine **li; 1152 struct ChoLineItemAbove **above; 1153 struct ChoChord **chords = NULL; 1154 1155 for (se = song->sections; *se; se++) { 1156 for (li = (*se)->lines; *li; li++) { 1157 for (above = (*li)->text_above; *above; above++) { 1158 if ((*above)->is_chord) { 1159 if (!cho_chords_has(chords, (*above)->u.chord)) { 1160 cho_chords_add(&chords, (*above)->u.chord); 1161 } 1162 } 1163 } 1164 } 1165 } 1166 return chords; 1167 } 1168 1169 static double 1170 line_width_until_text_above( 1171 struct PDFContext *ctx, 1172 struct ChoLineItem **items, 1173 struct ChoLineItemAbove *above, 1174 struct Obj **img_objs, 1175 struct SpaceNeeded *space 1176 ) 1177 { 1178 int i, save_i, save_k; 1179 int k = EMPTY_INT; 1180 int pos = 0; 1181 double width = 0.0; 1182 char *name; 1183 pdfio_obj_t *font_obj; 1184 pdfio_obj_t *obj; 1185 struct ChoText *text; 1186 1187 for (i = 0; items[i]; i++) { 1188 if (items[i]->is_text) { 1189 for (k = 0; items[i]->u.text->text[k]; k++) { 1190 if (above->position == pos) { 1191 save_i = i; 1192 save_k = k; 1193 goto FOUND; 1194 } 1195 pos++; 1196 } 1197 } 1198 } 1199 /* INFO: If the chord/annotation is the last thing in the line */ 1200 save_i = i; 1201 save_k = k; 1202 FOUND: 1203 if (space) { 1204 space->line_item_index = save_i; 1205 space->text_index = save_k; 1206 } 1207 for (i = 0; items[i] && i <= save_i; i++) { 1208 if (items[i]->is_text) { 1209 text = items[i]->u.text; 1210 name = fnt_name_create(text->style->font); 1211 font_obj = out_pdf_fnt_obj_get_by_name(ctx, name); 1212 if (!font_obj) { 1213 DEBUG("out_pdf_fnt_obj_get_by_name failed."); 1214 free(name); 1215 return ERROR; 1216 } 1217 free(name); 1218 if (save_i == i) { 1219 char tmp[strlen(text->text)+1]; 1220 strcpy((char *)&tmp, text->text); 1221 tmp[save_k] = 0; 1222 width += pdfioContentTextMeasure(font_obj, (const char *)&tmp, text->style->font->size); 1223 } else { 1224 width += pdfioContentTextMeasure(font_obj, text->text, text->style->font->size); 1225 } 1226 } else { 1227 name = image_name(ctx, items[i]->u.image); 1228 if (!name) { 1229 DEBUG("image_name failed."); 1230 return ERROR; 1231 } 1232 obj = objs_get_obj(img_objs, name); 1233 if (!obj) { 1234 DEBUG("objs_get_obj failed."); 1235 free(name); 1236 return ERROR; 1237 } 1238 width += image_width(items[i]->u.image, obj); 1239 free(name); 1240 } 1241 } 1242 return width; 1243 } 1244 1245 static struct PDFImage * 1246 pdf_image_new(void) 1247 { 1248 struct PDFImage *image = emalloc(sizeof(struct PDFImage)); 1249 image->x = 0.0; 1250 image->y = 0.0; 1251 image->width = 0.0; 1252 image->height = 0.0; 1253 image->name = NULL; 1254 image->obj = NULL; 1255 return image; 1256 } 1257 1258 static void 1259 pdf_image_free(struct PDFImage *image) 1260 { 1261 if (!image) { 1262 return; 1263 } 1264 free(image->name); 1265 free(image); 1266 } 1267 1268 static struct PDFText * 1269 pdf_text_new(void) 1270 { 1271 struct PDFText *t = emalloc(sizeof(struct PDFText)); 1272 t->text = NULL; 1273 t->style = NULL; 1274 t->x = 0.0; 1275 t->y = 0.0; 1276 t->width = 0.0; 1277 return t; 1278 } 1279 1280 static void 1281 pdf_text_free(struct PDFText *text) 1282 { 1283 if (!text) { 1284 return; 1285 } 1286 free(text->text); 1287 cho_style_free(text->style); 1288 free(text); 1289 } 1290 1291 static void 1292 toc_entry_free(struct TocEntry *entry) 1293 { 1294 if (!entry) { 1295 return; 1296 } 1297 free(entry->title); 1298 free(entry); 1299 } 1300 1301 static struct PDFPage * 1302 pdf_page_new(struct PDFContext *ctx) 1303 { 1304 struct PDFPage *page = emalloc(sizeof(struct PDFPage)); 1305 page->texts = NULL; 1306 page->images = NULL; 1307 page->diagrams = NULL; 1308 page->annots = pdfioArrayCreate(ctx->pdf_file); 1309 return page; 1310 } 1311 1312 static void 1313 pdf_page_free(struct PDFPage *page) 1314 { 1315 if (!page) { 1316 return; 1317 } 1318 struct PDFText **t; 1319 struct PDFImage **i; 1320 struct ChordDiagram **d; 1321 if ((t = page->texts)) { 1322 for (; *t; t++) { 1323 pdf_text_free(*t); 1324 } 1325 free(page->texts); 1326 } 1327 if ((i = page->images)) { 1328 for (; *i; i++) { 1329 pdf_image_free(*i); 1330 } 1331 free(page->images); 1332 } 1333 if ((d = page->diagrams)) { 1334 for (; *d; d++) { 1335 chord_diagram_free(*d); 1336 } 1337 free(page->diagrams); 1338 } 1339 free(page); 1340 } 1341 1342 static void 1343 pdf_content_context_init(struct PDFContentContext *c_ctx) 1344 { 1345 c_ctx->content = NULL; 1346 c_ctx->x = MARGIN_HORIZONTAL; 1347 c_ctx->y = MEDIABOX_HEIGHT - MARGIN_TOP; 1348 c_ctx->text = 0; 1349 c_ctx->image = 0; 1350 c_ctx->diagram = 0; 1351 c_ctx->page = 0; 1352 c_ctx->toc_entry = 0; 1353 c_ctx->spaces = NULL; 1354 c_ctx->biggest_font_size = 0.0; 1355 c_ctx->consumed_lyrics = 0; 1356 c_ctx->prev_added_space = 0.0; 1357 c_ctx->margin_bottom = MARGIN_BOTTOM; 1358 } 1359 1360 static struct PDFContent * 1361 pdf_content_new(void) 1362 { 1363 struct PDFContent *content = emalloc(sizeof(struct PDFContent)); 1364 content->pages = NULL; 1365 content->toc = NULL; 1366 return content; 1367 } 1368 1369 static void 1370 pdf_content_free(struct PDFContent *content) 1371 { 1372 if (!content) { 1373 return; 1374 } 1375 struct PDFPage **p; 1376 struct TocEntry **toc; 1377 for (p = content->pages; *p; p++) { 1378 pdf_page_free(*p); 1379 } 1380 free(content->pages); 1381 if ((toc = content->toc)) { 1382 for (; *toc; toc++) { 1383 toc_entry_free(*toc); 1384 } 1385 free(content->toc); 1386 } 1387 free(content); 1388 } 1389 1390 static void 1391 spaces_free(struct SpaceNeeded **spaces) 1392 { 1393 if (!spaces) { 1394 return; 1395 } 1396 struct SpaceNeeded **s; 1397 for (s = spaces; *s; s++) { 1398 free(*s); 1399 } 1400 free(spaces); 1401 } 1402 1403 static bool 1404 calc_space_between_text_above( 1405 struct PDFContext *ctx, 1406 struct ChoLineItem **items, 1407 struct ChoLineItemAbove **text_above, 1408 struct Obj **img_objs, 1409 struct SpaceNeeded ***spaces 1410 ) 1411 { 1412 if (!*text_above) { 1413 return true; 1414 } 1415 int sp = 0; 1416 int i; 1417 for (i = 1; text_above[i]; i++) { 1418 struct SpaceNeeded space = { 1419 .line_item_index = 0, 1420 .text_index = 0, 1421 .text_above_index = 0, 1422 .amount = 0.0 1423 }; 1424 double width_between; 1425 double width_until_cur; 1426 double width_until_prev; 1427 double prev_width; 1428 width_until_cur = line_width_until_text_above(ctx, items, text_above[i], img_objs, NULL); 1429 if (width_until_cur == ERROR) { 1430 DEBUG("line_width_until_text_above failed."); 1431 return false; 1432 } 1433 i--; 1434 width_until_prev = line_width_until_text_above(ctx, items, text_above[i], img_objs, &space); 1435 if (width_until_prev == ERROR) { 1436 DEBUG("line_width_until_text_above failed."); 1437 return false; 1438 } 1439 prev_width = text_above_width(ctx, text_above[i]); 1440 if (prev_width == ERROR) { 1441 DEBUG("text_above_width failed."); 1442 return false; 1443 } 1444 i++; 1445 width_between = width_until_cur - width_until_prev; 1446 if (prev_width + MIN_CHORD_GAP_WIDTH >= width_between) { 1447 space.text_above_index = i; 1448 space.amount = prev_width - width_between + MIN_CHORD_GAP_WIDTH; 1449 *spaces = erealloc(*spaces, (sp+1) * sizeof(struct SpaceNeeded *)); 1450 (*spaces)[sp] = emalloc(sizeof(struct SpaceNeeded)); 1451 *((*spaces)[sp]) = space; 1452 sp++; 1453 } else { 1454 } 1455 } 1456 *spaces = erealloc(*spaces, (sp+1) * sizeof(struct SpaceNeeded *)); 1457 (*spaces)[sp] = NULL; 1458 return true; 1459 } 1460 1461 static double 1462 item_width( 1463 struct PDFContext *ctx, 1464 struct ChoLineItem *item, 1465 int i, 1466 struct SpaceNeeded **spaces, 1467 struct Obj **img_objs 1468 ) 1469 { 1470 char *name; 1471 double width; 1472 pdfio_obj_t *obj; 1473 struct SpaceNeeded **s; 1474 1475 if (item->is_text) { 1476 width = text_width(ctx, item->u.text->text, item->u.text->style); 1477 if (width == ERROR) { 1478 DEBUG("text_width failed."); 1479 return ERROR; 1480 } 1481 if (spaces) { 1482 for (s = spaces; *s; s++) { 1483 if ((*s)->line_item_index == i) { 1484 width += (*s)->amount; 1485 } 1486 } 1487 } 1488 } else { 1489 name = image_name(ctx, item->u.image); 1490 if (!name) { 1491 DEBUG("image_name failed."); 1492 return ERROR; 1493 } 1494 obj = objs_get_obj(img_objs, name); 1495 if (!obj) { 1496 DEBUG("objs_get_obj failed."); 1497 free(name); 1498 return ERROR; 1499 } 1500 free(name); 1501 width = image_width(item->u.image, obj); 1502 } 1503 return width; 1504 } 1505 1506 static struct CharPosition * 1507 items_find_position_to_break_line( 1508 struct PDFContext *ctx, 1509 struct ChoLineItem **items, 1510 struct SpaceNeeded **spaces, 1511 struct Obj **img_objs 1512 ) 1513 { 1514 int i; 1515 double d; 1516 double width = 0.0; 1517 struct CharPosition *pos; 1518 1519 pos = emalloc(sizeof(struct CharPosition)); 1520 pos->line_item_index = EMPTY_INT; 1521 pos->text_index = EMPTY_INT; 1522 for (i = 0; items[i]; i++) { 1523 d = item_width(ctx, items[i], i, spaces, img_objs); 1524 if (d == ERROR) { 1525 DEBUG("item_width failed."); 1526 goto ERR; 1527 } 1528 if (width + d > LINE_WIDTH) { 1529 if (items[i]->is_text) { 1530 pos->line_item_index = i; 1531 pos->text_index = text_find_fitting( 1532 ctx, 1533 items[i]->u.text->text, 1534 items[i]->u.text->style, 1535 width, 1536 LINE_WIDTH 1537 ); 1538 if (pos->text_index == EMPTY_INT) { 1539 DEBUG("text_find_fitting failed."); 1540 goto ERR; 1541 } 1542 } else { 1543 pos->line_item_index = i; 1544 pos->text_index = -1; 1545 } 1546 return pos; 1547 } else { 1548 width += d; 1549 } 1550 } 1551 return pos; 1552 ERR: 1553 free(pos); 1554 return NULL; 1555 } 1556 1557 static double 1558 images_find_biggest_height( 1559 struct PDFContext *ctx, 1560 struct ChoLineItem **left_items, 1561 int line_item_index, 1562 struct Obj **img_objs 1563 ) 1564 { 1565 struct ChoLineItem **it; 1566 pdfio_obj_t *obj; 1567 char *name; 1568 int i = 0; 1569 double end = line_item_index; 1570 double biggest = 0.0; 1571 double height; 1572 1573 // TODO: Why 10000? 1574 if (end == -1) { 1575 end = 10000; 1576 } 1577 for (it = left_items; *it && i <= end; it++, i++) { 1578 if (!(*it)->is_text) { 1579 name = image_name(ctx, (*it)->u.image); 1580 if (!name) { 1581 DEBUG("image_name failed."); 1582 return ERROR; 1583 } 1584 obj = objs_get_obj(img_objs, name); 1585 if (!obj) { 1586 DEBUG("objs_get_obj failed."); 1587 free(name); 1588 return ERROR; 1589 } 1590 height = image_height((*it)->u.image, obj); 1591 if (height > biggest) { 1592 biggest = height; 1593 } 1594 free(name); 1595 } 1596 } 1597 return biggest; 1598 } 1599 1600 static int 1601 text_above_find_index_to_break_line( 1602 struct ChoLineItem **items, 1603 struct ChoLineItemAbove **text_above, 1604 struct CharPosition *pos 1605 ) 1606 { 1607 int position = 0; 1608 int i, k; 1609 1610 if (pos->text_index == -1) { 1611 for (i = 0; items[i]; i++) { 1612 if (pos->line_item_index == i) { 1613 goto FOUND; 1614 } 1615 if (items[i]->is_text) { 1616 for (k = 0; items[i]->u.text->text[k]; k++) { 1617 position++; 1618 } 1619 } 1620 } 1621 } else { 1622 for (i = 0; items[i]; i++) { 1623 if (items[i]->is_text) { 1624 for (k = 0; items[i]->u.text->text[k]; k++) { 1625 if (pos->line_item_index == i && pos->text_index == k) { 1626 goto FOUND; 1627 } 1628 position++; 1629 } 1630 } 1631 } 1632 } 1633 return -2; 1634 FOUND: 1635 for (i = 0; text_above[i]; i++) { 1636 if (text_above[i]->position >= position) { 1637 return i; 1638 } 1639 } 1640 return -1; 1641 } 1642 1643 static void 1644 text_above_update_positions(struct ChoLineItemAbove **aboves, size_t consumed_lyrics) 1645 { 1646 struct ChoLineItemAbove **a; 1647 for (a = aboves; *a; a++) { 1648 (*a)->position -= consumed_lyrics + 1; // why plus one? 1649 } 1650 } 1651 1652 static double 1653 calc_x(double width, enum Alignment align) 1654 { 1655 if (align == ALIGNMENT_CENTER) { 1656 return MARGIN_HORIZONTAL + (LINE_WIDTH - width) / 2; 1657 } 1658 return MARGIN_HORIZONTAL; 1659 } 1660 1661 static const char * 1662 handle_index(int index, char a, char b, char c) 1663 { 1664 int i = 0; 1665 static char str[16]; 1666 1667 memset(&str, 0, sizeof(str)); 1668 switch (index) { 1669 case 1: 1670 str[i++] = a; 1671 break; 1672 case 2: 1673 str[i++] = a; 1674 str[i++] = a; 1675 break; 1676 case 3: 1677 str[i++] = a; 1678 str[i++] = a; 1679 str[i++] = a; 1680 break; 1681 case 4: 1682 str[i++] = a; 1683 str[i++] = b; 1684 break; 1685 case 5: 1686 str[i++] = b; 1687 break; 1688 case 6: 1689 str[i++] = b; 1690 str[i++] = a; 1691 break; 1692 case 7: 1693 str[i++] = b; 1694 str[i++] = a; 1695 str[i++] = a; 1696 break; 1697 case 8: 1698 str[i++] = b; 1699 str[i++] = a; 1700 str[i++] = a; 1701 str[i++] = a; 1702 break; 1703 case 9: 1704 str[i++] = a; 1705 str[i++] = c; 1706 } 1707 str[i] = 0; 1708 return str; 1709 } 1710 1711 static const char * 1712 numeral_system_western_arabic_to_roman(unsigned int n) 1713 { 1714 if (n > 999) { 1715 util_log(NULL, 0, LOG_ERR, "Converting numbers higher than 999 is not supported."); 1716 return NULL; 1717 } 1718 const char *str; 1719 static char roman[64]; 1720 int k; 1721 int r = 0; 1722 char i = 'I'; 1723 char v = 'V'; 1724 char x = 'X'; 1725 char l = 'L'; 1726 char c = 'C'; 1727 char d = 'D'; 1728 char m = 'M'; 1729 int index_0 = n - n / 10 * 10; 1730 int index_1 = (n - n / 100 * 100 - index_0) / 10; 1731 int index_2 = (n - n / 1000 * 1000 - index_0 - index_1) / 100; 1732 if (index_2 > 0) { 1733 str = handle_index(index_2, c, d, m); 1734 for (k = 0; str[k]; k++, r++) { 1735 roman[r] = str[k]; 1736 } 1737 } 1738 if (index_1 > 0) { 1739 str = handle_index(index_1, x, l, c); 1740 for (k = 0; str[k]; k++, r++) { 1741 roman[r] = str[k]; 1742 } 1743 } 1744 if (index_0 > 0) { 1745 str = handle_index(index_0, i, v, x); 1746 for (k = 0; str[k]; k++, r++) { 1747 roman[r] = str[k]; 1748 } 1749 } 1750 roman[r] = 0; 1751 return roman; 1752 } 1753 1754 static const char * 1755 numeral_system_number_to_str(enum NumeralSystem system, int n) 1756 { 1757 if (system == NUMERAL_SYSTEM_ROMAN) { 1758 const char *str = numeral_system_western_arabic_to_roman((unsigned int)n); 1759 if (!str) { 1760 DEBUG("numeral_system_western_arabic_to_roman failed."); 1761 return NULL; 1762 } 1763 return str; 1764 } else { 1765 static char str[11+1]; 1766 snprintf((char *)&str, 11+1, "%d", n); 1767 return str; 1768 } 1769 } 1770 1771 static bool 1772 pdf_page_add_page_no( 1773 struct PDFContext *ctx, 1774 struct PDFContentContext *c_ctx, 1775 enum NumeralSystem numeral_system 1776 ) 1777 { 1778 struct PDFText ***texts; 1779 struct ChoStyle *style; 1780 const char *page_no; 1781 double width, x; 1782 1783 style = cho_style_new(); 1784 style->font->family = strdup(DEFAULT_FONT); 1785 texts = &c_ctx->content->pages[c_ctx->page]->texts; 1786 page_no = numeral_system_number_to_str(numeral_system, c_ctx->page+1); 1787 if (!page_no) { 1788 DEBUG("numeral_system_number_to_str failed."); 1789 cho_style_free(style); 1790 return false; 1791 } 1792 width = text_width(ctx, page_no, style); 1793 if (width == ERROR) { 1794 DEBUG("text_width failed."); 1795 cho_style_free(style); 1796 return false; 1797 } 1798 *texts = erealloc(*texts, (c_ctx->text+1) * sizeof(struct PDFText *)); 1799 (*texts)[c_ctx->text] = pdf_text_new(); 1800 (*texts)[c_ctx->text]->text = strdup(page_no); 1801 (*texts)[c_ctx->text]->style = style; 1802 (*texts)[c_ctx->text]->x = MEDIABOX_WIDTH - MARGIN_HORIZONTAL / 2 - width; 1803 (*texts)[c_ctx->text]->y = MARGIN_BOTTOM / 2; 1804 (*texts)[c_ctx->text]->width = width; 1805 switch (ctx->config->output->page_no->align) { 1806 case ALIGNMENT_LEFT: 1807 x = MARGIN_HORIZONTAL / 2; 1808 break; 1809 case ALIGNMENT_CENTER: 1810 x = MARGIN_HORIZONTAL + (LINE_WIDTH - width) / 2; 1811 break; 1812 case ALIGNMENT_RIGHT: 1813 x = MEDIABOX_WIDTH - MARGIN_HORIZONTAL / 2 - width; 1814 break; 1815 default: 1816 util_log(NULL, 0, LOG_ERR, "Invalid Alignment enum value '%d'.", ctx->config->output->page_no->align); 1817 return false; 1818 } 1819 (*texts)[c_ctx->text]->x = x; 1820 c_ctx->text++; 1821 return true; 1822 } 1823 1824 static bool 1825 pdf_page_close_then_add( 1826 struct PDFContext *ctx, 1827 struct PDFContentContext *c_ctx, 1828 enum NumeralSystem numeral_system 1829 ) 1830 { 1831 c_ctx->content->pages[c_ctx->page]->texts = erealloc( 1832 c_ctx->content->pages[c_ctx->page]->texts, 1833 (c_ctx->text+1) * sizeof(struct PDFText *) 1834 ); 1835 c_ctx->content->pages[c_ctx->page]->texts[c_ctx->text] = NULL; 1836 c_ctx->content->pages[c_ctx->page]->images = erealloc( 1837 c_ctx->content->pages[c_ctx->page]->images, 1838 (c_ctx->image+1) * sizeof(struct PDFImage *) 1839 ); 1840 c_ctx->content->pages[c_ctx->page]->images[c_ctx->image] = NULL; 1841 c_ctx->content->pages[c_ctx->page]->diagrams = erealloc( 1842 c_ctx->content->pages[c_ctx->page]->diagrams, 1843 (c_ctx->diagram+1) * sizeof(struct ChordDiagram *) 1844 ); 1845 c_ctx->content->pages[c_ctx->page]->diagrams[c_ctx->diagram] = NULL; 1846 c_ctx->text = 0; 1847 c_ctx->image = 0; 1848 c_ctx->diagram = 0; 1849 c_ctx->page++; 1850 c_ctx->content->pages = erealloc(c_ctx->content->pages, (c_ctx->page+1) * sizeof(struct PDFPage *)); 1851 c_ctx->content->pages[c_ctx->page] = pdf_page_new(ctx); 1852 c_ctx->y = MEDIABOX_HEIGHT - MARGIN_TOP; 1853 if (ctx->config->output->page_no->show) { 1854 if (!pdf_page_add_page_no(ctx, c_ctx, numeral_system)) { 1855 DEBUG("pdf_page_add_page_no failed."); 1856 return false; 1857 } 1858 } 1859 return true; 1860 } 1861 1862 static int 1863 pdf_toc_page_count( 1864 struct PDFContext *ctx, 1865 struct TocEntry **entries, 1866 struct ChoStyle *style, 1867 double max_title_width 1868 ) 1869 { 1870 int index; 1871 int page = 0; 1872 double y = MEDIABOX_HEIGHT - MARGIN_TOP; 1873 double width; 1874 char *t; 1875 struct TocEntry **toc; 1876 1877 for (toc = entries; *toc; toc++) { 1878 char tmp[strlen((*toc)->title)+1]; 1879 1880 if (y < MARGIN_BOTTOM) { 1881 y = MEDIABOX_HEIGHT - MARGIN_TOP; 1882 page++; 1883 } 1884 strcpy((char *)&tmp, (*toc)->title); 1885 width = text_width(ctx, (*toc)->title, style); 1886 if (width == ERROR) { 1887 DEBUG("text_width failed."); 1888 return -1; 1889 } 1890 width += MARGIN_HORIZONTAL; 1891 if (width > max_title_width) { 1892 t = (char *)&tmp; 1893 while (width > max_title_width) { 1894 index = text_find_fitting(ctx, t, style, MARGIN_HORIZONTAL, max_title_width); 1895 if (index == EMPTY_INT) { 1896 DEBUG("text_find_fitting failed."); 1897 return -1; 1898 } 1899 t[index] = 0; 1900 y -= 8.0 + style->font->size; 1901 t += index + 1; 1902 width = text_width(ctx, t, style); 1903 if (width == ERROR) { 1904 DEBUG("text_width failed."); 1905 return -1; 1906 } 1907 } 1908 y -= 8.0 + style->font->size; 1909 } else { 1910 y -= 8.0 + style->font->size; 1911 } 1912 } 1913 return page + 1; 1914 } 1915 1916 static const char * 1917 toc_dots_create(double available_width, double dot_width) 1918 { 1919 available_width -= MARGIN_HORIZONTAL; 1920 int dot_count; 1921 static char dots[1024]; 1922 dot_count = (int)floor(available_width / dot_width); 1923 if (dot_count > 1023) { 1924 return NULL; 1925 } 1926 memset(&dots, (int)'.', dot_count); 1927 dots[dot_count+1] = 0; 1928 return dots; 1929 } 1930 1931 // HERE 1932 static bool 1933 pdf_texts_add_toc_entry( 1934 struct PDFContext *ctx, 1935 struct TocEntry *entry, 1936 struct ChoStyle *style, 1937 double max_song_title_width, 1938 int toc_page_count, 1939 double dot_width 1940 ) 1941 { 1942 struct PDFText ***texts; 1943 double width, page_no_x, end_of_title_x, width_between_title_and_page_no, available_dots_width; 1944 double page_no_width, dots_width; 1945 int index, line_count; 1946 char tmp[strlen(entry->title)+1]; 1947 size_t page_no_size = 11 + 1; 1948 char page_no[page_no_size]; 1949 1950 texts = &ctx->t_ctx.content->pages[ctx->t_ctx.page]->texts; 1951 strcpy((char *)&tmp, entry->title); 1952 width = text_width(ctx, entry->title, style); 1953 if (width == ERROR) { 1954 DEBUG("text_width failed."); 1955 return false; 1956 } 1957 if (width+MARGIN_HORIZONTAL > max_song_title_width) { 1958 char *t = (char *)&tmp; 1959 line_count = 0; 1960 while (width+MARGIN_HORIZONTAL > max_song_title_width) { 1961 if (ctx->t_ctx.y < MARGIN_BOTTOM) { 1962 if (!pdf_page_close_then_add(ctx, &ctx->t_ctx, NUMERAL_SYSTEM_ROMAN)) { 1963 DEBUG("pdf_page_close_then_add failed."); 1964 return false; 1965 } 1966 texts = &ctx->t_ctx.content->pages[ctx->t_ctx.page]->texts; 1967 } 1968 index = text_find_fitting(ctx, t, style, MARGIN_HORIZONTAL, max_song_title_width); 1969 if (index == EMPTY_INT) { 1970 DEBUG("text_find_fitting failed."); 1971 return false; 1972 } 1973 t[index] = 0; 1974 *texts = erealloc(*texts, (ctx->t_ctx.text+1) * sizeof(struct PDFText *)); 1975 (*texts)[ctx->t_ctx.text] = pdf_text_new(); 1976 (*texts)[ctx->t_ctx.text]->text = strdup(t); 1977 (*texts)[ctx->t_ctx.text]->style = cho_style_copy(style); 1978 (*texts)[ctx->t_ctx.text]->x = MARGIN_HORIZONTAL; 1979 (*texts)[ctx->t_ctx.text]->y = ctx->t_ctx.y; 1980 ctx->t_ctx.y -= 8.0 + style->font->size; 1981 line_count++; 1982 width = text_width(ctx, t, style); 1983 if (width == ERROR) { 1984 DEBUG("text_width failed."); 1985 return false; 1986 } 1987 (*texts)[ctx->t_ctx.text]->width = width; 1988 t += index + 1; 1989 width = text_width(ctx, t, style); 1990 if (width == ERROR) { 1991 DEBUG("text_width failed."); 1992 return false; 1993 } 1994 ctx->t_ctx.text++; 1995 } 1996 if (ctx->t_ctx.y < MARGIN_BOTTOM) { 1997 if (!pdf_page_close_then_add(ctx, &ctx->t_ctx, NUMERAL_SYSTEM_ROMAN)) { 1998 DEBUG("pdf_page_close_then_add failed."); 1999 return false; 2000 } 2001 texts = &ctx->t_ctx.content->pages[ctx->t_ctx.page]->texts; 2002 } 2003 end_of_title_x = width; 2004 *texts = erealloc(*texts, (ctx->t_ctx.text+1) * sizeof(struct PDFText *)); 2005 (*texts)[ctx->t_ctx.text] = pdf_text_new(); 2006 (*texts)[ctx->t_ctx.text]->text = strdup(t); 2007 (*texts)[ctx->t_ctx.text]->style = cho_style_copy(style); 2008 (*texts)[ctx->t_ctx.text]->x = MARGIN_HORIZONTAL; 2009 (*texts)[ctx->t_ctx.text]->y = ctx->t_ctx.y; 2010 (*texts)[ctx->t_ctx.text]->width = width; 2011 ctx->t_ctx.text++; 2012 snprintf((char *)&page_no, page_no_size, "%d", entry->page_index+1); 2013 width = text_width(ctx, page_no, style); 2014 if (width == ERROR) { 2015 DEBUG("text_width failed."); 2016 return false; 2017 } 2018 2019 page_no_x = MEDIABOX_WIDTH - width - MARGIN_HORIZONTAL; 2020 width_between_title_and_page_no = page_no_x - end_of_title_x; 2021 available_dots_width = width_between_title_and_page_no - TOC_DOTS_GAP_WIDTH*2; 2022 const char *dots = toc_dots_create(available_dots_width, dot_width); 2023 if (!dots) { 2024 DEBUG("toc_dots_create failed."); 2025 return false; 2026 } 2027 2028 *texts = erealloc(*texts, (ctx->t_ctx.text+1) * sizeof(struct PDFText *)); 2029 (*texts)[ctx->t_ctx.text] = pdf_text_new(); 2030 (*texts)[ctx->t_ctx.text]->text = strdup(dots); 2031 (*texts)[ctx->t_ctx.text]->style = cho_style_copy(style); 2032 (*texts)[ctx->t_ctx.text]->x = MARGIN_HORIZONTAL + end_of_title_x + TOC_DOTS_GAP_WIDTH; 2033 (*texts)[ctx->t_ctx.text]->y = ctx->t_ctx.y; 2034 ctx->t_ctx.text++; 2035 2036 *texts = erealloc(*texts, (ctx->t_ctx.text+1) * sizeof(struct PDFText *)); 2037 (*texts)[ctx->t_ctx.text] = pdf_text_new(); 2038 (*texts)[ctx->t_ctx.text]->text = strdup(page_no); 2039 (*texts)[ctx->t_ctx.text]->style = cho_style_copy(style); 2040 (*texts)[ctx->t_ctx.text]->x = page_no_x; 2041 (*texts)[ctx->t_ctx.text]->y = ctx->t_ctx.y; 2042 (*texts)[ctx->t_ctx.text]->width = width; 2043 line_count++; 2044 if (!annot_page_link_add(ctx, entry, toc_page_count, line_count, style->font->size)) { 2045 DEBUG("annot_page_link_add"); 2046 return false; 2047 } 2048 ctx->t_ctx.text++; 2049 ctx->t_ctx.y -= 8.0 + style->font->size; 2050 } else { 2051 if (ctx->t_ctx.y < MARGIN_BOTTOM) { 2052 if (!pdf_page_close_then_add(ctx, &ctx->t_ctx, NUMERAL_SYSTEM_ROMAN)) { 2053 DEBUG("pdf_page_close_then_add failed."); 2054 return false; 2055 } 2056 texts = &ctx->t_ctx.content->pages[ctx->t_ctx.page]->texts; 2057 } 2058 end_of_title_x = width; 2059 *texts = erealloc(*texts, (ctx->t_ctx.text+1) * sizeof(struct PDFText *)); 2060 (*texts)[ctx->t_ctx.text] = pdf_text_new(); 2061 (*texts)[ctx->t_ctx.text]->text = strdup(entry->title); 2062 (*texts)[ctx->t_ctx.text]->style = cho_style_copy(style); 2063 (*texts)[ctx->t_ctx.text]->x = MARGIN_HORIZONTAL; 2064 (*texts)[ctx->t_ctx.text]->y = ctx->t_ctx.y; 2065 width = text_width(ctx, entry->title, style); 2066 if (width == ERROR) { 2067 DEBUG("text_width failed."); 2068 return false; 2069 } 2070 (*texts)[ctx->t_ctx.text]->width = width; 2071 ctx->t_ctx.text++; 2072 snprintf((char *)&page_no, page_no_size, "%d", entry->page_index+1); 2073 page_no_width = text_width(ctx, page_no, style); 2074 if (page_no_width == ERROR) { 2075 DEBUG("text_width failed."); 2076 return false; 2077 } 2078 2079 page_no_x = MEDIABOX_WIDTH - page_no_width - MARGIN_HORIZONTAL; 2080 width_between_title_and_page_no = page_no_x - end_of_title_x; 2081 available_dots_width = width_between_title_and_page_no - TOC_DOTS_GAP_WIDTH*2; 2082 const char *dots = toc_dots_create(available_dots_width, dot_width); 2083 if (!dots) { 2084 DEBUG("toc_dots_create failed."); 2085 return false; 2086 } 2087 dots_width = text_width(ctx, dots, style); 2088 if (dots_width == ERROR) { 2089 DEBUG("text_width failed."); 2090 return false; 2091 } 2092 2093 *texts = erealloc(*texts, (ctx->t_ctx.text+1) * sizeof(struct PDFText *)); 2094 (*texts)[ctx->t_ctx.text] = pdf_text_new(); 2095 (*texts)[ctx->t_ctx.text]->text = strdup(dots); 2096 (*texts)[ctx->t_ctx.text]->style = cho_style_copy(style); 2097 (*texts)[ctx->t_ctx.text]->x = MARGIN_HORIZONTAL + end_of_title_x + TOC_DOTS_GAP_WIDTH; 2098 (*texts)[ctx->t_ctx.text]->y = ctx->t_ctx.y; 2099 (*texts)[ctx->t_ctx.text]->width = dots_width; 2100 ctx->t_ctx.text++; 2101 2102 *texts = erealloc(*texts, (ctx->t_ctx.text+1) * sizeof(struct PDFText *)); 2103 (*texts)[ctx->t_ctx.text] = pdf_text_new(); 2104 (*texts)[ctx->t_ctx.text]->text = strdup(page_no); 2105 (*texts)[ctx->t_ctx.text]->style = cho_style_copy(style); 2106 (*texts)[ctx->t_ctx.text]->x = page_no_x; 2107 (*texts)[ctx->t_ctx.text]->y = ctx->t_ctx.y; 2108 (*texts)[ctx->t_ctx.text]->width = page_no_width; 2109 if (!annot_page_link_add(ctx, entry, toc_page_count, 1, style->font->size)) { 2110 DEBUG("annot_page_link_add"); 2111 return false; 2112 } 2113 ctx->t_ctx.text++; 2114 ctx->t_ctx.y -= 8.0 + style->font->size; 2115 } 2116 return true; 2117 } 2118 2119 static bool 2120 pdf_texts_add_lyrics( 2121 struct PDFContext *ctx, 2122 struct ChoLineItem *item, 2123 int i 2124 ) 2125 { 2126 struct PDFText ***texts; 2127 struct SpaceNeeded **sp; 2128 double width; 2129 int t = 0; 2130 int c; 2131 2132 texts = &ctx->b_ctx.content->pages[ctx->b_ctx.page]->texts; 2133 *texts = erealloc(*texts, (ctx->b_ctx.text+1) * sizeof(struct PDFText *)); 2134 (*texts)[ctx->b_ctx.text] = pdf_text_new(); 2135 if (!ctx->b_ctx.spaces) { 2136 (*texts)[ctx->b_ctx.text]->text = strdup(item->u.text->text); 2137 (*texts)[ctx->b_ctx.text]->style = cho_style_copy(item->u.text->style); 2138 (*texts)[ctx->b_ctx.text]->x = ctx->b_ctx.x; 2139 (*texts)[ctx->b_ctx.text]->y = ctx->b_ctx.y; 2140 width = text_width(ctx, item->u.text->text, item->u.text->style); 2141 if (width == ERROR) { 2142 DEBUG("text_width failed."); 2143 return false; 2144 } 2145 (*texts)[ctx->b_ctx.text]->width = width; 2146 if (item->u.text->style->href) { 2147 if (!annot_url_link_add(ctx, &ctx->b_ctx, item->u.text->style, width)) { 2148 DEBUG("annot_url_link_add failed."); 2149 return false; 2150 } 2151 } 2152 ctx->b_ctx.x += width; 2153 if ((*texts)[ctx->b_ctx.text]->style->font->size > ctx->b_ctx.biggest_font_size) { 2154 ctx->b_ctx.biggest_font_size = (*texts)[ctx->b_ctx.text]->style->font->size; 2155 } 2156 ctx->b_ctx.consumed_lyrics += strlen((*texts)[ctx->b_ctx.text]->text); 2157 ctx->b_ctx.text++; 2158 return true; 2159 } 2160 for (c = 0; item->u.text->text[c]; c++) { 2161 (*texts)[ctx->b_ctx.text]->text = erealloc((*texts)[ctx->b_ctx.text]->text, (t+1) * sizeof(char)); 2162 (*texts)[ctx->b_ctx.text]->text[t] = item->u.text->text[c]; 2163 t++; 2164 for (sp = ctx->b_ctx.spaces; *sp; sp++) { 2165 if ((*sp)->line_item_index == i && (*sp)->text_index == c) { 2166 size_t len = grapheme_next_character_break_utf8(&item->u.text->text[c], 10); 2167 size_t i; 2168 for (i = 0; i<len-1; i++, t++) { 2169 c++; 2170 (*texts)[ctx->b_ctx.text]->text = erealloc((*texts)[ctx->b_ctx.text]->text, (t+1) * sizeof(char)); 2171 (*texts)[ctx->b_ctx.text]->text[t] = item->u.text->text[c]; 2172 } 2173 (*texts)[ctx->b_ctx.text]->text = erealloc((*texts)[ctx->b_ctx.text]->text, (t+1) * sizeof(char)); 2174 (*texts)[ctx->b_ctx.text]->text[t] = 0; 2175 (*texts)[ctx->b_ctx.text]->style = cho_style_copy(item->u.text->style); 2176 (*texts)[ctx->b_ctx.text]->x = ctx->b_ctx.x; 2177 (*texts)[ctx->b_ctx.text]->y = ctx->b_ctx.y; 2178 width = text_width(ctx, (*texts)[ctx->b_ctx.text]->text, item->u.text->style); 2179 if (width == ERROR) { 2180 DEBUG("text_width failed."); 2181 return false; 2182 } 2183 (*texts)[ctx->b_ctx.text]->width = width; 2184 if (item->u.text->style->href) { 2185 if (!annot_url_link_add(ctx, &ctx->b_ctx, item->u.text->style, width)) { 2186 DEBUG("annot_url_link_add failed."); 2187 return false; 2188 } 2189 } 2190 ctx->b_ctx.x += width; 2191 ctx->b_ctx.x += (*sp)->amount; 2192 t = 0; 2193 ctx->b_ctx.consumed_lyrics += strlen((*texts)[ctx->b_ctx.text]->text); 2194 ctx->b_ctx.text++; 2195 *texts = erealloc(*texts, (ctx->b_ctx.text+1) * sizeof(struct PDFText *)); 2196 (*texts)[ctx->b_ctx.text] = pdf_text_new(); 2197 } 2198 } 2199 } 2200 (*texts)[ctx->b_ctx.text]->text = erealloc((*texts)[ctx->b_ctx.text]->text, (t+1) * sizeof(char)); 2201 (*texts)[ctx->b_ctx.text]->text[t] = 0; 2202 (*texts)[ctx->b_ctx.text]->style = cho_style_copy(item->u.text->style); 2203 (*texts)[ctx->b_ctx.text]->x = ctx->b_ctx.x; 2204 (*texts)[ctx->b_ctx.text]->y = ctx->b_ctx.y; 2205 width = text_width(ctx, (*texts)[ctx->b_ctx.text]->text, item->u.text->style); 2206 if (width == ERROR) { 2207 DEBUG("text_width failed."); 2208 return false; 2209 } 2210 (*texts)[ctx->b_ctx.text]->width = width; 2211 if (item->u.text->style->href) { 2212 if (!annot_url_link_add(ctx, &ctx->b_ctx, item->u.text->style, width)) { 2213 DEBUG("annot_url_link_add failed."); 2214 return false; 2215 } 2216 } 2217 ctx->b_ctx.x += width; 2218 if ((*texts)[ctx->b_ctx.text]->style->font->size > ctx->b_ctx.biggest_font_size) { 2219 ctx->b_ctx.biggest_font_size = (*texts)[ctx->b_ctx.text]->style->font->size; 2220 } 2221 ctx->b_ctx.consumed_lyrics += strlen((*texts)[ctx->b_ctx.text]->text); 2222 ctx->b_ctx.text++; 2223 return true; 2224 } 2225 2226 static bool 2227 pdf_texts_add_text( 2228 struct PDFContext *ctx, 2229 struct PDFContentContext *c_ctx, 2230 const char *text, 2231 struct ChoStyle *style, 2232 enum Alignment align, 2233 enum NumeralSystem numeral_system 2234 ) 2235 { 2236 struct PDFText ***texts; 2237 char str[strlen(text)+1]; 2238 double width; 2239 int index; 2240 2241 strcpy((char *)&str, text); 2242 texts = &c_ctx->content->pages[c_ctx->page]->texts; 2243 width = text_width(ctx, text, style); 2244 if (width == ERROR) { 2245 DEBUG("text_width failed."); 2246 return false; 2247 } 2248 if (width > LINE_WIDTH) { 2249 char *t = (char *)&str; 2250 while (width > LINE_WIDTH) { 2251 if (c_ctx->y < c_ctx->margin_bottom) { 2252 if (!pdf_page_close_then_add(ctx, c_ctx, numeral_system)) { 2253 DEBUG("pdf_page_close_then_add failed."); 2254 return false; 2255 } 2256 texts = &c_ctx->content->pages[c_ctx->page]->texts; 2257 } 2258 index = text_find_fitting(ctx, t, style, 0.0, LINE_WIDTH); 2259 if (index == EMPTY_INT) { 2260 DEBUG("text_find_fitting failed."); 2261 return false; 2262 } 2263 t[index] = 0; 2264 width = text_width(ctx, t, style); 2265 if (width == ERROR) { 2266 DEBUG("text_width failed."); 2267 return false; 2268 } 2269 c_ctx->x = calc_x(width, align); 2270 *texts = erealloc(*texts, (c_ctx->text+1) * sizeof(struct PDFText *)); 2271 (*texts)[c_ctx->text] = pdf_text_new(); 2272 (*texts)[c_ctx->text]->text = strdup(t); 2273 (*texts)[c_ctx->text]->style = cho_style_copy(style); 2274 (*texts)[c_ctx->text]->x = c_ctx->x; 2275 (*texts)[c_ctx->text]->y = c_ctx->y; 2276 (*texts)[c_ctx->text]->width = width; 2277 if (style->href) { 2278 if (!annot_url_link_add(ctx, c_ctx, style, width)) { 2279 DEBUG("annot_url_link_add failed."); 2280 return false; 2281 } 2282 } 2283 c_ctx->text++; 2284 c_ctx->y -= 8.0 + style->font->size; 2285 t += index+1; 2286 width = text_width(ctx, t, style); 2287 if (width == ERROR) { 2288 DEBUG("text_width failed."); 2289 return false; 2290 } 2291 } 2292 width = text_width(ctx, t, style); 2293 if (width == ERROR) { 2294 DEBUG("text_width failed."); 2295 return false; 2296 } 2297 c_ctx->x = calc_x(width, align); 2298 *texts = erealloc(*texts, (c_ctx->text+1) * sizeof(struct PDFText *)); 2299 (*texts)[c_ctx->text] = pdf_text_new(); 2300 (*texts)[c_ctx->text]->text = strdup(t); 2301 (*texts)[c_ctx->text]->style = cho_style_copy(style); 2302 (*texts)[c_ctx->text]->x = c_ctx->x; 2303 (*texts)[c_ctx->text]->y = c_ctx->y; 2304 (*texts)[c_ctx->text]->width = width; 2305 if (style->href) { 2306 if (!annot_url_link_add(ctx, c_ctx, style, width)) { 2307 DEBUG("annot_url_link_add failed."); 2308 return false; 2309 } 2310 } 2311 c_ctx->text++; 2312 c_ctx->y -= 8.0 + style->font->size; 2313 } else { 2314 if (c_ctx->y < c_ctx->margin_bottom) { 2315 if (!pdf_page_close_then_add(ctx, c_ctx, numeral_system)) { 2316 DEBUG("pdf_page_close_then_add failed."); 2317 return false; 2318 } 2319 texts = &c_ctx->content->pages[c_ctx->page]->texts; 2320 } 2321 c_ctx->x = calc_x(width, align); 2322 *texts = erealloc(*texts, (c_ctx->text+1) * sizeof(struct PDFText *)); 2323 (*texts)[c_ctx->text] = pdf_text_new(); 2324 (*texts)[c_ctx->text]->text = strdup(text); 2325 (*texts)[c_ctx->text]->style = cho_style_copy(style); 2326 (*texts)[c_ctx->text]->x = c_ctx->x; 2327 (*texts)[c_ctx->text]->y = c_ctx->y; 2328 (*texts)[c_ctx->text]->width = width; 2329 if (style->href) { 2330 if (!annot_url_link_add(ctx, c_ctx, style, width)) { 2331 DEBUG("annot_url_link_add failed."); 2332 return false; 2333 } 2334 } 2335 c_ctx->text++; 2336 c_ctx->y -= 8.0 + style->font->size; 2337 } 2338 return true; 2339 } 2340 2341 static double 2342 find_biggest_line_item_width(struct PDFContext *ctx, struct ChoLine **lines) 2343 { 2344 struct ChoLine **li; 2345 struct ChoLineItem **lii; 2346 double width; 2347 double biggest = 0.0; 2348 2349 for (li = lines; *li; li++) { 2350 for (lii = (*li)->items; *lii; lii++) { 2351 width = text_width(ctx, (*lii)->u.text->text, (*lii)->u.text->style); 2352 if (width == -1) { 2353 DEBUG("text_width failed."); 2354 return -1; 2355 } 2356 if (width > biggest) { 2357 biggest = width; 2358 } 2359 } 2360 } 2361 return biggest; 2362 } 2363 2364 static bool 2365 pdf_toc_create( 2366 struct PDFContext *ctx, 2367 struct PDFContent *pdf_body, 2368 struct PDFContent **out 2369 ) 2370 { 2371 double max_song_title_width, dot_width; 2372 int toc_page_count; 2373 struct PDFText ***texts; 2374 struct ChoStyle *toc_style, *title_style; 2375 struct TocEntry **toc; 2376 2377 pdf_content_context_init(&ctx->t_ctx); 2378 ctx->t_ctx.content = pdf_content_new(); 2379 ctx->t_ctx.content->pages = emalloc(sizeof(struct PDFPage *)); 2380 ctx->t_ctx.content->pages[ctx->t_ctx.page] = pdf_page_new(ctx); 2381 texts = &ctx->t_ctx.content->pages[ctx->t_ctx.page]->texts; 2382 if (ctx->config->output->page_no->show) { 2383 if (!pdf_page_add_page_no(ctx, &ctx->t_ctx, NUMERAL_SYSTEM_ROMAN)) { 2384 DEBUG("pdf_page_add_page_no failed."); 2385 return false; 2386 } 2387 } 2388 toc_style = ctx->config->output->styles[TEXT_TYPE_TOC]; 2389 toc = pdf_body->toc; 2390 max_song_title_width = LINE_WIDTH * 0.85; 2391 toc_page_count = pdf_toc_page_count(ctx, toc, toc_style, max_song_title_width); 2392 if (toc_page_count == -1) { 2393 DEBUG("pdf_toc_page_count failed."); 2394 return false; 2395 } 2396 title_style = ctx->config->output->styles[TEXT_TYPE_TOC_TITLE]; 2397 if (!pdf_texts_add_text(ctx, &ctx->t_ctx, ctx->config->output->toc->title, title_style, ALIGNMENT_CENTER, NUMERAL_SYSTEM_ROMAN)) { 2398 DEBUG("pdf_texts_add_text(toctitle) failed."); 2399 return false; 2400 } 2401 ctx->t_ctx.y -= 30.0; 2402 dot_width = text_width(ctx, ".", toc_style); 2403 if (dot_width == ERROR) { 2404 DEBUG("text_width failed."); 2405 return false; 2406 } 2407 for (; *toc; toc++) { 2408 if (!pdf_texts_add_toc_entry(ctx, *toc, toc_style, max_song_title_width, toc_page_count, dot_width)) { 2409 DEBUG("pdf_texts_add_toc_entry failed."); 2410 return false; 2411 } 2412 } 2413 texts = &ctx->t_ctx.content->pages[ctx->t_ctx.page]->texts; 2414 *texts = erealloc(*texts, (ctx->t_ctx.text+1) * sizeof(struct PDFText *)); 2415 (*texts)[ctx->t_ctx.text] = NULL; 2416 ctx->t_ctx.page++; 2417 ctx->t_ctx.content->pages = erealloc(ctx->t_ctx.content->pages, (ctx->t_ctx.page+1) * sizeof(struct PDFPage *)); 2418 ctx->t_ctx.content->pages[ctx->t_ctx.page] = NULL; 2419 *out = ctx->t_ctx.content; 2420 return true; 2421 } 2422 2423 static bool 2424 pdf_body_create( 2425 struct PDFContext *ctx, 2426 struct ChoSong **songs, 2427 struct Obj **img_objs, 2428 struct PDFContent **out 2429 ) 2430 { 2431 struct ChoSection **se; 2432 struct ChoLine **li; 2433 struct PDFText ***texts; 2434 struct PDFImage ***imgs; 2435 struct ChordDiagram ***diagrams, **dgrams, **d; 2436 struct ChoStyle *metadata_style; 2437 char *metadata; 2438 bool show_diagram = ctx->config->output->diagram->show; 2439 bool start_song_on_new_page = ctx->config->output->start_song_on_new_page; 2440 double width, height; 2441 2442 pdf_content_context_init(&ctx->b_ctx); 2443 if (show_diagram) { 2444 ctx->b_ctx.margin_bottom = 150.0; 2445 } 2446 ctx->b_ctx.content = pdf_content_new(); 2447 ctx->b_ctx.content->pages = emalloc(sizeof(struct PDFPage *)); 2448 ctx->b_ctx.content->pages[ctx->b_ctx.page] = pdf_page_new(ctx); 2449 texts = &ctx->b_ctx.content->pages[ctx->b_ctx.page]->texts; 2450 imgs = &ctx->b_ctx.content->pages[ctx->b_ctx.page]->images; 2451 diagrams = &ctx->b_ctx.content->pages[ctx->b_ctx.page]->diagrams; 2452 if (ctx->config->output->page_no->show) { 2453 if (!pdf_page_add_page_no(ctx, &ctx->b_ctx, NUMERAL_SYSTEM_WESTERN_ARABIC)) { 2454 DEBUG("pdf_page_add_page_no failed."); 2455 return false; 2456 } 2457 } 2458 int s; 2459 for (s = 0; songs[s]; s++) { 2460 if (show_diagram) { 2461 struct ChoChord **chords; 2462 chords = pdf_get_chords(songs[s]); 2463 if (chords) { 2464 qsort(chords, cho_chord_count(chords), sizeof(struct ChoChord *), cho_chord_compare); 2465 dgrams = chord_diagrams_create(ctx->config, &chords, songs[s]->diagrams); 2466 for (d = dgrams; *d; d++) { 2467 *diagrams = erealloc(*diagrams, (ctx->b_ctx.diagram+1) * sizeof(struct ChordDiagram *)); 2468 (*diagrams)[ctx->b_ctx.diagram] = *d; 2469 ctx->b_ctx.diagram++; 2470 } 2471 free(dgrams); 2472 cho_chords_free(chords); 2473 } 2474 } 2475 if (!cho_metadata_value(songs[s]->metadata, "title", ctx->config->metadata_separator, &metadata, &metadata_style)) { 2476 DEBUG("cho_metadata_value failed."); 2477 return false; 2478 } 2479 ctx->b_ctx.content->toc = erealloc(ctx->b_ctx.content->toc, (ctx->b_ctx.toc_entry+1) * sizeof(struct TocEntry *)); 2480 ctx->b_ctx.content->toc[ctx->b_ctx.toc_entry] = emalloc(sizeof(struct TocEntry)); 2481 ctx->b_ctx.content->toc[ctx->b_ctx.toc_entry]->title = strdup(metadata); 2482 ctx->b_ctx.content->toc[ctx->b_ctx.toc_entry]->page_index = ctx->b_ctx.page; 2483 ctx->b_ctx.content->toc[ctx->b_ctx.toc_entry]->page_y = ctx->b_ctx.y; 2484 ctx->b_ctx.toc_entry++; 2485 if (!pdf_texts_add_text(ctx, &ctx->b_ctx, metadata, metadata_style, ALIGNMENT_CENTER, NUMERAL_SYSTEM_WESTERN_ARABIC)) { 2486 DEBUG("pdf_texts_add_text(title) failed."); 2487 free(metadata); 2488 return false; 2489 } 2490 free(metadata); 2491 if (cho_metadata_value(songs[s]->metadata, "subtitle", ctx->config->metadata_separator, &metadata, &metadata_style)) { 2492 if (!pdf_texts_add_text(ctx, &ctx->b_ctx, metadata, metadata_style, ALIGNMENT_CENTER, NUMERAL_SYSTEM_WESTERN_ARABIC)) { 2493 DEBUG("pdf_texts_add_text(subtitle) failed."); 2494 free(metadata); 2495 return false; 2496 } 2497 free(metadata); 2498 } 2499 texts = &ctx->b_ctx.content->pages[ctx->b_ctx.page]->texts; 2500 imgs = &ctx->b_ctx.content->pages[ctx->b_ctx.page]->images; 2501 diagrams = &ctx->b_ctx.content->pages[ctx->b_ctx.page]->diagrams; 2502 ctx->b_ctx.y -= 30.0; 2503 for (se = songs[s]->sections; *se; se++) { 2504 if ((*se)->label) { 2505 if (!pdf_texts_add_text(ctx, &ctx->b_ctx, (*se)->label->text, (*se)->label->style, ALIGNMENT_LEFT, NUMERAL_SYSTEM_WESTERN_ARABIC)) { 2506 DEBUG("pdf_texts_add_text(label) failed."); 2507 return false; 2508 } 2509 texts = &ctx->b_ctx.content->pages[ctx->b_ctx.page]->texts; 2510 imgs = &ctx->b_ctx.content->pages[ctx->b_ctx.page]->images; 2511 diagrams = &ctx->b_ctx.content->pages[ctx->b_ctx.page]->diagrams; 2512 } 2513 if ((*se)->type == SECTION_TYPE_GRID) { 2514 double biggest_line_item_width, x, biggest_font_size; 2515 struct ChoLine **li; 2516 struct ChoLineItem **lii; 2517 2518 biggest_line_item_width = find_biggest_line_item_width(ctx, (*se)->lines); 2519 if (biggest_line_item_width == -1) { 2520 DEBUG("find_biggest_line_item_width failed."); 2521 return false; 2522 } 2523 for (li = (*se)->lines; *li; li++) { 2524 x = MARGIN_HORIZONTAL; 2525 biggest_font_size = 0.0; 2526 for (lii = (*li)->items; *lii; lii++) { 2527 if ((*lii)->u.text->style->font->size > biggest_font_size) { 2528 biggest_font_size = (*lii)->u.text->style->font->size; 2529 } 2530 *texts = erealloc(*texts, (ctx->b_ctx.text+1) * sizeof(struct PDFText *)); 2531 (*texts)[ctx->b_ctx.text] = pdf_text_new(); 2532 (*texts)[ctx->b_ctx.text]->text = strdup((*lii)->u.text->text); 2533 (*texts)[ctx->b_ctx.text]->style = cho_style_copy((*lii)->u.text->style); 2534 (*texts)[ctx->b_ctx.text]->x = x; 2535 (*texts)[ctx->b_ctx.text]->y = ctx->b_ctx.y; 2536 ctx->b_ctx.text++; 2537 x += biggest_line_item_width; 2538 } 2539 ctx->b_ctx.y -= 8.0 + biggest_font_size; 2540 } 2541 continue; 2542 } 2543 for (li = (*se)->lines; *li; li++) { 2544 int item_index; 2545 int text_above_index; 2546 struct CharPosition *pos; 2547 struct ChoLineItemAbove **left_aboves = (*li)->text_above; 2548 struct ChoLineItem **left_items = (*li)->items; 2549 2550 while (*left_aboves || *left_items) { 2551 bool text_above_exist; 2552 char *string; 2553 int i; 2554 struct ChoStyle *style; 2555 2556 ctx->b_ctx.consumed_lyrics = 0; 2557 ctx->b_ctx.biggest_font_size = 0.0; 2558 ctx->b_ctx.prev_added_space = 0.0; 2559 if (!calc_space_between_text_above(ctx, left_items, left_aboves, img_objs, &ctx->b_ctx.spaces)) { 2560 DEBUG("calc_space_between_text_above failed."); 2561 return false; 2562 } 2563 pos = items_find_position_to_break_line(ctx, left_items, ctx->b_ctx.spaces, img_objs); 2564 if (pos->line_item_index == -1) { 2565 item_index = 10000; 2566 text_above_index = 10000; 2567 } else { 2568 item_index = pos->line_item_index; 2569 text_above_index = text_above_find_index_to_break_line(left_items, left_aboves, pos); 2570 if (text_above_index == -2) { 2571 DEBUG("text_above_find_index_to_break_line failed."); 2572 return false; 2573 } 2574 if (text_above_index == -1) { 2575 text_above_index = 20000; 2576 } 2577 } 2578 for (i = 0; left_aboves[i] && i<text_above_index; i++) { 2579 struct SpaceNeeded **sp; 2580 2581 width = line_width_until_text_above(ctx, left_items, left_aboves[i], img_objs, NULL); 2582 if (width == ERROR) { 2583 DEBUG("line_width_until_text_above failed."); 2584 return false; 2585 } 2586 ctx->b_ctx.x = MARGIN_HORIZONTAL + width + ctx->b_ctx.prev_added_space; 2587 for (sp = ctx->b_ctx.spaces; *sp; sp++) { 2588 if ((*sp)->text_above_index == i) { 2589 ctx->b_ctx.x += (*sp)->amount; 2590 ctx->b_ctx.prev_added_space += (*sp)->amount; 2591 } 2592 } 2593 *texts = erealloc(*texts, (ctx->b_ctx.text+1) * sizeof(struct PDFText *)); 2594 (*texts)[ctx->b_ctx.text] = pdf_text_new(); 2595 (*texts)[ctx->b_ctx.text]->x = ctx->b_ctx.x; 2596 (*texts)[ctx->b_ctx.text]->y = ctx->b_ctx.y; 2597 if (left_aboves[i]->is_chord) { 2598 string = cho_chord_name_generate(left_aboves[i]->u.chord); 2599 style = cho_style_copy(left_aboves[i]->u.chord->style); 2600 } else { 2601 string = strdup(left_aboves[i]->u.annot->text); 2602 style = cho_style_copy(left_aboves[i]->u.annot->style); 2603 } 2604 (*texts)[ctx->b_ctx.text]->text = string; 2605 (*texts)[ctx->b_ctx.text]->style = style; 2606 (*texts)[ctx->b_ctx.text]->width = text_width(ctx, string, style); 2607 if ((*texts)[ctx->b_ctx.text]->width == ERROR) { 2608 DEBUG("text_width failed."); 2609 return false; 2610 } 2611 if (style->href) { 2612 if (!annot_url_link_add(ctx, &ctx->b_ctx, style, (*texts)[ctx->b_ctx.text]->width)) { 2613 DEBUG("annot_url_link_add failed."); 2614 return false; 2615 } 2616 } 2617 if (style->font->size > ctx->b_ctx.biggest_font_size) { 2618 ctx->b_ctx.biggest_font_size = style->font->size; 2619 } 2620 ctx->b_ctx.text++; 2621 } 2622 height = images_find_biggest_height(ctx, left_items, pos->line_item_index, img_objs); 2623 if (height == ERROR) { 2624 DEBUG("images_find_biggest_height failed."); 2625 return false; 2626 } 2627 text_above_exist = i > 0; 2628 if (text_above_exist) { 2629 left_aboves += i; 2630 if (height > 2.0 + ctx->b_ctx.biggest_font_size) { 2631 ctx->b_ctx.y -= height; 2632 } else { 2633 ctx->b_ctx.y -= 2.0 + ctx->b_ctx.biggest_font_size; 2634 } 2635 } else { 2636 if (height > 0.0) { 2637 ctx->b_ctx.y -= height; 2638 } 2639 } 2640 if (ctx->b_ctx.y < ctx->b_ctx.margin_bottom) { 2641 struct PDFText **tmp = NULL; 2642 int tm = 0; 2643 2644 ctx->b_ctx.y = MEDIABOX_HEIGHT - MARGIN_TOP; 2645 if (text_above_exist) { 2646 /* INFO: chords/annotations and their corresponding lyrics won't be splitted */ 2647 int p; 2648 double prev_y = (*texts)[ctx->b_ctx.text-1]->y; 2649 2650 for (p = ctx->b_ctx.text-1; prev_y == (*texts)[p]->y; p--) { 2651 (*texts)[p]->y = ctx->b_ctx.y; 2652 tmp = erealloc(tmp, (tm+1) * sizeof(struct PDFText *)); 2653 tmp[tm] = (*texts)[p]; 2654 tm++; 2655 *texts = erealloc(*texts, (--ctx->b_ctx.text) * sizeof(struct PDFText *)); 2656 } 2657 } 2658 if (!pdf_page_close_then_add(ctx, &ctx->b_ctx, NUMERAL_SYSTEM_WESTERN_ARABIC)) { 2659 DEBUG("pdf_page_close_then_add failed."); 2660 free(tmp); 2661 return false; 2662 } 2663 texts = &ctx->b_ctx.content->pages[ctx->b_ctx.page]->texts; 2664 imgs = &ctx->b_ctx.content->pages[ctx->b_ctx.page]->images; 2665 diagrams = &ctx->b_ctx.content->pages[ctx->b_ctx.page]->diagrams; 2666 if (text_above_exist) { 2667 for (int i=0; i<tm; i++) { 2668 *texts = erealloc(*texts, (ctx->b_ctx.text+1) * sizeof(struct PDFText *)); 2669 (*texts)[ctx->b_ctx.text] = tmp[i]; 2670 ctx->b_ctx.text++; 2671 } 2672 free(tmp); 2673 } 2674 if (text_above_exist) { 2675 if (height > 2.0 + ctx->b_ctx.biggest_font_size) { 2676 ctx->b_ctx.y -= height; 2677 } else { 2678 ctx->b_ctx.y -= 2.0 + ctx->b_ctx.biggest_font_size; 2679 } 2680 } else { 2681 ctx->b_ctx.y -= height; 2682 } 2683 } 2684 ctx->b_ctx.biggest_font_size = 0.0; 2685 ctx->b_ctx.x = MARGIN_HORIZONTAL; 2686 i = 0; 2687 while (*left_items && i < item_index) { 2688 if ((*left_items)->is_text) { 2689 if (!pdf_texts_add_lyrics(ctx, *left_items, i)) { 2690 DEBUG("pdf_texts_add_lyrics failed."); 2691 return false; 2692 } 2693 texts = &ctx->b_ctx.content->pages[ctx->b_ctx.page]->texts; 2694 imgs = &ctx->b_ctx.content->pages[ctx->b_ctx.page]->images; 2695 diagrams = &ctx->b_ctx.content->pages[ctx->b_ctx.page]->diagrams; 2696 } else { 2697 *imgs = erealloc(*imgs, (ctx->b_ctx.image+1) * sizeof(struct PDFImage *)); 2698 (*imgs)[ctx->b_ctx.image] = pdf_image_new(); 2699 (*imgs)[ctx->b_ctx.image]->name = image_name(ctx, (*left_items)->u.image); 2700 if (!(*imgs)[ctx->b_ctx.image]->name) { 2701 DEBUG("image_name failed."); 2702 return false; 2703 } 2704 (*imgs)[ctx->b_ctx.image]->obj = objs_get_obj(img_objs, (*imgs)[ctx->b_ctx.image]->name); 2705 if (!(*imgs)[ctx->b_ctx.image]->obj) { 2706 DEBUG("objs_get_obj failed."); 2707 return false; 2708 } 2709 (*imgs)[ctx->b_ctx.image]->width = image_width((*left_items)->u.image, (*imgs)[ctx->b_ctx.image]->obj); 2710 (*imgs)[ctx->b_ctx.image]->height = image_height((*left_items)->u.image, (*imgs)[ctx->b_ctx.image]->obj); 2711 (*imgs)[ctx->b_ctx.image]->x = ctx->b_ctx.x; 2712 (*imgs)[ctx->b_ctx.image]->y = ctx->b_ctx.y; 2713 ctx->b_ctx.x += (*imgs)[ctx->b_ctx.image]->width; 2714 ctx->b_ctx.image++; 2715 } 2716 i++; 2717 left_items++; 2718 } 2719 if (pos->line_item_index != -1 && pos->text_index != -1) { 2720 if ((*left_items)->is_text) { 2721 char *tmp; 2722 2723 (*left_items)->u.text->text[pos->text_index] = 0; 2724 tmp = strdup(&(*left_items)->u.text->text[pos->text_index+1]); 2725 if (!pdf_texts_add_lyrics(ctx, *left_items, i)) { 2726 DEBUG("pdf_texts_add_lyrics failed."); 2727 free(tmp); 2728 return false; 2729 } 2730 free((*left_items)->u.text->text); 2731 (*left_items)->u.text->text = tmp; 2732 texts = &ctx->b_ctx.content->pages[ctx->b_ctx.page]->texts; 2733 imgs = &ctx->b_ctx.content->pages[ctx->b_ctx.page]->images; 2734 diagrams = &ctx->b_ctx.content->pages[ctx->b_ctx.page]->diagrams; 2735 } 2736 } else 2737 if (pos->line_item_index != -1 && pos->text_index == -1) { 2738 if (!(*left_items)->is_text) { 2739 *imgs = erealloc(*imgs, (ctx->b_ctx.image+1) * sizeof(struct PDFImage *)); 2740 (*imgs)[ctx->b_ctx.image] = pdf_image_new(); 2741 (*imgs)[ctx->b_ctx.image]->name = image_name(ctx, (*left_items)->u.image); 2742 if (!(*imgs)[ctx->b_ctx.image]->name) { 2743 DEBUG("image_name failed."); 2744 return false; 2745 } 2746 (*imgs)[ctx->b_ctx.image]->obj = objs_get_obj(img_objs, (*imgs)[ctx->b_ctx.image]->name); 2747 if (!(*imgs)[ctx->b_ctx.image]->obj) { 2748 DEBUG("objs_get_obj failed."); 2749 return false; 2750 } 2751 (*imgs)[ctx->b_ctx.image]->width = image_width((*left_items)->u.image, (*imgs)[ctx->b_ctx.image]->obj); 2752 (*imgs)[ctx->b_ctx.image]->height = image_height((*left_items)->u.image, (*imgs)[ctx->b_ctx.image]->obj); 2753 (*imgs)[ctx->b_ctx.image]->x = ctx->b_ctx.x; 2754 (*imgs)[ctx->b_ctx.image]->y = ctx->b_ctx.y; 2755 ctx->b_ctx.image++; 2756 } 2757 left_items++; 2758 } 2759 ctx->b_ctx.y -= 8.0 + ctx->b_ctx.biggest_font_size; 2760 if (ctx->b_ctx.y < ctx->b_ctx.margin_bottom) { 2761 if (!pdf_page_close_then_add(ctx, &ctx->b_ctx, NUMERAL_SYSTEM_WESTERN_ARABIC)) { 2762 DEBUG("pdf_page_close_then_add failed."); 2763 return false; 2764 } 2765 texts = &ctx->b_ctx.content->pages[ctx->b_ctx.page]->texts; 2766 imgs = &ctx->b_ctx.content->pages[ctx->b_ctx.page]->images; 2767 diagrams = &ctx->b_ctx.content->pages[ctx->b_ctx.page]->diagrams; 2768 } 2769 spaces_free(ctx->b_ctx.spaces); 2770 ctx->b_ctx.spaces = NULL; 2771 free(pos); 2772 pos = NULL; 2773 text_above_update_positions(left_aboves, ctx->b_ctx.consumed_lyrics); 2774 } 2775 if ((*li)->btype == BREAK_TYPE_PAGE) { 2776 if (!pdf_page_close_then_add(ctx, &ctx->b_ctx, NUMERAL_SYSTEM_WESTERN_ARABIC)) { 2777 DEBUG("pdf_page_close_then_add failed."); 2778 return false; 2779 } 2780 texts = &ctx->b_ctx.content->pages[ctx->b_ctx.page]->texts; 2781 imgs = &ctx->b_ctx.content->pages[ctx->b_ctx.page]->images; 2782 diagrams = &ctx->b_ctx.content->pages[ctx->b_ctx.page]->diagrams; 2783 } 2784 } 2785 ctx->b_ctx.y -= SECTION_GAP_WIDTH; 2786 } 2787 if (start_song_on_new_page) { 2788 if (!pdf_page_close_then_add(ctx, &ctx->b_ctx, NUMERAL_SYSTEM_WESTERN_ARABIC)) { 2789 DEBUG("pdf_page_close_then_add failed."); 2790 return false; 2791 } 2792 texts = &ctx->b_ctx.content->pages[ctx->b_ctx.page]->texts; 2793 imgs = &ctx->b_ctx.content->pages[ctx->b_ctx.page]->images; 2794 diagrams = &ctx->b_ctx.content->pages[ctx->b_ctx.page]->diagrams; 2795 } 2796 } 2797 *texts = erealloc(*texts, (ctx->b_ctx.text+1) * sizeof(struct PDFText *)); 2798 (*texts)[ctx->b_ctx.text] = NULL; 2799 *imgs = erealloc(*imgs, (ctx->b_ctx.image+1) * sizeof(struct PDFImage *)); 2800 (*imgs)[ctx->b_ctx.image] = NULL; 2801 *diagrams = erealloc(*diagrams, (ctx->b_ctx.diagram+1) * sizeof(struct ChordDiagram *)); 2802 (*diagrams)[ctx->b_ctx.diagram] = NULL; 2803 if (start_song_on_new_page) { 2804 pdf_page_free(ctx->b_ctx.content->pages[ctx->b_ctx.page]); 2805 ctx->b_ctx.content->pages[ctx->b_ctx.page] = NULL; 2806 } else { 2807 ctx->b_ctx.page++; 2808 ctx->b_ctx.content->pages = erealloc(ctx->b_ctx.content->pages, (ctx->b_ctx.page+1) * sizeof(struct PDFPage *)); 2809 ctx->b_ctx.content->pages[ctx->b_ctx.page] = NULL; 2810 } 2811 ctx->b_ctx.content->toc = erealloc(ctx->b_ctx.content->toc, (ctx->b_ctx.toc_entry+1) * sizeof(struct TocEntry *)); 2812 ctx->b_ctx.content->toc[ctx->b_ctx.toc_entry] = NULL; 2813 *out = ctx->b_ctx.content; 2814 return true; 2815 } 2816 2817 static bool 2818 pdf_toc_render(struct PDFContext *ctx, struct PDFContent *content) 2819 { 2820 struct PDFPage **pages; 2821 struct PDFText **texts; 2822 pdfio_stream_t *stream; 2823 int p; 2824 2825 pages = content->pages; 2826 for (p = 0; pages[p]; p++) { 2827 ctx->current_page_index = p; 2828 stream = pdf_page_create(ctx, NULL, pages[p]->annots); 2829 if (!stream) { 2830 DEBUG("pdf_page_create failed."); 2831 return false; 2832 } 2833 for (texts = pages[p]->texts; *texts; texts++) { 2834 if (!pdf_text_show(ctx, stream, *texts)) { 2835 DEBUG("pdf_text_show failed."); 2836 return false; 2837 } 2838 } 2839 if (!pdfioStreamClose(stream)) { 2840 DEBUG("pdfioStreamClose failed."); 2841 return false; 2842 } 2843 } 2844 return true; 2845 } 2846 2847 static bool 2848 pdf_body_render(struct PDFContext *ctx, struct PDFContent *content) 2849 { 2850 int p; 2851 struct PDFPage **pages; 2852 struct PDFText **texts; 2853 struct PDFImage **imgs; 2854 pdfio_stream_t *stream; 2855 2856 pages = content->pages; 2857 for (p = 0; pages[p]; p++) { 2858 ctx->current_page_index = p; 2859 stream = pdf_page_create(ctx, pages[p]->images, pages[p]->annots); 2860 if (!stream) { 2861 DEBUG("pdf_page_create failed."); 2862 return false; 2863 } 2864 for (texts = pages[p]->texts; *texts; texts++) { 2865 if (!pdf_text_show(ctx, stream, *texts)) { 2866 DEBUG("pdf_text_show failed."); 2867 return false; 2868 } 2869 } 2870 for (imgs = pages[p]->images; *imgs; imgs++) { 2871 if ( 2872 !pdfioContentDrawImage( 2873 stream, 2874 (*imgs)->name, 2875 (*imgs)->x, 2876 (*imgs)->y, 2877 (*imgs)->width, 2878 (*imgs)->height 2879 ) 2880 ) { 2881 DEBUG("pdfioContentDrawImage failed."); 2882 return false; 2883 } 2884 } 2885 if (pages[p]->diagrams) { 2886 double x = MARGIN_HORIZONTAL; 2887 double y = 50.0; 2888 double size = 50.0; 2889 double padding = 30.0; 2890 struct ChordDiagram **d; 2891 /* TODO: Handle line break when too long */ 2892 for (d = pages[p]->diagrams; *d; d++) { 2893 if ((*d)->show) { 2894 if (!chord_diagram_draw(ctx, stream, *d, x, y, size)) { 2895 DEBUG("chord_diagram_draw failed."); 2896 return false; 2897 } 2898 x += size + padding; 2899 } 2900 } 2901 } 2902 if (!pdfioStreamClose(stream)) { 2903 DEBUG("pdfioStreamClose failed."); 2904 return false; 2905 } 2906 } 2907 return true; 2908 } 2909 2910 char * 2911 out_pdf_create( 2912 const char *cho_filepath, 2913 const char *output_folder_or_file, 2914 struct ChoSong **songs, 2915 struct Config *config 2916 ) 2917 { 2918 struct Font **needed_fonts = NULL; 2919 struct Obj **img_objs = NULL; 2920 struct PDFContent *pdf_body = NULL; 2921 struct PDFContent *pdf_toc = NULL; 2922 struct PDFContext ctx; 2923 pdfio_rect_t media_box_a4 = { 0.0, 0.0, MEDIABOX_WIDTH, MEDIABOX_HEIGHT }; 2924 pdfio_rect_t crop_box = { 0.0, 0.0, MEDIABOX_WIDTH, MEDIABOX_HEIGHT }; 2925 char *dirpath = NULL; 2926 char *pdf_filepath = NULL; 2927 char *pdf_dot_filepath = NULL; 2928 2929 FcInit(); 2930 ctx.diagram_font_is_base_font = false; 2931 ctx.fonts = NULL; 2932 ctx.config = config; 2933 ctx.current_page_index = 0; 2934 ctx.current_font_size = 0.0; 2935 memset(&ctx.cho_dirpath, 0, PATH_MAX); 2936 memset(&ctx.current_font_name, 0, 200); 2937 if (cho_filepath) { 2938 dirpath = filepath_dirname(cho_filepath); 2939 } else { 2940 dirpath = getcwd(NULL, 0); 2941 } 2942 strcpy((char *)&ctx.cho_dirpath, dirpath); 2943 free(dirpath); 2944 pdf_filepath = pdf_filepath_create(&ctx, songs, cho_filepath, output_folder_or_file); 2945 if (!pdf_filepath) { 2946 DEBUG("pdf_filepath_create failed."); 2947 goto ERR; 2948 } 2949 pdf_dot_filepath = filepath_as_dot_file(pdf_filepath); 2950 ctx.pdf_file = pdfioFileCreate(pdf_dot_filepath, "2.0", &media_box_a4, &crop_box, NULL, NULL); 2951 if (!ctx.pdf_file) { 2952 DEBUG("pdfioFileCreateTemporary failed."); 2953 goto ERR; 2954 } 2955 if (!pdf_set_title(&ctx, songs)) { 2956 DEBUG("pdf_set_title failed."); 2957 goto ERR; 2958 } 2959 needed_fonts = fonts_get_all(songs, ctx.config); 2960 if (!pdf_load_fonts(&ctx, needed_fonts)) { 2961 DEBUG("pdf_load_fonts failed."); 2962 goto ERR; 2963 } 2964 cho_fonts_free(needed_fonts); 2965 if (!pdf_load_images(&ctx, &img_objs, songs)) { 2966 DEBUG("pdf_load_images failed."); 2967 goto ERR; 2968 } 2969 if (!pdf_body_create(&ctx, songs, img_objs, &pdf_body)) { 2970 DEBUG("pdf_body_create failed."); 2971 goto ERR; 2972 } 2973 if (ctx.config->output->toc->show) { 2974 if (!pdf_toc_create(&ctx, pdf_body, &pdf_toc)) { 2975 DEBUG("pdf_toc_create failed."); 2976 goto ERR; 2977 } 2978 if (!pdf_toc_render(&ctx, pdf_toc)) { 2979 DEBUG("pdf_toc_render failed."); 2980 goto ERR; 2981 } 2982 } 2983 if (!pdf_body_render(&ctx, pdf_body)) { 2984 DEBUG("pdf_body_render failed."); 2985 goto ERR; 2986 } 2987 objs_free(img_objs); 2988 pdf_content_free(pdf_toc); 2989 pdf_content_free(pdf_body); 2990 if (!pdfioFileClose(ctx.pdf_file)) { 2991 DEBUG("pdfioFileClose failed."); 2992 } 2993 objs_free(ctx.fonts); 2994 if (rename(pdf_dot_filepath, pdf_filepath)) { 2995 DEBUG("rename failed."); 2996 util_log( 2997 NULL, 2998 0, 2999 LOG_ERR, 3000 "Moving temporary created pdf file from '%s' to '%s' failed: %s", 3001 pdf_dot_filepath, 3002 pdf_filepath, 3003 strerror(errno) 3004 ); 3005 } 3006 free(pdf_dot_filepath); 3007 FcFini(); 3008 return pdf_filepath; 3009 ERR: 3010 objs_free(img_objs); 3011 pdf_content_free(pdf_toc); 3012 pdf_content_free(pdf_body); 3013 if (!pdfioFileClose(ctx.pdf_file)) { 3014 DEBUG("pdfioFileClose failed."); 3015 } 3016 objs_free(ctx.fonts); 3017 free(pdf_filepath); 3018 if (unlink(pdf_dot_filepath)) { 3019 DEBUG("unlink failed."); 3020 } 3021 free(pdf_dot_filepath); 3022 FcFini(); 3023 return NULL; 3024 }