commit e321cff419173fb31c3dece2b5900079b170d7da
parent dcb6d62024e4b11f1339cf0a4e1ecf5635036e44
Author: nibo <nibo@relim.de>
Date: Sun, 28 Jul 2024 21:59:05 +0200
Add a bunch of stuff
Diffstat:
| M | Makefile | | | 4 | ++-- |
| M | chordpro.c | | | 65 | +++++++++++++++++++++++++++++++---------------------------------- |
| M | chordpro.h | | | 8 | ++++++-- |
| M | lorid.c | | | 36 | ++++++++++++++++++------------------ |
| M | out_pdf.c | | | 362 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------- |
| M | out_pdf.h | | | 15 | ++++++++++++++- |
| A | str.c | | | 98 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | str.h | | | 3 | +++ |
| M | todo | | | 3 | ++- |
9 files changed, 437 insertions(+), 157 deletions(-)
diff --git a/Makefile b/Makefile
@@ -1,7 +1,7 @@
all:
- $(CC) -pedantic -Wall -Wextra -O2 fontconfig.c config.c chordpro.c out_pdf.c lorid.c -o lorid -lpdfio -ltoml -lfontconfig
+ $(CC) -pedantic -Wall -Wextra -O2 str.c fontconfig.c config.c chordpro.c out_pdf.c lorid.c -o lorid -lpdfio -ltoml -lfontconfig
debug:
- $(CC) -g -pedantic -Wall -Wextra fontconfig.c config.c chordpro.c out_pdf.c lorid.c -o lorid -lpdfio -ltoml -lfontconfig
+ $(CC) -g -pedantic -Wall -Wextra str.c fontconfig.c config.c chordpro.c out_pdf.c lorid.c -o lorid -lpdfio -ltoml -lfontconfig
fontconfig:
$(CC) -g chordpro.c fontconfig.c -o fontconfig -lfontconfig
.PHONY: all debug fontconfig
diff --git a/chordpro.c b/chordpro.c
@@ -6,6 +6,7 @@
#include <ctype.h>
#include "chordpro.h"
#include "config.h"
+#include "str.h"
static const char *environment_directives[] = {
"start_of_chorus", "soc", "end_of_chorus", "eoc", "chorus",
@@ -289,15 +290,6 @@ void the_default_style_properties(void)
}
}
-static char *string_remove_leading_whitespace(const char *str)
-{
- int i = 0;
- while (str[i] == ' ' || str[i] == '\t') {
- i++;
- }
- return strdup(&str[i]);
-}
-
static inline bool is_whitespace(char c)
{
if (
@@ -511,25 +503,6 @@ void cho_fonts_free(struct Font **fonts)
free(fonts);
}
-char *cho_font_name_sanitize(const char *name)
-{
- char *sanitized = NULL;
- int i = 0;
- int s = 0;
- char c;
- for (; name[i] != 0; i++) {
- if (name[i] == ' ')
- continue;
- c = (char)tolower(name[i]);
- sanitized = realloc(sanitized, (s+1) * sizeof(char));
- sanitized[s] = c;
- s++;
- }
- sanitized = realloc(sanitized, (s+1) * sizeof(char));
- sanitized[s] = 0;
- return sanitized;
-}
-
enum FontFamily cho_font_family_parse(const char *str)
{
if (strcasecmp(str, "sans") == 0) {
@@ -1293,10 +1266,21 @@ static struct ChoMetadata *cho_metadata_new(void)
return meta;
}
+const char *cho_metadata_get(struct ChoMetadata **metadata, const char *name)
+{
+ int m;
+ for (m = 0; metadata[m]; m++) {
+ if (strcmp(metadata[m]->name, name) == 0) {
+ return metadata[m]->value;
+ }
+ }
+ return NULL;
+}
+
static struct ChoMetadata *cho_metadata_split(const char *directive_value)
{
struct ChoMetadata *meta = cho_metadata_new();
- char *value = string_remove_leading_whitespace(directive_value);
+ char *value = str_remove_leading_whitespace(directive_value);
int i = 0;
int n = 0;
int v = 0;
@@ -1410,6 +1394,14 @@ static struct ChoSong *cho_song_new(void)
return song;
}
+int cho_song_count(struct ChoSong **songs)
+{
+ int i = 0;
+ while (songs[i] != NULL)
+ i++;
+ return i;
+}
+
static void cho_song_free(struct ChoSong *song)
{
int i = 0;
@@ -1718,7 +1710,7 @@ END:
return directive;
}
-struct ChoSong **cho_parse(FILE *fp, struct Config *config)
+struct ChoSong **cho_songs_parse(FILE *fp, struct Config *config)
{
g_config = config;
char buf = 0;
@@ -1895,6 +1887,8 @@ struct ChoSong **cho_parse(FILE *fp, struct Config *config)
break;
case DT_PREAMBLE:
// The only preamble directive is 'new_song'
+ cho_line_item_free(songs[so]->sections[se]->lines[li]->lyrics[ly]);
+ free(songs[so]->sections[se]->lines[li]->lyrics);
free(songs[so]->sections[se]->lines[li]);
songs[so]->sections[se]->lines[li] = NULL;
songs[so]->metadata = realloc(songs[so]->metadata, (m+1) * sizeof(struct ChoMetadata *));
@@ -1907,11 +1901,14 @@ struct ChoSong **cho_parse(FILE *fp, struct Config *config)
songs[so] = cho_song_new();
se = 0;
li = 0;
+ ly = 0;
m = 0;
songs[so]->sections = malloc((se+1) * sizeof(struct ChoSection *));
songs[so]->sections[se] = cho_section_new();
songs[so]->sections[se]->lines = realloc(songs[so]->sections[se]->lines, (li+1) * sizeof(struct ChoLine *));
songs[so]->sections[se]->lines[li] = cho_line_new();
+ songs[so]->sections[se]->lines[li]->lyrics = malloc(sizeof(struct ChoLineItem *));
+ songs[so]->sections[se]->lines[li]->lyrics[ly] = cho_line_item_new();
break;
case DT_FONT:
// TODO: Reset directive value to default
@@ -1959,7 +1956,7 @@ struct ChoSong **cho_parse(FILE *fp, struct Config *config)
songs[so]->sections = realloc(songs[so]->sections, (se+1) * sizeof(struct ChoSection *));
songs[so]->sections[se] = cho_section_new();
songs[so]->sections[se]->type = directive->stype;
- songs[so]->sections[se]->name = string_remove_leading_whitespace(directive_value);
+ songs[so]->sections[se]->name = str_remove_leading_whitespace(directive_value);
li = 0;
songs[so]->sections[se]->lines = malloc(sizeof(struct ChoLine *));
songs[so]->sections[se]->lines[li] = cho_line_new();
@@ -1999,7 +1996,7 @@ struct ChoSong **cho_parse(FILE *fp, struct Config *config)
songs[so]->metadata = realloc(songs[so]->metadata, (m+1) * sizeof(struct ChoMetadata *));
songs[so]->metadata[m] = cho_metadata_new();
songs[so]->metadata[m]->name = strdup(directive_name);
- songs[so]->metadata[m]->value = string_remove_leading_whitespace(directive_value);
+ songs[so]->metadata[m]->value = str_remove_leading_whitespace(directive_value);
m++;
}
break;
@@ -2015,7 +2012,7 @@ struct ChoSong **cho_parse(FILE *fp, struct Config *config)
songs[so]->sections[se]->lines[li]->lyrics[ly] = cho_line_item_new();
cho_style_free(songs[so]->sections[se]->lines[li]->lyrics[ly]->style);
songs[so]->sections[se]->lines[li]->lyrics[ly]->style = cho_style_duplicate(directive->style);
- char *trimmed_directive_value = string_remove_leading_whitespace(directive_value);
+ char *trimmed_directive_value = str_remove_leading_whitespace(directive_value);
songs[so]->sections[se]->lines[li]->lyrics[ly]->text = trimmed_directive_value;
te += strlen(trimmed_directive_value);
break;
@@ -2024,7 +2021,7 @@ struct ChoSong **cho_parse(FILE *fp, struct Config *config)
break;
case DT_FONT:
sprop.ftype = directive->ftype;
- char *dir_value = string_remove_leading_whitespace(directive_value);
+ char *dir_value = str_remove_leading_whitespace(directive_value);
switch (directive->sprop) {
case SPT_FONT:
sprop.u.font_name = malloc((strlen(dir_value)+1) * sizeof(char));
diff --git a/chordpro.h b/chordpro.h
@@ -203,10 +203,14 @@ struct ChoSong {
struct ChoSection **sections;
};
-struct ChoSong **cho_parse(FILE *fp, struct Config *config);
+struct ChoSong **cho_songs_parse(FILE *fp, struct Config *config);
+int cho_song_count(struct ChoSong **songs);
void cho_songs_free(struct ChoSong **song);
int cho_line_item_count(struct ChoLineItem **items);
int cho_chord_count(struct ChoChord **chords);
+
+const char *cho_metadata_get(struct ChoMetadata **metadata, const char *name);
+
struct Style *cho_style_new(void);
void cho_style_free(struct Style *style);
struct Style *cho_style_duplicate(struct Style *style);
@@ -220,7 +224,7 @@ void cho_font_free(struct Font *font);
struct Font *cho_font_duplicate(struct Font *font);
void cho_fonts_free(struct Font **fonts);
-char *cho_font_name_sanitize(const char *name);
+char *cho_font_name_normalize(const char *name);
enum FontFamily cho_font_family_parse(const char *str);
const char *cho_font_family_to_string(enum FontFamily font_family);
diff --git a/lorid.c b/lorid.c
@@ -7,26 +7,33 @@
#include "config.h"
#include "chordpro.h"
#include "out_pdf.h"
+#include "str.h"
int main(int argc, char *argv[])
{
static struct option long_options[] = {
{ "print-default-config", no_argument, 0, 'p' },
{ "config", required_argument, 0, 'c' },
+ { "output", required_argument, 0, 'o' },
{ 0, 0, 0, 0 }
};
int o, option_index;
char *config_filepath = NULL;
+ char *output = NULL;
FILE *fp;
- while ((o = getopt_long(argc, argv, "pc:", long_options, &option_index)) != -1) {
+ while ((o = getopt_long(argc, argv, "pc:o:", long_options, &option_index)) != -1) {
switch(o) {
- case 'p':
- config_print_default();
- return 0;
- case 'c':
- config_filepath = malloc((strlen(optarg)+1) * sizeof(char));
- strcpy(config_filepath, optarg);
- break;
+ case 'p':
+ config_print_default();
+ return 0;
+ case 'c':
+ config_filepath = malloc((strlen(optarg)+1) * sizeof(char));
+ strcpy(config_filepath, optarg);
+ break;
+ case 'o':
+ output = realloc(output, (strlen(optarg)+1) * sizeof(char));
+ strcpy(output, optarg);
+ break;
}
}
struct Config *config = config_load(config_filepath);
@@ -34,14 +41,6 @@ int main(int argc, char *argv[])
fprintf(stderr, "config_load failed.\n");
return 1;
}
- /* int u = 0;
- while (config->printable_items[u] != NULL) {
- printf("---- BEGIN PRINTABLE ITEM ----\n");
- printf("name: %s\n", config->printable_items[u]->name);
- cho_style_print(config->printable_items[u]->style);
- printf("---- END PRINTABLE ITEM ------\n");
- u++;
- } */
free(config_filepath);
if (argc == optind) {
fp = stdin;
@@ -55,15 +54,16 @@ int main(int argc, char *argv[])
fprintf(stderr, "Provide only one file.\n");
return 1;
}
- struct ChoSong **songs = cho_parse(fp, config);
+ struct ChoSong **songs = cho_songs_parse(fp, config);
if (songs == NULL) {
fprintf(stderr, "cho_parse failed.\n");
return 1;
}
- if (!out_pdf_new(argv[1], songs, config)) {
+ if (!out_pdf_new(argc == optind+1 ? argv[argc-1] : NULL, output ? output : NULL, songs, config)) {
fprintf(stderr, "out_pdf_new failed.\n");
return 1;
}
+ free(output);
cho_songs_free(songs);
config_free(config);
fclose(fp);
diff --git a/out_pdf.c b/out_pdf.c
@@ -1,12 +1,14 @@
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
+#include <sys/stat.h>
#include <pdfio.h>
#include <pdfio-content.h>
#include "chordpro.h"
#include "config.h"
#include "out_pdf.h"
#include "fontconfig.h"
+#include "str.h"
static struct Fnt **g_fonts = NULL;
@@ -25,47 +27,6 @@ static pdfio_obj_t *out_pdf_fnt_obj_get_by_name(const char *name)
return NULL;
}
-static char *string_trim(const char *text)
-{
- char *trimmed_text = NULL;
- int begin = 0;
- int end = 0;
- int len = (int)strlen(text);
- for (int i=0; i<len; i++) {
- if (
- text[i] == ' ' ||
- text[i] == '\n' ||
- text[i] == '\t' ||
- text[i] == '\r'
- )
- begin++;
- else
- break;
- }
- for (int i=len-1; i>=0; i--) {
- if (
- text[i] == ' '||
- text[i] == '\n' ||
- text[i] == '\t' ||
- text[i] == '\r'
- )
- end++;
- else
- break;
- }
- int k = 0;
- for (int i=0; i<len; i++) {
- if (i >= begin && i < len - end) {
- trimmed_text = realloc(trimmed_text, (k+1) * sizeof(char));
- trimmed_text[k] = text[i];
- k++;
- }
- }
- trimmed_text = realloc(trimmed_text, (k+1) * sizeof(char));
- trimmed_text[k] = 0;
- return trimmed_text;
-}
-
/* static void out_pdf_font_add(struct Font *font, struct Font ***array)
{
int a = 0;
@@ -119,7 +80,7 @@ static void out_pdf_add_fonts(struct Font *font, struct Font ***fonts)
while (font->name[i] != 0) {
if (font->name[i] == ',') {
part[p] = 0;
- trimmed = string_trim(part);
+ trimmed = str_trim(part);
new_font = malloc(sizeof(struct Font));
new_font->name = trimmed;
new_font->family = font->family;
@@ -140,7 +101,7 @@ static void out_pdf_add_fonts(struct Font *font, struct Font ***fonts)
i++;
}
part[p] = 0;
- trimmed = string_trim(part);
+ trimmed = str_trim(part);
new_font = malloc(sizeof(struct Font));
new_font->name = trimmed;
new_font->family = font->family;
@@ -192,7 +153,7 @@ static struct Font **out_pdf_font_get_all(struct ChoSong **songs, struct Config
return fonts;
}
-size_t out_pdf_text_find_fitting_length(const char *str, size_t len)
+static size_t out_pdf_text_find_fitting_length(const char *str, size_t len)
{
char tmp[512];
strncpy((char *)&tmp, str, len);
@@ -211,28 +172,68 @@ size_t out_pdf_text_find_fitting_length(const char *str, size_t len)
return len;
}
-static bool out_pdf_text_show(pdfio_stream_t *stream, struct TextLineItem *item, double y)
+static enum FileType file_type(const char *path)
{
- if (!pdfioContentTextBegin(stream)) {
- fprintf(stderr, "pdfioContentTextBegin failed.\n");
- return false;
+ struct stat s;
+ if (stat(path, &s) != 0) {
+ return F_ERROR;
}
- if (!pdfioContentTextMoveTo(stream, item->x, y)) {
- fprintf(stderr, "pdfioContentTextMoveTo failed.\n");
- return false;
+ switch (s.st_mode & S_IFMT) {
+ case S_IFDIR:
+ return F_FOLDER;
+ case S_IFREG:
+ return F_REG_FILE;
}
- if (!pdfioContentTextShow(stream, true, item->text)) {
- fprintf(stderr, "pdfioContentTextShow failed.\n");
- return false;
+ return F_OTHER;
+}
+
+static char *filepath_add_ending_slash_if_missing(const char *path)
+{
+ size_t len = strlen(path);
+ if (path[len-1] == '/') {
+ return strdup(path);
+ } else {
+ char *path_with_slash = malloc((len+2) * sizeof(char));
+ strcpy(path_with_slash, path);
+ path_with_slash[len] = '/';
+ path_with_slash[len+1] = 0;
+ return path_with_slash;
}
- if (!pdfioContentTextEnd(stream)) {
- fprintf(stderr, "pdfioContentTextEnd failed.\n");
- return false;
+}
+
+static char *filepath_basename(const char *path)
+{
+ int begin = 0;
+ int i;
+ for (i = 0; path[i] != 0; i++) {
+ if (path[i] == '/') {
+ begin = i+1;
+ }
}
- return true;
+ return strdup(&path[begin]);
+}
+
+static char *filepath_dirname(const char *path)
+{
+ char *dirname;
+ int end = 0;
+ int i;
+ for (i = 0; path[i] != 0; i++) {
+ if (path[i] == '/') {
+ end = i;
+ }
+ }
+ dirname = malloc((end+1)* sizeof(char));
+ i = 0;
+ while (i<end) {
+ dirname[i] = path[i];
+ i++;
+ }
+ dirname[i] = 0;
+ return dirname;
}
-static char *out_pdf_filename_create(const char *old)
+static char *file_extension_replace_or_add(const char *old)
{
char *new = NULL;
int mark = -1;
@@ -270,6 +271,87 @@ static char *out_pdf_filename_create(const char *old)
return new;
}
+static char *out_pdf_filename_generate_from_songs(struct ChoSong **songs)
+{
+ char *filename;
+ char *normalized_title;
+ const char *title;
+ int len = cho_song_count(songs);
+ if (len == 0)
+ return NULL;
+ if (len == 1) {
+ title = cho_metadata_get(songs[0]->metadata, "title");
+ if (!title) {
+ fprintf(stderr, "INFO: Song has no title.\n");
+ return NULL;
+ }
+ normalized_title = str_normalize(title);
+ filename = file_extension_replace_or_add(normalized_title);
+ free(normalized_title);
+ return filename;
+ }
+ return strdup("collection-of-songs.pdf");
+ /* if (len > 1) {
+ } */
+}
+
+static char *out_pdf_filename_create(struct ChoSong **songs, const char *cho_filepath, const char *out)
+{
+ char *pdf_filepath = NULL;
+ char *pdf_filename;
+ char *tmp;
+ if (cho_filepath) {
+ pdf_filepath = file_extension_replace_or_add(cho_filepath);
+ pdf_filename = filepath_basename(pdf_filepath);
+ } else {
+ pdf_filename = out_pdf_filename_generate_from_songs(songs);
+ if (!pdf_filename) {
+ fprintf(stderr, "out_pdf_filename_generate_from_songs failed.\n");
+ return NULL;
+ }
+ }
+ if (out) {
+ switch (file_type(out)) {
+ case F_ERROR:
+ tmp = filepath_dirname(out);
+ if (file_type(tmp) == F_FOLDER) {
+ free(pdf_filepath);
+ free(pdf_filename);
+ free(tmp);
+ return strdup(out);
+ } else {
+ free(tmp);
+ fprintf(stderr, "INFO: Invalid argument --output/-o value.\n");
+ return NULL;
+ }
+ break;
+ case F_REG_FILE:
+ free(pdf_filepath);
+ free(pdf_filename);
+ return strdup(out);
+ case F_FOLDER:
+ tmp = filepath_add_ending_slash_if_missing(out);
+ tmp = realloc(tmp, (strlen(tmp)+strlen(pdf_filename)+1) * sizeof(char));
+ strcat(tmp, pdf_filename);
+ free(pdf_filename);
+ free(pdf_filepath);
+ return tmp;
+ case F_OTHER:
+ fprintf(stderr, "INFO: Invalid argument --output/-o value. It doesn't refer to a folder or regular file.\n");
+ return NULL;
+ default:
+ fprintf(stderr, "INFO: Invalid enum FileType value.\n");
+ return NULL;
+ }
+ } else {
+ if (pdf_filepath) {
+ free(pdf_filename);
+ return pdf_filepath;
+ }
+ return pdf_filename;
+ }
+}
+
static struct Fnt *out_pdf_fnt_new(void)
{
struct Fnt *fnt = malloc(sizeof(struct Fnt));
@@ -280,7 +362,7 @@ static struct Fnt *out_pdf_fnt_new(void)
static char *out_pdf_fnt_name_create(struct Font *font)
{
- char *name = cho_font_name_sanitize(font->name);
+ char *name = str_normalize(font->name);
const char *family = cho_font_family_to_string(font->family);
const char *style = cho_font_style_to_string(font->style);
const char *weight = cho_font_weight_to_string(font->weight);
@@ -376,42 +458,107 @@ static bool out_pdf_font_set(pdfio_stream_t *stream, struct Font *font)
return true;
}
-static bool out_pdf_style_apply(pdfio_stream_t *stream, struct Style *style)
+static double text_width(struct TextLineItem *item)
+{
+ char *name = out_pdf_fnt_name_create(item->style->font);
+ pdfio_obj_t *font_obj = out_pdf_fnt_obj_get_by_name(name);
+ if (font_obj == NULL) {
+ fprintf(stderr, "out_pdf_fnt_obj_get_by_name failed.\n");
+ return -1.0;
+ }
+ free(name);
+ return pdfioContentTextMeasure(font_obj, item->text, item->style->font->size);
+}
+
+static bool out_pdf_draw_line(pdfio_stream_t *stream, struct TextLineItem *item, double y, double width, enum LineLocation line_location)
{
double red, green, blue;
- if (!out_pdf_font_set(stream, style->font)) {
+ switch (line_location) {
+ case LL_UNDER:
+ red = item->style->underline_color->red / 255.0;
+ green = item->style->underline_color->green / 255.0;
+ blue = item->style->underline_color->blue / 255.0;
+ y -= 2.0;
+ break;
+ case LL_STRIKETHROUGH:
+ red = item->style->strikethrough_color->red / 255.0;
+ green = item->style->strikethrough_color->green / 255.0;
+ blue = item->style->strikethrough_color->blue / 255.0;
+ printf("add to y: %.2f\n", item->style->font->size * 0.21);
+ y += item->style->font->size * 0.3;
+ break;
+ case LL_OVER:
+ red = item->style->overline_color->red / 255.0;
+ green = item->style->overline_color->green / 255.0;
+ blue = item->style->overline_color->blue / 255.0;
+ break;
+ }
+ if (!pdfioContentPathMoveTo(stream, item->x, y)) {
+ fprintf(stderr, "pdfioContentPathMoveTo failed.\n");
+ return false;
+ }
+ if (!pdfioContentPathLineTo(stream, item->x + width, y)) {
+ fprintf(stderr, "pdfioContentPathLineTo failed.\n");
+ return false;
+ }
+ if (!pdfioContentSetStrokeColorRGB(stream, red, green, blue)) {
+ fprintf(stderr, "pdfioContentSetStrokeColorRGB failed.\n");
+ return false;
+ }
+ if (!pdfioContentStroke(stream)) {
+ fprintf(stderr, "pdfioContentStroke failed.\n");
+ return false;
+ }
+ return true;
+}
+
+static bool out_pdf_text_show(pdfio_stream_t *stream, struct TextLineItem *item, double y)
+{
+ double red, green, blue, width;
+ // TODO: Maybe store the width in TextLineItem
+ width = text_width(item);
+ if (width == EMPTY) {
+ fprintf(stderr, "text_width failed.\n");
+ return false;
+ }
+ if (!out_pdf_font_set(stream, item->style->font)) {
fprintf(stderr, "out_pdf_font_set failed.\n");
return false;
}
- red = style->foreground_color->red / 255.0;
- green = style->foreground_color->green / 255.0;
- blue = style->foreground_color->blue / 255.0;
+ red = item->style->foreground_color->red / 255.0;
+ green = item->style->foreground_color->green / 255.0;
+ blue = item->style->foreground_color->blue / 255.0;
// printf("foreground_color: %s\n", the_rgb_color(style->foreground_color));
- // printf("%f, %f, %f\n", red, green, blue);
if (!pdfioContentSetFillColorRGB(stream, red, green, blue)) {
fprintf(stderr, "pdfioContentSetFillColorRGB failed.\n");
return false;
}
- // cho_style_print(style);
- /* printf("name: %s\n", style->font->name);
- printf("size: %.1f\n", style->font->size);
- printf(
- "\x1b[38;2;%d;%d;%dmRGBCOLOR\x1b[0m\n",
- style->foreground_color->red,
- style->foreground_color->green,
- style->foreground_color->blue
- ); */
+ if (!pdfioContentTextBegin(stream)) {
+ fprintf(stderr, "pdfioContentTextBegin failed.\n");
+ return false;
+ }
+ if (!pdfioContentTextMoveTo(stream, item->x, y)) {
+ fprintf(stderr, "pdfioContentTextMoveTo failed.\n");
+ return false;
+ }
+ if (!pdfioContentTextShow(stream, true, item->text)) {
+ fprintf(stderr, "pdfioContentTextShow failed.\n");
+ return false;
+ }
+ if (!pdfioContentTextEnd(stream)) {
+ fprintf(stderr, "pdfioContentTextEnd failed.\n");
+ return false;
+ }
+ if (item->style->underline_style == LS_SINGLE) {
+ out_pdf_draw_line(stream, item, y, width, LL_UNDER);
+ } else if (item->style->underline_style == LS_DOUBLE) {
+ }
+ if (item->style->strikethrough) {
+ out_pdf_draw_line(stream, item, y, width, LL_STRIKETHROUGH);
+ }
return true;
}
-static double text_width(struct TextLineItem *item)
-{
- char *name = out_pdf_fnt_name_create(item->style->font);
- pdfio_obj_t *font_obj = out_pdf_fnt_obj_get_by_name(name);
- free(name);
- return pdfioContentTextMeasure(font_obj, item->text, item->style->font->size);
-}
-
static double line_width_until_chord(struct ChoLine *line, struct ChoChord *chord, struct SpaceNeeded *space)
{
int i = -1;
@@ -538,6 +685,10 @@ static struct Text **text_create(struct ChoSong **songs, struct Config *config)
text[t]->lines[tl]->items[0]->text = strdup(songs[so]->metadata[m]->value);
text[t]->lines[tl]->items[0]->style = cho_style_duplicate(printable_item->style);
width = text_width(text[t]->lines[tl]->items[0]);
+ if (width == EMPTY) {
+ fprintf(stderr, "text_width failed.\n");
+ return NULL;
+ }
text[t]->lines[tl]->items[0]->x = PADDING + (LINE_LEN - width) / 2;
text[t]->lines[tl]->items[1] = NULL;
text_line_set_lineheight(text[t]->lines[tl], SF_TEXT);
@@ -558,6 +709,10 @@ static struct Text **text_create(struct ChoSong **songs, struct Config *config)
text[t]->lines[tl]->items[0]->text = strdup(songs[so]->metadata[m]->value);
text[t]->lines[tl]->items[0]->style = cho_style_duplicate(printable_item->style);
width = text_width(text[t]->lines[tl]->items[0]);
+ if (width == EMPTY) {
+ fprintf(stderr, "text_width failed.\n");
+ return NULL;
+ }
text[t]->lines[tl]->items[0]->x = PADDING + (LINE_LEN - width) / 2;
text[t]->lines[tl]->items[1] = NULL;
text_line_set_lineheight(text[t]->lines[tl], SF_TEXT);
@@ -660,6 +815,10 @@ static struct Text **text_create(struct ChoSong **songs, struct Config *config)
text[t]->lines[tl]->items[tli]->x = PADDING;
} else {
last_text_line_item_width = text_width(text[t]->lines[tl]->items[tli-1]);
+ if (last_text_line_item_width == EMPTY) {
+ fprintf(stderr, "text_width failed.\n");
+ return NULL;
+ }
text[t]->lines[tl]->items[tli]->x = text[t]->lines[tl]->items[tli-1]->x + last_text_line_item_width;
}
int tlii = 0;
@@ -677,6 +836,10 @@ static struct Text **text_create(struct ChoSong **songs, struct Config *config)
text[t]->lines[tl]->items[tli]->text = NULL;
text[t]->lines[tl]->items[tli]->style = cho_style_duplicate(lines[li]->lyrics[ly]->style);
last_text_line_item_width = text_width(text[t]->lines[tl]->items[tli-1]);
+ if (last_text_line_item_width == EMPTY) {
+ fprintf(stderr, "text_width failed.\n");
+ return NULL;
+ }
text[t]->lines[tl]->items[tli]->x = text[t]->lines[tl]->items[tli-1]->x + last_text_line_item_width + sp->amount;
} else {
text[t]->lines[tl]->items[tli]->text = realloc(text[t]->lines[tl]->items[tli]->text, (tlii+1) * sizeof(char));
@@ -719,10 +882,7 @@ static void text_free(struct Text **text)
if (text[t]->lines[tl]->items) {
for (tli = 0; text[t]->lines[tl]->items[tli]; tli++) {
free(text[t]->lines[tl]->items[tli]->text);
- // NULL check because chord style is not yet implemented
- if (text[t]->lines[tl]->items[tli]->style) {
- cho_style_free(text[t]->lines[tl]->items[tli]->style);
- }
+ cho_style_free(text[t]->lines[tl]->items[tli]->style);
free(text[t]->lines[tl]->items[tli]);
}
free(text[t]->lines[tl]->items);
@@ -739,24 +899,28 @@ bool out_pdf_set_title(pdfio_file_t *pdf, struct ChoSong **songs)
{
// Set pdf title only if single song exist
if (songs[0] && !songs[1]) {
- int m;
- for (m = 0; songs[0]->metadata[m]; m++) {
- if (strcmp(songs[0]->metadata[m]->name, "title") == 0) {
- pdfioFileSetTitle(pdf, songs[0]->metadata[m]->value);
- return true;
- }
+ const char *title;
+ title = cho_metadata_get(songs[0]->metadata, "title");
+ if (title) {
+ pdfioFileSetTitle(pdf, title);
+ return true;
}
return false;
}
return true;
}
-bool out_pdf_new(const char *cho_filename, struct ChoSong **songs, struct Config *config)
+bool out_pdf_new(const char *cho_filepath, const char *output_folder_or_file, struct ChoSong **songs, struct Config *config)
{
memset(&g_current_font_name, 0, sizeof(g_current_font_name));
- char *pdf_filename = out_pdf_filename_create(cho_filename);
+ char *pdf_filename = out_pdf_filename_create(songs, cho_filepath, output_folder_or_file);
+ if (!pdf_filename) {
+ fprintf(stderr, "out_pdf_filename_create failed.\n");
+ return false;
+ }
pdfio_rect_t media_box_a4 = { 0.0, 0.0, MEDIABOX_WIDTH, MEDIABOX_HEIGHT };
pdfio_rect_t crop_box = { 36.0, 36.0, MEDIABOX_WIDTH, MEDIABOX_HEIGHT };
+ fprintf(stderr, "INFO: Writing pdf to file: '%s'\n", pdf_filename);
pdfio_file_t *pdf = pdfioFileCreate(pdf_filename, "2.0", &media_box_a4, &crop_box, NULL, NULL);
free(pdf_filename);
if (!out_pdf_set_title(pdf, songs)) {
@@ -816,16 +980,16 @@ bool out_pdf_new(const char *cho_filename, struct ChoSong **songs, struct Config
fprintf(stderr, "pdfioContentSetFillColorSpace failed.\n");
return 1;
}
+ if (!pdfioContentSetStrokeColorSpace(page1_stream, "rgbcolorspace")) {
+ fprintf(stderr, "pdfioContentSetStrokeColorSpace failed.\n");
+ return 1;
+ }
double y = MEDIABOX_HEIGHT - 25.0;
int t, tl, tli;
for (t = 0; text[t]; t++) {
for (tl = 0; text[t]->lines[tl]; tl++) {
if (text[t]->lines[tl]->items) {
for (tli = 0; text[t]->lines[tl]->items[tli]; tli++) {
- // NULL check because chord style is not yet implemented
- if (text[t]->lines[tl]->items[tli]->style) {
- out_pdf_style_apply(page1_stream, text[t]->lines[tl]->items[tli]->style);
- }
out_pdf_text_show(page1_stream, text[t]->lines[tl]->items[tli], y);
}
}
diff --git a/out_pdf.h b/out_pdf.h
@@ -38,4 +38,17 @@ struct Text {
struct TextLine **lines;
};
-bool out_pdf_new(const char *cho_filename, struct ChoSong **songs, struct Config *config);
+enum FileType {
+ F_ERROR,
+ F_FOLDER,
+ F_REG_FILE,
+ F_OTHER
+};
+
+enum LineLocation {
+ LL_OVER,
+ LL_STRIKETHROUGH,
+ LL_UNDER
+};
+
+bool out_pdf_new(const char *cho_filename, const char *output_folder_or_file, struct ChoSong **songs, struct Config *config);
diff --git a/str.c b/str.c
@@ -0,0 +1,98 @@
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include "str.h"
+
+/* char *str_normalize(const char *str)
+{
+ char *normalized = NULL;
+ int i = 0;
+ int s = 0;
+ char c;
+ for (; str[i] != 0; i++) {
+ if (str[i] == ' ')
+ continue;
+ c = (char)tolower(str[i]);
+ normalized = realloc(normalized, (s+1) * sizeof(char));
+ normalized[s] = c;
+ s++;
+ }
+ normalized = realloc(normalized, (s+1) * sizeof(char));
+ normalized[s] = 0;
+ return normalized;
+} */
+
+char *str_normalize(const char *str)
+{
+ char *normalized = NULL;
+ int i = 0;
+ int n = 0;
+ char c;
+ for (; str[i] != 0; i++) {
+ if (str[i] == ' ') {
+ normalized = realloc(normalized, (n+1) * sizeof(char));
+ normalized[n] = '-';
+ n++;
+ continue;
+ }
+ if (str[i] == '\'' || str[i] == ',')
+ continue;
+ c = (char)tolower(str[i]);
+ normalized = realloc(normalized, (n+1) * sizeof(char));
+ normalized[n] = c;
+ n++;
+ }
+ normalized = realloc(normalized, (n+1) * sizeof(char));
+ normalized[n] = 0;
+ return normalized;
+}
+
+char *str_trim(const char *str)
+{
+ char *trimmed = NULL;
+ int begin = 0;
+ int end = 0;
+ int len = (int)strlen(str);
+ for (int i=0; i<len; i++) {
+ if (
+ str[i] == ' ' ||
+ str[i] == '\n' ||
+ str[i] == '\t' ||
+ str[i] == '\r'
+ )
+ begin++;
+ else
+ break;
+ }
+ for (int i=len-1; i>=0; i--) {
+ if (
+ str[i] == ' '||
+ str[i] == '\n' ||
+ str[i] == '\t' ||
+ str[i] == '\r'
+ )
+ end++;
+ else
+ break;
+ }
+ int k = 0;
+ for (int i=0; i<len; i++) {
+ if (i >= begin && i < len - end) {
+ trimmed = realloc(trimmed, (k+1) * sizeof(char));
+ trimmed[k] = str[i];
+ k++;
+ }
+ }
+ trimmed = realloc(trimmed, (k+1) * sizeof(char));
+ trimmed[k] = 0;
+ return trimmed;
+}
+
+char *str_remove_leading_whitespace(const char *str)
+{
+ int i = 0;
+ while (str[i] == ' ' || str[i] == '\t') {
+ i++;
+ }
+ return strdup(&str[i]);
+}
diff --git a/str.h b/str.h
@@ -0,0 +1,3 @@
+char *str_normalize(const char *str);
+char *str_trim(const char *str);
+char *str_remove_leading_whitespace(const char *str);
diff --git a/todo b/todo
@@ -1,4 +1,4 @@
-apply config in cho_parse() instead of out_pdf_new()
+apply all config in cho_parse() instead of out_pdf_new()
'chorus' directive
decide how to implement
metadata directives
@@ -16,3 +16,4 @@ introduce parse errors
and continue execution and when it should fail
metadata items need a style
font, size, colour directives
+new_song directive