lorid

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

commit d5287903db5d14f56f94795b8f1988014a096317
parent d9c47c88cabf983d380fdf5c2688d1687d972ed1
Author: nibo <nibo@relim.de>
Date:   Sun, 19 Jan 2025 10:49:58 +0100

Add page number to pdf pages (left/center/right)

Diffstat:
Mchordpro.h | 12++++++------
Mconfig.c | 40+++++++++++++++++++++++++++++++++++++++-
Mconfig.h | 1+
Mout_pdf.c | 243++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
Mout_pdf.h | 5+++++
5 files changed, 261 insertions(+), 40 deletions(-)

diff --git a/chordpro.h b/chordpro.h @@ -23,6 +23,12 @@ enum TextType { SF_LENGTH }; +enum Alignment { + A_LEFT, + A_CENTER, + A_RIGHT +}; + #include "config.h" @@ -35,12 +41,6 @@ enum TextType { #define URL_MAX_LEN 2000 #define FONT_NAME_MAX 100 -enum Alignment { - A_LEFT, - A_CENTER, - A_RIGHT -}; - enum Anchor { AN_PAPER, AN_PAGE, diff --git a/config.c b/config.c @@ -37,6 +37,10 @@ static const char *text_types[] = { "comment_italic", "comment_box" }; +static const char *alignments[] = { + "left", "center", "right" +}; + /* static const char *g_valid_styles[] = { "title", "subtitle", @@ -387,6 +391,27 @@ config_parse_mode_to_config_string(enum ParseMode mode) return parse_modes[mode]; } +static enum Alignment +config_alignment_parse(const char *str) +{ + if (!strcmp(str, "left")) { + return A_LEFT; + } else + if (!strcmp(str, "center")) { + return A_CENTER; + } else + if (!strcmp(str, "right")) { + return A_RIGHT; + } + return ENUM_UNKNOWN; +} + +static const char * +config_alignment_to_config_string(enum Alignment align) +{ + return alignments[align]; +} + static struct Config * config_load_default(void) { @@ -402,6 +427,7 @@ config_load_default(void) config->output->diagram->show = true; config->output->diagram->instrument = INS_GUITAR; config->output->system = NS_COMMON; + config->output->page_no_position = A_CENTER; config->output->styles = emalloc(SF_LENGTH * sizeof(struct ChoStyle *)); config->output->styles[SF_EMPTY] = NULL; @@ -472,7 +498,8 @@ config_print_default(void) config_notes_print_as_toml(NS_ROMAN); config_notes_print_as_toml(NS_NASHVILLE); printf("[output]\n"); - printf("system = \"%s\"\n\n", config_naming_system_to_config_string(config->output->system)); + printf("system = \"%s\"\n", config_naming_system_to_config_string(config->output->system)); + printf("page_no_position = \"%s\"\n\n", config_alignment_to_config_string(config->output->page_no_position)); printf("[output.toc]\n"); printf("show = %s\n", config->output->toc->show ? "true" : "false"); printf("title = \"%s\"\n\n", config->output->toc->title); @@ -841,6 +868,7 @@ config_load(const char *filepath) toml_value_t value; enum NamingSystem system; enum Instrument instrument; + enum Alignment position; struct Note **custom_notes; chorus = toml_table_table(output, "chorus"); if (chorus) { @@ -907,6 +935,16 @@ config_load(const char *filepath) } free(value.u.s); } + value = toml_table_string(output, "page_no_position"); + if (value.ok) { + position = config_alignment_parse(value.u.s); + if (position == ENUM_UNKNOWN) { + LOG_DEBUG("config_alignment_parse failed."); + return NULL; + } + config->output->page_no_position = position; + free(value.u.s); + } toml_table_t *styles = toml_table_table(output, "styles"); if (styles) { int i, unused; diff --git a/config.h b/config.h @@ -97,6 +97,7 @@ struct ConfigOutput { struct ConfigToc *toc; struct ConfigChordDiagram *diagram; enum NamingSystem system; + enum Alignment page_no_position; struct ChoStyle **styles; struct Note **notes; }; diff --git a/out_pdf.c b/out_pdf.c @@ -20,6 +20,7 @@ static char g_current_font_name[200]; static double g_current_font_size; static int g_current_page_index; static pdfio_file_t *g_pdf_file = NULL; +static struct Config *g_config = NULL; static const char *g_base_fonts[] = { "Courier", @@ -1569,8 +1570,164 @@ calc_x(double width, enum Alignment align) return MARGIN_HORIZONTAL; } -static void -pdf_page_close_then_add(struct PDFContext *ctx) +static const char * +handle_index(int index, char a, char b, char c) +{ + int i = 0; + static char str[16]; + memset(&str, 0, sizeof(str)); + switch (index) { + case 1: + str[i++] = a; + break; + case 2: + str[i++] = a; + str[i++] = a; + break; + case 3: + str[i++] = a; + str[i++] = a; + str[i++] = a; + break; + case 4: + str[i++] = a; + str[i++] = b; + break; + case 5: + str[i++] = b; + break; + case 6: + str[i++] = b; + str[i++] = a; + break; + case 7: + str[i++] = b; + str[i++] = a; + str[i++] = a; + break; + case 8: + str[i++] = b; + str[i++] = a; + str[i++] = a; + str[i++] = a; + break; + case 9: + str[i++] = a; + str[i++] = c; + } + str[i] = 0; + return str; +} + +static const char * +numeral_system_western_arabic_to_roman(unsigned int n) +{ + if (n > 999) { + util_log(LOG_ERR, "Converting numbers higher than 999 is not supported."); + return NULL; + } + const char *str; + static char roman[64]; + int k; + int r = 0; + memset(&str, 0, sizeof(str)); + char i = 'I'; + char v = 'V'; + char x = 'X'; + char l = 'L'; + char c = 'C'; + char d = 'D'; + char m = 'M'; + int index_0 = n - n / 10 * 10; + int index_1 = (n - n / 100 * 100 - index_0) / 10; + int index_2 = (n - n / 1000 * 1000 - index_0 - index_1) / 100; + if (index_2 > 0) { + str = handle_index(index_2, c, d, m); + for (k = 0; str[k]; k++, r++) { + roman[r] = str[k]; + } + } + if (index_1 > 0) { + str = handle_index(index_1, x, l, c); + for (k = 0; str[k]; k++, r++) { + roman[r] = str[k]; + } + } + if (index_0 > 0) { + str = handle_index(index_0, i, v, x); + for (k = 0; str[k]; k++, r++) { + roman[r] = str[k]; + } + } + roman[r] = 0; + return roman; +} + +static const char * +numeral_system_number_to_str(enum NumeralSystem system, int n) +{ + if (system == NUS_ROMAN) { + const char *str = numeral_system_western_arabic_to_roman((unsigned int)n); + if (!str) { + LOG_DEBUG("numeral_system_western_arabic_to_roman failed."); + return NULL; + } + return str; + } else { + static char str[11+1]; + sprintf((char *)&str, "%d", n); + return str; + } +} + +static bool +pdf_page_add_page_no(struct PDFContext *ctx, enum NumeralSystem numeral_system) +{ + struct PDFText ***texts; + struct ChoStyle *style; + const char *page_no; + double width, x; + + printf("page no position %d\n", g_config->output->page_no_position); + + style = cho_style_new(); + style->font->name = strdup(DEFAULT_FONT_FAMILY); + texts = &ctx->content->pages[ctx->page]->texts; + page_no = numeral_system_number_to_str(numeral_system, ctx->page+1); + if (!page_no) { + LOG_DEBUG("numeral_system_number_to_str failed."); + return false; + } + width = text_width(page_no, style); + if (width == ERROR) { + LOG_DEBUG("text_width failed."); + return false; + } + *texts = erealloc(*texts, (ctx->text+1) * sizeof(struct PDFText *)); + (*texts)[ctx->text] = pdf_text_new(); + (*texts)[ctx->text]->text = strdup(page_no); + (*texts)[ctx->text]->style = style; + (*texts)[ctx->text]->x = MEDIABOX_WIDTH - MARGIN_HORIZONTAL / 2 - width; + (*texts)[ctx->text]->y = ctx->margin_bottom / 2; + (*texts)[ctx->text]->width = width; + switch (g_config->output->page_no_position) { + case A_LEFT: + x = MARGIN_HORIZONTAL / 2; + break; + case A_CENTER: + x = MARGIN_HORIZONTAL + (LINE_WIDTH - width) / 2; + break; + case A_RIGHT: + x = MEDIABOX_WIDTH - MARGIN_HORIZONTAL / 2 - width; + break; + } + (*texts)[ctx->text]->x = x; + ctx->text++; + return true; +} + +static bool +pdf_page_close_then_add(struct PDFContext *ctx, enum NumeralSystem numeral_system) { ctx->content->pages[ctx->page]->texts = erealloc( ctx->content->pages[ctx->page]->texts, @@ -1594,6 +1751,11 @@ pdf_page_close_then_add(struct PDFContext *ctx) ctx->content->pages = erealloc(ctx->content->pages, (ctx->page+1) * sizeof(struct PDFPage *)); ctx->content->pages[ctx->page] = pdf_page_new(); ctx->y = MEDIABOX_HEIGHT - MARGIN_TOP; + if (!pdf_page_add_page_no(ctx, numeral_system)) { + LOG_DEBUG("pdf_page_add_page_no failed."); + return false; + } + return true; } static bool @@ -1601,7 +1763,8 @@ pdf_texts_add_text( struct PDFContext *ctx, const char *text, struct ChoStyle *style, - enum Alignment align + enum Alignment align, + enum NumeralSystem numeral_system ) { struct PDFText ***texts; @@ -1619,7 +1782,10 @@ pdf_texts_add_text( char *t = (char *)&str; while (width > LINE_WIDTH) { if (ctx->y < ctx->margin_bottom) { - pdf_page_close_then_add(ctx); + if (!pdf_page_close_then_add(ctx, numeral_system)) { + LOG_DEBUG("pdf_page_close_then_add failed."); + return false; + } texts = &ctx->content->pages[ctx->page]->texts; } index = text_find_fitting(t, style, 0.0, LINE_WIDTH); @@ -1679,7 +1845,10 @@ pdf_texts_add_text( ctx->y -= 8.0 + style->font->size; } else { if (ctx->y < ctx->margin_bottom) { - pdf_page_close_then_add(ctx); + if (!pdf_page_close_then_add(ctx, numeral_system)) { + LOG_DEBUG("pdf_page_close_then_add failed."); + return false; + } texts = &ctx->content->pages[ctx->page]->texts; } ctx->x = calc_x(width, align); @@ -1795,7 +1964,10 @@ pdf_texts_add_toc_entry( line_count = 0; while (width > max_song_title_width) { if (ctx->y < MARGIN_BOTTOM) { - pdf_page_close_then_add(ctx); + if (!pdf_page_close_then_add(ctx, NUS_ROMAN)) { + LOG_DEBUG("pdf_page_close_then_add failed."); + return false; + } texts = &ctx->content->pages[ctx->page]->texts; } index = text_find_fitting(t, style, MARGIN_HORIZONTAL, max_song_title_width); @@ -1823,7 +1995,10 @@ pdf_texts_add_toc_entry( width += MARGIN_HORIZONTAL; } if (ctx->y < MARGIN_BOTTOM) { - pdf_page_close_then_add(ctx); + if (!pdf_page_close_then_add(ctx, NUS_ROMAN)) { + LOG_DEBUG("pdf_page_close_then_add failed."); + return false; + } texts = &ctx->content->pages[ctx->page]->texts; } end_of_title_x = width; @@ -1875,13 +2050,10 @@ pdf_texts_add_toc_entry( ctx->y -= 8.0 + style->font->size; } else { 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(); + if (!pdf_page_close_then_add(ctx, NUS_ROMAN)) { + LOG_DEBUG("pdf_page_close_then_add failed."); + return false; + } texts = &ctx->content->pages[ctx->page]->texts; } end_of_title_x = width; @@ -1958,6 +2130,10 @@ pdf_toc_create( ctx.content->pages = emalloc(sizeof(struct PDFPage *)); ctx.content->pages[ctx.page] = pdf_page_new(); texts = &ctx.content->pages[ctx.page]->texts; + if (!pdf_page_add_page_no(&ctx, NUS_ROMAN)) { + LOG_DEBUG("pdf_page_add_page_no failed."); + return false; + } toc_style = config->output->styles[SF_TOC]; toc = pdf_content->toc; max_song_title_width = LINE_WIDTH * 0.85; @@ -1967,7 +2143,7 @@ pdf_toc_create( return false; } title_style = config->output->styles[SF_TOC_TITLE]; - if (!pdf_texts_add_text(&ctx, config->output->toc->title, title_style, A_CENTER)) { + if (!pdf_texts_add_text(&ctx, config->output->toc->title, title_style, A_CENTER, NUS_ROMAN)) { LOG_DEBUG("pdf_texts_add_text failed."); return false; } @@ -2020,6 +2196,10 @@ pdf_content_create( texts = &ctx.content->pages[ctx.page]->texts; imgs = &ctx.content->pages[ctx.page]->images; diagrams = &ctx.content->pages[ctx.page]->diagrams; + if (!pdf_page_add_page_no(&ctx, NUS_WESTERN_ARABIC)) { + LOG_DEBUG("pdf_page_add_page_no failed."); + return false; + } int s; // int the_page; for (s = 0; songs[s]; s++) { @@ -2059,7 +2239,7 @@ pdf_content_create( ctx.content->toc[ctx.toc_entry]->page_index = ctx.page; ctx.content->toc[ctx.toc_entry]->page_y = ctx.y; ctx.toc_entry++; - if (!pdf_texts_add_text(&ctx, (*m)->value, (*m)->style, A_CENTER)) { + if (!pdf_texts_add_text(&ctx, (*m)->value, (*m)->style, A_CENTER, NUS_WESTERN_ARABIC)) { LOG_DEBUG("pdf_texts_add_text failed."); return false; } @@ -2077,7 +2257,7 @@ pdf_content_create( */ struct ChoStyle *output_style; output_style = config->output->styles[SF_SUBTITLE]; - if (!pdf_texts_add_text(&ctx, (*m)->value, output_style, A_CENTER)) { + if (!pdf_texts_add_text(&ctx, (*m)->value, output_style, A_CENTER, NUS_WESTERN_ARABIC)) { LOG_DEBUG("pdf_texts_add_text failed."); return false; } @@ -2089,7 +2269,7 @@ pdf_content_create( ctx.y -= 30.0; for (se = songs[s]->sections; *se; se++) { if ((*se)->label) { - if (!pdf_texts_add_text(&ctx, (*se)->label->text, (*se)->label->style, A_LEFT)) { + if (!pdf_texts_add_text(&ctx, (*se)->label->text, (*se)->label->style, A_LEFT, NUS_WESTERN_ARABIC)) { LOG_DEBUG("pdf_texts_add_text failed."); return false; } @@ -2206,7 +2386,10 @@ pdf_content_create( *texts = erealloc(*texts, (--ctx.text) * sizeof(struct PDFText *)); } } - pdf_page_close_then_add(&ctx); + if (!pdf_page_close_then_add(&ctx, NUS_WESTERN_ARABIC)) { + LOG_DEBUG("pdf_page_close_then_add failed."); + return false; + } texts = &ctx.content->pages[ctx.page]->texts; imgs = &ctx.content->pages[ctx.page]->images; diagrams = &ctx.content->pages[ctx.page]->diagrams; @@ -2303,7 +2486,10 @@ pdf_content_create( } ctx.y -= 8.0 + ctx.biggest_font_size; if (ctx.y < ctx.margin_bottom) { - pdf_page_close_then_add(&ctx); + if (!pdf_page_close_then_add(&ctx, NUS_WESTERN_ARABIC)) { + LOG_DEBUG("pdf_page_close_then_add failed."); + return false; + } texts = &ctx.content->pages[ctx.page]->texts; imgs = &ctx.content->pages[ctx.page]->images; diagrams = &ctx.content->pages[ctx.page]->diagrams; @@ -2315,22 +2501,10 @@ pdf_content_create( text_above_update_positions(left_aboves, ctx.consumed_lyrics); } if ((*li)->btype == BT_PAGE) { - *texts = erealloc(*texts, (ctx.text+1) * sizeof(struct PDFText *)); - (*texts)[ctx.text] = NULL; - *imgs = erealloc(*imgs, (ctx.image+1) * sizeof(struct PDFImage *)); - (*imgs)[ctx.image] = NULL; - *diagrams = erealloc(*diagrams, (ctx.diagram+1) * sizeof(struct ChordDiagram *)); - (*diagrams)[ctx.diagram] = NULL; - ctx.text = 0; - ctx.image = 0; - ctx.diagram = 0; - ctx.page++; - ctx.content->pages = erealloc(ctx.content->pages, (ctx.page+1) * sizeof(struct PDFPage *)); - ctx.content->pages[ctx.page] = pdf_page_new(); + pdf_page_close_then_add(&ctx, NUS_WESTERN_ARABIC); texts = &ctx.content->pages[ctx.page]->texts; imgs = &ctx.content->pages[ctx.page]->images; diagrams = &ctx.content->pages[ctx.page]->diagrams; - ctx.y = MEDIABOX_HEIGHT - MARGIN_TOP; } } ctx.y -= SECTION_GAP_WIDTH; @@ -2459,6 +2633,8 @@ out_pdf_create( pdfio_rect_t crop_box = { 0.0, 0.0, MEDIABOX_WIDTH, MEDIABOX_HEIGHT }; char *dirpath, *pdf_filepath; + g_config = config; + memset(&g_current_font_name, 0, sizeof(g_current_font_name)); memset(&g_cho_dirpath, 0, PATH_MAX); @@ -2523,6 +2699,7 @@ out_pdf_create( g_fonts = NULL; g_current_font_size = 0.0; g_current_page_index = 0; + g_config = NULL; return pdf_filepath; CLEAN: if (unlink(pdf_filepath)) { diff --git a/out_pdf.h b/out_pdf.h @@ -25,6 +25,11 @@ enum FontType { FT_OTF }; +enum NumeralSystem { + NUS_WESTERN_ARABIC, + NUS_ROMAN +}; + struct CharPosition { int line_item_index; int text_index;