lorid

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

commit 7df72cd690fff63c07b79ac54852b4b6802c2528
parent de51f322557a93465ce974f6b2b1223a7cbddc52
Author: nibo <nibo@relim.de>
Date:   Mon, 23 Dec 2024 20:21:03 +0100

WIP: toc

Diffstat:
Mchordpro.c | 3+++
Mlorid.c | 2+-
Mout_pdf.c | 210++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
3 files changed, 160 insertions(+), 55 deletions(-)

diff --git a/chordpro.c b/chordpro.c @@ -3532,6 +3532,9 @@ cho_directive_parse(const char *name) } /* else if (!strcmp(name, "footerfont")) { } else if (!strcmp(name, "footersize")) { } else if (!strcmp(name, "footercolour")) { + } else if (!strcmp(name, "labelfont")) { + } else if (!strcmp(name, "labelsize")) { + } else if (!strcmp(name, "labelcolour")) { } */ if (!strcmp(name, "transpose")) { directive->dtype = DT_CHORD; diff --git a/lorid.c b/lorid.c @@ -97,8 +97,8 @@ main(int argc, char *argv[]) } all_songs = erealloc(all_songs, (s+1) * sizeof(struct ChoSong *)); all_songs[s] = NULL; + qsort(all_songs, cho_song_count(all_songs), sizeof(struct ChoSong *), cho_song_compare); } - qsort(all_songs, cho_song_count(all_songs), sizeof(struct ChoSong *), cho_song_compare); char *pdf_filename = out_pdf_create(chordpro_filepath, output, all_songs, config); if (!pdf_filename) { LOG_DEBUG("out_pdf_new failed."); diff --git a/out_pdf.c b/out_pdf.c @@ -361,7 +361,12 @@ find_whitespace(const char *str, size_t start) } static int -text_find_fitting(const char *str, struct ChoStyle *style, double x) +text_find_fitting( + const char *str, + struct ChoStyle *style, + double x, + double max_width +) { size_t len = strlen(str); size_t start = len - 1; @@ -373,16 +378,16 @@ text_find_fitting(const char *str, struct ChoStyle *style, double x) i = find_whitespace((const char *)&tmp, start); if (i == -1) { util_log(LOG_ERR, "Can't split text because no whitespace was found."); - return ERROR; + return -1; } tmp[i] = 0; width = text_width((const char *)&tmp, style); if (width == ERROR) { LOG_DEBUG("text_width failed."); - return ERROR; + return -1; } start = i - 1; - } while (x + width > LINE_WIDTH); + } while (x + width > max_width); return i; } @@ -506,7 +511,51 @@ out_pdf_text_show(pdfio_stream_t *stream, struct PDFText *text) } static bool -annot_add(struct PDFContext *ctx, struct ChoStyle *style, double width) +annot_page_link_add( + struct PDFContext *ctx, + struct TocEntry *entry, + double font_size +) +{ + pdfio_rect_t rect; + pdfio_dict_t *annot; + pdfio_array_t *destination; + rect.x1 = MARGIN_HORIZONTAL; + rect.x2 = MARGIN_HORIZONTAL + LINE_WIDTH; + rect.y1 = ctx->y - 2.0; + rect.y2 = ctx->y + font_size * 0.8; + annot = pdfioDictCreate(g_pdf_file); + if (!pdfioDictSetName(annot, "Subtype", "Link")) { + LOG_DEBUG("pdfioDictSetName failed."); + return false; + } + if (!pdfioDictSetRect(annot, "Rect", &rect)) { + LOG_DEBUG("pdfioDictSetRect failed."); + return false; + } + destination = pdfioArrayCreate(g_pdf_file); + printf("page index '%d'\n", entry->page_index); + if (!pdfioArrayAppendNumber(destination, entry->page_index)) { + LOG_DEBUG("pdfioArrayAppendNumber failed."); + return false; + } + if (!pdfioArrayAppendName(destination, "Fit")) { + LOG_DEBUG("pdfioArrayAppendName failed."); + return false; + } + if (!pdfioDictSetArray(annot, "Dest", destination)) { + LOG_DEBUG("pdfioDictSetArray failed."); + return false; + } + if (!pdfioArrayAppendDict(ctx->content->pages[ctx->page]->annots, annot)) { + LOG_DEBUG("pdfioArrayAppendDict failed."); + return false; + } + return true; +} + +static bool +annot_url_link_add(struct PDFContext *ctx, struct ChoStyle *style, double width) { pdfio_rect_t rect; pdfio_dict_t *annot, *action; @@ -1186,7 +1235,8 @@ items_find_position_to_break_line( pos->text_index = text_find_fitting( it[i]->u.text->text, it[i]->u.text->style, - width + width, + LINE_WIDTH ); if (pos->text_index == EMPTY_INT) { LOG_DEBUG("text_find_fitting failed."); @@ -1315,8 +1365,8 @@ pdf_texts_add_lyrics( return false; } if (item->u.text->style->href) { - if (!annot_add(ctx, item->u.text->style, width)) { - LOG_DEBUG("annot_add failed."); + if (!annot_url_link_add(ctx, item->u.text->style, width)) { + LOG_DEBUG("annot_url_link_add failed."); return false; } } @@ -1335,6 +1385,7 @@ pdf_texts_add_lyrics( sp = ctx->spaces; while (*sp) { if ((*sp)->line_item_index == i && (*sp)->text_index == c) { + // TODO: This code splits multibyte characters which leads to invalid UTF-8 (*texts)[ctx->text]->text = erealloc((*texts)[ctx->text]->text, (t+1) * sizeof(char)); (*texts)[ctx->text]->text[t] = 0; (*texts)[ctx->text]->style = cho_style_copy(item->u.text->style); @@ -1346,8 +1397,8 @@ pdf_texts_add_lyrics( return false; } if (item->u.text->style->href) { - if (!annot_add(ctx, item->u.text->style, width)) { - LOG_DEBUG("annot_add failed."); + if (!annot_url_link_add(ctx, item->u.text->style, width)) { + LOG_DEBUG("annot_url_link_add failed."); return false; } } @@ -1373,8 +1424,8 @@ pdf_texts_add_lyrics( return false; } if (item->u.text->style->href) { - if (!annot_add(ctx, item->u.text->style, width)) { - LOG_DEBUG("annot_add failed."); + if (!annot_url_link_add(ctx, item->u.text->style, width)) { + LOG_DEBUG("annot_url_link_add failed."); return false; } } @@ -1418,8 +1469,8 @@ pdf_texts_add_text( if (width > LINE_WIDTH) { char *t = (char *)&str; while (width > LINE_WIDTH) { - index = text_find_fitting(t, style, 0.0); - if (index == ERROR) { + index = text_find_fitting(t, style, 0.0, LINE_WIDTH); + if (index == EMPTY_INT) { LOG_DEBUG("text_find_fitting failed."); return false; } @@ -1437,8 +1488,8 @@ pdf_texts_add_text( (*texts)[ctx->text]->x = ctx->x; (*texts)[ctx->text]->y = ctx->y; if (style->href) { - if (!annot_add(ctx, style, width)) { - LOG_DEBUG("annot_add failed."); + if (!annot_url_link_add(ctx, style, width)) { + LOG_DEBUG("annot_url_link_add failed."); return false; } } @@ -1464,8 +1515,8 @@ pdf_texts_add_text( (*texts)[ctx->text]->x = ctx->x; (*texts)[ctx->text]->y = ctx->y; if (style->href) { - if (!annot_add(ctx, style, width)) { - LOG_DEBUG("annot_add failed."); + if (!annot_url_link_add(ctx, style, width)) { + LOG_DEBUG("annot_url_link_add failed."); return false; } } @@ -1480,8 +1531,8 @@ pdf_texts_add_text( (*texts)[ctx->text]->x = ctx->x; (*texts)[ctx->text]->y = ctx->y; if (style->href) { - if (!annot_add(ctx, style, width)) { - LOG_DEBUG("annot_add failed."); + if (!annot_url_link_add(ctx, style, width)) { + LOG_DEBUG("annot_url_link_add failed."); return false; } } @@ -1491,39 +1542,47 @@ pdf_texts_add_text( return true; } -/* static pdfio_dict_t * -annot_within_document_create(struct TocEntry *toc) +static bool +pdf_texts_add_toc_entry( + struct PDFContext *ctx, + struct TocEntry *entry, + struct ChoStyle *style, + double max_title_width +) { - pdfio_rect_t rect; - pdfio_dict_t *annot, *action; - rect.x1 = ctx->x; - rect.x2 = ctx->x + width; - rect.y1 = ctx->y - 2.0; - rect.y2 = ctx->y + style->font->size * 0.8; - annot = pdfioDictCreate(g_pdf_file); - if (!pdfioDictSetName(annot, "Subtype", "Link")) { - LOG_DEBUG("pdfioDictSetName failed."); - return false; - } - if (!pdfioDictSetRect(annot, "Rect", &rect)) { - LOG_DEBUG("pdfioDictSetRect failed."); - return false; - } - action = pdfioDictCreate(g_pdf_file); - if (!pdfioDictSetName(action, "S", "GoTo")) { - LOG_DEBUG("pdfioDictSetName failed."); + double width; + int index; + char tmp[strlen(entry->title)+1]; + strcpy((char *)&tmp, entry->title); + width = text_width(entry->title, style); + if (width == ERROR) { + LOG_DEBUG("text_width failed."); return false; } - if (!pdfioDictSetName(action, "D", destination)) { - LOG_DEBUG("pdfioDictSetName failed."); - return false; + width += MARGIN_HORIZONTAL; + if (width > max_title_width) { + char *t = (char *)&tmp; + while (width > max_title_width) { + index = text_find_fitting(t, style, MARGIN_HORIZONTAL, max_title_width); + if (index == EMPTY_INT) { + LOG_DEBUG("text_find_fitting failed."); + return false; + } + t[index] = 0; + printf("part '%s'\n", t); + t += index + 1; + width = text_width(t, style); + if (width == ERROR) { + LOG_DEBUG("text_width failed."); + return false; + } + } + printf("last part '%s'\n", t); + } else { + printf("title '%s'\n", entry->title); } - // if (!pdfioArrayAppendDict(ctx->content->pages[ctx->page]->annots, annot)) { - // LOG_DEBUG("pdfioArrayAppendDict failed."); - // return false; - // } return true; -} */ +} static bool pdf_toc_create( @@ -1533,6 +1592,7 @@ pdf_toc_create( struct Config *config ) { + double width, space_for_dots, max_title_width; struct PDFContext ctx; struct PDFText ***texts; ctx.text = 0; @@ -1546,13 +1606,55 @@ pdf_toc_create( struct OutputStyle *toc_style = config_output_style_get(config->output->styles, "toc"); struct TocEntry **toc; toc = pdf_content->toc; + max_title_width = LINE_WIDTH * 0.85 + MARGIN_HORIZONTAL; + // TODO: First find out how many pages the toc will have while (*toc) { - // annot_within_document_create(*toc); - if (!pdf_texts_add_text(&ctx, (*toc)->title, toc_style->style, A_LEFT)) { - LOG_DEBUG("pdf_texts_add_text failed."); + if (!pdf_texts_add_toc_entry(&ctx, *toc, toc_style->style, max_title_width)) { + LOG_DEBUG("pdf_texts_add_toc_entry failed."); return false; } - printf("%s\n", (*toc)->title); + if (ctx.y < MARGIN_BOTTOM) { + *texts = erealloc(*texts, (ctx.text+1) * sizeof(struct PDFText *)); + (*texts)[ctx.text] = NULL; + ctx.text = 0; + ctx.y = MEDIABOX_HEIGHT - MARGIN_TOP; + ctx.page++; + ctx.content->pages = erealloc(ctx.content->pages, (ctx.page+1) * sizeof(struct PDFPage *)); + ctx.content->pages[ctx.page] = pdf_page_new(); + texts = &ctx.content->pages[ctx.page]->texts; + } + // pdf_text_count_vertical_space((*toc)->title, toc_style->style); + /* if (!annot_page_link_add(&ctx, *toc, toc_style->style->font->size)) { + LOG_DEBUG("annot_page_link_add"); + return false; + } */ + // TODO: create (multi)line with title, dots and the page number + space_for_dots = LINE_WIDTH; + char page_no[5+1]; + sprintf((char *)&page_no, "%d", (*toc)->page_index+1); + width = text_width(page_no, toc_style->style); + + double most_right_x = MEDIABOX_WIDTH - MARGIN_HORIZONTAL; + double page_no_x = most_right_x - width; + *texts = erealloc(*texts, (ctx.text+1) * sizeof(struct PDFText *)); + (*texts)[ctx.text] = pdf_text_new(); + (*texts)[ctx.text]->text = strdup(page_no); + (*texts)[ctx.text]->style = cho_style_copy(toc_style->style); + (*texts)[ctx.text]->x = page_no_x; + (*texts)[ctx.text]->y = ctx.y; + ctx.text++; + ctx.y -= 8.0 + toc_style->style->font->size; + + /* space_for_dots -= width; + width = text_width((*toc)->title, toc_style->style); + space_for_dots -= width; + printf("width for dots '%.1f'\n", space_for_dots); + char line[4096]; + sprintf((char *)&line, "%s ........ %d", (*toc)->title, (*toc)->page_index+1); + if (!pdf_texts_add_text(&ctx, (char *)&line, toc_style->style, A_LEFT)) { + LOG_DEBUG("pdf_texts_add_text failed."); + return false; + } */ toc++; } *texts = erealloc(*texts, (ctx.text+1) * sizeof(struct PDFText *)); @@ -1735,8 +1837,8 @@ pdf_content_create( LOG_DEBUG("text_width failed."); return false; } - if (!annot_add(&ctx, style, width)) { - LOG_DEBUG("annot_add failed."); + if (!annot_url_link_add(&ctx, style, width)) { + LOG_DEBUG("annot_url_link_add failed."); return false; } }