From 5c1cbaf533c3d96064212fac43ace049f089a18f Mon Sep 17 00:00:00 2001 From: Ievgenii Meshcheriakov Date: Thu, 30 Sep 2021 19:18:28 +0200 Subject: [PATCH 1/9] Convert code to C++ Mostly minimal conversion to C++17. --- CMakeLists.txt | 6 +- src/CMakeLists.txt | 24 ++--- src/{fntsample.c => fntsample.cpp} | 99 ++++++++++--------- ...nicode_blocks.c => gen_unicode_blocks.cpp} | 15 +-- src/{read_blocks.c => read_blocks.cpp} | 30 +++--- src/static_unicode_blocks.h | 2 +- src/unicode_blocks.h | 4 +- 7 files changed, 90 insertions(+), 90 deletions(-) rename src/{fntsample.c => fntsample.cpp} (93%) rename src/{gen_unicode_blocks.c => gen_unicode_blocks.cpp} (74%) rename src/{read_blocks.c => read_blocks.cpp} (64%) diff --git a/CMakeLists.txt b/CMakeLists.txt index cef7ee5..5d7bf57 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,10 +4,10 @@ project(fntsample VERSION 5.4 DESCRIPTION "PDF and PostScript font samples generator" HOMEPAGE_URL "https://github.com/eugmes/fntsample" - LANGUAGES C) + LANGUAGES CXX) -set(CMAKE_C_STANDARD 99 CACHE STRING "The C standard to use") -set(CMAKE_C_STANDARD_REQUIRED ON) +set(CMAKE_CXX_STANDARD 17 CACHE STRING "The C++ standard to use") +set(CMAKE_CXX_STANDARD_REQUIRED ON) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake") diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 921db45..a2bc172 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,35 +2,36 @@ configure_file(config.h.in config.h ESCAPE_QUOTES @ONLY) configure_file(fntsample.1.in fntsample.1 @ONLY) set( + # FIXME: -Wformat-security does not work well with gettext on MacOS C_WARNING_FLAGS -Wcast-align -Werror-implicit-function-declaration -Wchar-subscripts - -Wall -W -Wpointer-arith -Wwrite-strings -Wformat-security + -Wall -W -Wpointer-arith -Wwrite-strings -Wno-format-security -Wmissing-format-attribute -fno-common -Wundef CACHE STRING "Warning flags for C compiler" ) add_executable(gen-unicode-blocks EXCLUDE_FROM_ALL - gen_unicode_blocks.c - read_blocks.c + gen_unicode_blocks.cpp + read_blocks.cpp ) target_compile_options(gen-unicode-blocks PRIVATE ${C_WARNING_FLAGS}) add_custom_command( - OUTPUT static_unicode_blocks.c - COMMAND gen-unicode-blocks "${UNICODE_BLOCKS}" static_unicode_blocks.c + OUTPUT static_unicode_blocks.cpp + COMMAND gen-unicode-blocks "${UNICODE_BLOCKS}" static_unicode_blocks.cpp MAIN_DEPENDENCY "${UNICODE_BLOCKS}" DEPENDS gen-unicode-blocks VERBATIM ) add_executable(fntsample - fntsample.c - read_blocks.c - ${CMAKE_CURRENT_BINARY_DIR}/static_unicode_blocks.c + fntsample.cpp + read_blocks.cpp + ${CMAKE_CURRENT_BINARY_DIR}/static_unicode_blocks.cpp ) -add_translatable_sources(fntsample.c read_blocks.c) +add_translatable_sources(fntsample.cpp read_blocks.cpp) target_include_directories(fntsample PRIVATE ${CMAKE_CURRENT_BINARY_DIR} @@ -39,11 +40,6 @@ target_include_directories(fntsample PRIVATE target_link_libraries(fntsample PRIVATE Intl::Intl PkgConfig::pkgs) -find_library(MATH_LIBRARY m) -if(MATH_LIBRARY) - target_link_libraries(fntsample PRIVATE ${MATH_LIBRARY}) -endif() - target_compile_options(fntsample PRIVATE ${C_WARNING_FLAGS}) # TODO use improved install handling in CMake 3.14 diff --git a/src/fntsample.c b/src/fntsample.cpp similarity index 93% rename from src/fntsample.c rename to src/fntsample.cpp index c76c7bd..600176d 100644 --- a/src/fntsample.c +++ b/src/fntsample.cpp @@ -1,7 +1,7 @@ /* Copyright © Євгеній Мещеряков * SPDX-License-Identifier: GPL-3.0-or-later */ -#include +#include // TODO: freetype 2.10.3, do not include ft2build.h anymore #include #include @@ -10,24 +10,25 @@ #include #include #include -#include -#include -#include +#include +#include +#include #include -#include #include #include -#include +#include #include #include -#include +#include #include -#include +#include #include "unicode_blocks.h" #include "static_unicode_blocks.h" #include "config.h" +using namespace std; + #define _(str) gettext(str) #define POINTS_PER_INCH 72 @@ -44,7 +45,7 @@ static double cell_x(double x_min, int pos) { return x_min + cell_width * (pos / static double cell_y(int pos) { return ymin_border + cell_height * (pos % 16); } -static struct option longopts[] = { +static option longopts[] = { {"blocks-file", 1, 0, 'b'}, {"font-file", 1, 0, 'f'}, {"output-file", 1, 0, 'o'}, @@ -68,7 +69,7 @@ struct range { uint32_t first; uint32_t last; bool include; - struct range *next; + range *next; }; static const char *font_file_name; @@ -79,8 +80,8 @@ static bool svg_output; static bool print_outline; static bool write_outline; static bool no_embed; -static struct range *ranges; -static struct range *last_range; +static range *ranges; +static range *last_range; static int font_index; static int other_index; @@ -90,12 +91,12 @@ struct fntsample_style { char *val; }; -static struct fntsample_style styles[] = { - {"header-font", "Sans Bold 12", NULL}, - {"font-name-font", "Serif Bold 12", NULL}, - {"table-numbers-font", "Sans 10", NULL}, - {"cell-numbers-font", "Mono 8", NULL}, - {NULL, NULL, NULL}, +static fntsample_style styles[] = { + {"header-font", "Sans Bold 12", nullptr}, + {"font-name-font", "Serif Bold 12", nullptr}, + {"table-numbers-font", "Sans 10", nullptr}, + {"cell-numbers-font", "Mono 8", nullptr}, + {nullptr, nullptr, nullptr}, }; struct table_fonts { @@ -105,31 +106,31 @@ struct table_fonts { PangoFontDescription *cell_numbers; }; -static struct table_fonts table_fonts; +static table_fonts table_fonts; static double cell_label_offset; static double cell_glyph_bot_offset; static double glyph_baseline_offset; static double font_scale; -static const struct unicode_block *unicode_blocks; +static const unicode_block *unicode_blocks; static void usage(const char *); -static struct fntsample_style *find_style(const char *name) +static fntsample_style *find_style(const char *name) { - for (struct fntsample_style *style = styles; style->name; style++) { + for (fntsample_style *style = styles; style->name; style++) { if (!strcmp(name, style->name)) { return style; } } - return NULL; + return nullptr; } static int set_style(const char *name, const char *val) { - struct fntsample_style *style = find_style(name); + fntsample_style *style = find_style(name); if (!style) { return -1; @@ -151,10 +152,10 @@ static int set_style(const char *name, const char *val) static const char *get_style(const char *name) { - struct fntsample_style *style = find_style(name); + fntsample_style *style = find_style(name); if (!style) { - return NULL; + return nullptr; } return style->val ? style->val : style->default_val; @@ -176,17 +177,17 @@ static int parse_style_string(char *s) * * Returns -1 on error. */ -static int add_range(char *range, bool include) +static int add_range(char *range_str, bool include) { uint32_t first = 0, last = 0xffffffff; char *endptr; - char *minus = strchr(range, '-'); + char *minus = strchr(range_str, '-'); if (minus) { - if (minus != range) { + if (minus != range_str) { *minus = '\0'; - first = strtoul(range, &endptr, 0); + first = strtoul(range_str, &endptr, 0); if (*endptr) { return -1; } @@ -197,11 +198,11 @@ static int add_range(char *range, bool include) if (*endptr) { return -1; } - } else if (minus == range) { + } else if (minus == range_str) { return -1; } } else { - first = strtoul(range, &endptr, 0); + first = strtoul(range_str, &endptr, 0); if (*endptr) return -1; last = first; @@ -211,7 +212,7 @@ static int add_range(char *range, bool include) return -1; } - struct range *r = malloc(sizeof(*r)); + range *r = static_cast(malloc(sizeof(*r))); if (!r) { return -1; } @@ -219,7 +220,7 @@ static int add_range(char *range, bool include) r->first = first; r->last = last; r->include = include; - r->next = NULL; + r->next = nullptr; if (ranges) { last_range->next = r; @@ -240,7 +241,7 @@ static bool in_range(uint32_t c) { bool in = ranges ? (!ranges->include) : 1; - for (struct range *r = ranges; r; r = r->next) { + for (range *r = ranges; r; r = r->next) { if ((c >= r->first) && (c <= r->last)) { in = r->include; } @@ -295,7 +296,7 @@ static PangoLayout *layout_text(cairo_t *cr, PangoFontDescription *ftdesc, const PangoLayout *layout = pango_cairo_create_layout(cr); pango_layout_set_font_description(layout, ftdesc); pango_layout_set_text(layout, text, -1); - pango_layout_get_extents(layout, r, NULL); + pango_layout_get_extents(layout, r, nullptr); return layout; } @@ -304,7 +305,7 @@ static void parse_options(int argc, char *const argv[]) { for (;;) { int n; - int c = getopt_long(argc, argv, "b:f:o:hd:sglwi:x:t:n:m:ep", longopts, NULL); + int c = getopt_long(argc, argv, "b:f:o:hd:sglwi:x:t:n:m:ep", longopts, nullptr); if (c == -1) { break; @@ -415,23 +416,23 @@ static void parse_options(int argc, char *const argv[]) /* * Locate unicode block that contains given character code. - * Returns this block or NULL if not found. + * Returns this block or nullptr if not found. */ -static const struct unicode_block *get_unicode_block(unsigned long charcode) +static const unicode_block *get_unicode_block(unsigned long charcode) { - for (const struct unicode_block *block = unicode_blocks; block->name; block++) { + for (const unicode_block *block = unicode_blocks; block->name; block++) { if ((charcode >= block->start) && (charcode <= block->end)) { return block; } } - return NULL; + return nullptr; } /* * Check if the given character code belongs to the given Unicode block. */ -static bool is_in_block(unsigned long charcode, const struct unicode_block *block) +static bool is_in_block(unsigned long charcode, const unicode_block *block) { return ((charcode >= block->start) && (charcode <= block->end)); } @@ -447,7 +448,7 @@ static void outline(cairo_surface_t *surface, int level, int page, const char *t if (write_outline && cairo_surface_get_type(surface) == CAIRO_SURFACE_TYPE_PDF) { int len = snprintf(0, 0, "page=%d", page); - char *dest = malloc(len + 1); + char *dest = static_cast(malloc(len + 1)); sprintf(dest, "page=%d", page); /* FIXME passing level here is not correct. */ @@ -598,7 +599,7 @@ static void draw_charcode(cairo_t *cr, double x, double y, FT_ULong charcode) */ static int draw_unicode_block(cairo_t *cr, PangoLayout *layout, FT_Face ft_face, const char *font_name, unsigned long charcode, - const struct unicode_block *block, FT_Face ft_other_face) + const unicode_block *block, FT_Face ft_other_face) { int npages = 0; FT_UInt idx = FT_Get_Char_Index(ft_face, charcode); @@ -710,7 +711,7 @@ static void draw_glyphs(cairo_t *cr, FT_Face ft_face, FT_Face ft_other_face) FcPattern *fc_pat = FcPatternCreate(); FcPatternAddInteger(fc_pat, FC_INDEX, font_index); - FcFontSet *fc_fontset = FcFontList(fc_config, fc_pat, NULL); + FcFontSet *fc_fontset = FcFontList(fc_config, fc_pat, nullptr); assert(fc_fontset->nfont > 0); FcPattern *fc_font = fc_fontset->fonts[0]; @@ -730,7 +731,7 @@ static void draw_glyphs(cairo_t *cr, FT_Face ft_face, FT_Face ft_other_face) FT_ULong charcode = get_first_char(ft_face, &idx); while (idx) { - const struct unicode_block *block = get_unicode_block(charcode); + const unicode_block *block = get_unicode_block(charcode); if (block) { outline(surface, 1, pageno, block->name); int npages = draw_unicode_block(cr, layout, ft_face, font_name, charcode, block, @@ -782,7 +783,7 @@ static void usage(const char *cmd) fprintf(stderr, _("\nSupported styles (and default values):\n")); - for (const struct fntsample_style *style = styles; style->name; style++) { + for (const fntsample_style *style = styles; style->name; style++) { fprintf(stderr, "\t%s (%s)\n", style->name, style->default_val); } } @@ -886,7 +887,7 @@ static void set_repeatable_pdf_metadata(cairo_surface_t *surface) fprintf(stderr, _("Failed to parse environment variable SOURCE_DATE_EPOCH.\n")); exit(1); } - struct tm *build_time = gmtime(&now); + tm *build_time = gmtime(&now); char buffer[25]; strftime(buffer, sizeof(buffer), "%Y-%m-%dT%H:%M:%SZ", build_time); @@ -919,7 +920,7 @@ int main(int argc, char **argv) exit(4); } - FT_Face other_face = NULL; + FT_Face other_face = nullptr; if (other_font_file_name) { error = FT_New_Face(library, other_font_file_name, other_index, &other_face); diff --git a/src/gen_unicode_blocks.c b/src/gen_unicode_blocks.cpp similarity index 74% rename from src/gen_unicode_blocks.c rename to src/gen_unicode_blocks.cpp index 6e24fd4..9fd45bc 100644 --- a/src/gen_unicode_blocks.c +++ b/src/gen_unicode_blocks.cpp @@ -2,29 +2,32 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ #include "unicode_blocks.h" -#include +#include +#include + +using namespace std; static void write_header(FILE *f) { fprintf(f, "#include \"static_unicode_blocks.h\"\n" "\n" - "const struct unicode_block static_unicode_blocks[] = {\n"); + "const unicode_block static_unicode_blocks[] = {\n"); } static void write_footer(FILE *f) { fprintf(f, - " {0, 0, NULL},\n" + " {0, 0, nullptr},\n" "};\n"); } -static void write_block(FILE *f, const struct unicode_block *block) +static void write_block(FILE *f, const unicode_block *block) { fprintf(f, " {0x%04lx, 0x%04lx, \"%s\"},\n", block->start, block->end, block->name); } -static void write_blocks(FILE *f, const struct unicode_block *blocks, int n) +static void write_blocks(FILE *f, const unicode_block *blocks, int n) { write_header(f); for (int i = 0; i < n; i++) { @@ -41,7 +44,7 @@ int main(int argc, char **argv) } int n; - struct unicode_block *blocks = read_blocks(argv[1], &n); + unicode_block *blocks = read_blocks(argv[1], &n); if (!blocks) { fprintf(stderr, "Failed to read unicode blocks file.\n"); diff --git a/src/read_blocks.c b/src/read_blocks.cpp similarity index 64% rename from src/read_blocks.c rename to src/read_blocks.cpp index fd5979a..e47bc31 100644 --- a/src/read_blocks.c +++ b/src/read_blocks.cpp @@ -2,14 +2,16 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ #include "unicode_blocks.h" -#include -#include -#include +#include +#include +#include -struct unicode_block *read_blocks(const char *file_name, int *n) +using namespace std; + +unicode_block *read_blocks(const char *file_name, int *n) { int nalloc = 256; - struct unicode_block *blocks = calloc(nalloc, sizeof(struct unicode_block)); + unicode_block *blocks = static_cast(calloc(nalloc, sizeof(unicode_block))); *n = 0; FILE *input_file = fopen(file_name, "r"); @@ -18,7 +20,7 @@ struct unicode_block *read_blocks(const char *file_name, int *n) exit(7); } - char *line = NULL; + char *line = nullptr; size_t len = 0; ssize_t nread; @@ -31,11 +33,11 @@ struct unicode_block *read_blocks(const char *file_name, int *n) int matched = sscanf(line, "%lx..%lx; %[^\r\n]", &block_start, &block_end, block_name); if (matched == 3) { - struct unicode_block *b = blocks + *n; + unicode_block *b = blocks + *n; b->start = block_start; b->end = block_end; b->name = strdup(block_name); - if (b->name == NULL) { + if (b->name == nullptr) { perror("strdup"); exit(8); } @@ -43,14 +45,14 @@ struct unicode_block *read_blocks(const char *file_name, int *n) *n += 1; if (*n >= nalloc) { int new_nalloc = nalloc + 256; - struct unicode_block *new_blocks - = realloc(blocks, new_nalloc * sizeof(struct unicode_block)); - if (new_blocks == NULL) { + unicode_block *new_blocks + = static_cast(realloc(blocks, new_nalloc * sizeof(unicode_block))); + if (new_blocks == nullptr) { perror("realloc"); exit(9); } memset(new_blocks + nalloc, 0, - (new_nalloc - nalloc) * sizeof(struct unicode_block)); + (new_nalloc - nalloc) * sizeof(unicode_block)); nalloc = new_nalloc; blocks = new_blocks; } @@ -60,9 +62,9 @@ struct unicode_block *read_blocks(const char *file_name, int *n) if (*n == 0) { free(blocks); - return NULL; + return nullptr; } else if (*n < nalloc) { - blocks = realloc(blocks, *n * sizeof(struct unicode_block)); + blocks = static_cast(realloc(blocks, *n * sizeof(unicode_block))); } return blocks; diff --git a/src/static_unicode_blocks.h b/src/static_unicode_blocks.h index 594e52d..9369cd6 100644 --- a/src/static_unicode_blocks.h +++ b/src/static_unicode_blocks.h @@ -6,6 +6,6 @@ #define STATIC_UNICODE_BLOCKS_H #include "unicode_blocks.h" -extern const struct unicode_block static_unicode_blocks[]; +extern const unicode_block static_unicode_blocks[]; #endif diff --git a/src/unicode_blocks.h b/src/unicode_blocks.h index f07924d..374d926 100644 --- a/src/unicode_blocks.h +++ b/src/unicode_blocks.h @@ -5,14 +5,12 @@ #ifndef UNICODE_BLOCKS_H #define UNICODE_BLOCKS_H -#include - struct unicode_block { unsigned long start; unsigned long end; const char *name; }; -struct unicode_block *read_blocks(const char *file_name, int *n); +unicode_block *read_blocks(const char *file_name, int *n); #endif From c1354c2d994861995ad84d8065ee5547a68ba231 Mon Sep 17 00:00:00 2001 From: Ievgenii Meshcheriakov Date: Thu, 30 Sep 2021 21:20:41 +0200 Subject: [PATCH 2/9] Use C++ stream for all formatted output Handle formatting using fmt library. --- .github/workflows/ci.yml | 4 +-- CMakeLists.txt | 1 + src/CMakeLists.txt | 7 ++-- src/fntsample.cpp | 70 ++++++++++++++++++-------------------- src/gen_unicode_blocks.cpp | 55 ++++++++++++++++-------------- 5 files changed, 70 insertions(+), 67 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3ce9a44..e2a5068 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,13 +21,13 @@ jobs: blocks: -DUNICODE_BLOCKS=/usr/share/unicode/Blocks.txt install_packages: > sudo apt update && - sudo apt install cmake gettext libcairo2-dev libglib2.0-dev libfreetype6-dev libpango1.0-dev ninja-build pkg-config unicode-data + sudo apt install cmake gettext libcairo2-dev libglib2.0-dev libfmt-dev libfreetype6-dev libpango1.0-dev ninja-build pkg-config unicode-data - os: macos-latest env: - CMAKE_PREFIX_PATH: /usr/local/opt/gettext install_packages: > brew update && - brew install cairo cmake fontconfig freetype gettext glib pango ninja pkg-config + brew install cairo cmake fmt fontconfig freetype gettext glib pango ninja pkg-config runs-on: ${{matrix.os}} diff --git a/CMakeLists.txt b/CMakeLists.txt index 5d7bf57..5d97629 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,7 @@ include(CPack) find_package(PkgConfig REQUIRED) find_package(Intl REQUIRED) +find_package(fmt REQUIRED) # The target was added in CMake 3.20. if(NOT TARGET Intl::Intl) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a2bc172..9bc8081 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,10 +2,9 @@ configure_file(config.h.in config.h ESCAPE_QUOTES @ONLY) configure_file(fntsample.1.in fntsample.1 @ONLY) set( - # FIXME: -Wformat-security does not work well with gettext on MacOS C_WARNING_FLAGS -Wcast-align -Werror-implicit-function-declaration -Wchar-subscripts - -Wall -W -Wpointer-arith -Wwrite-strings -Wno-format-security + -Wall -W -Wpointer-arith -Wwrite-strings -Wformat-security -Wmissing-format-attribute -fno-common -Wundef CACHE STRING "Warning flags for C compiler" ) @@ -15,6 +14,8 @@ add_executable(gen-unicode-blocks EXCLUDE_FROM_ALL read_blocks.cpp ) +target_link_libraries(gen-unicode-blocks PRIVATE fmt::fmt) + target_compile_options(gen-unicode-blocks PRIVATE ${C_WARNING_FLAGS}) add_custom_command( @@ -38,7 +39,7 @@ target_include_directories(fntsample PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ) -target_link_libraries(fntsample PRIVATE Intl::Intl PkgConfig::pkgs) +target_link_libraries(fntsample PRIVATE Intl::Intl PkgConfig::pkgs fmt::fmt) target_compile_options(fntsample PRIVATE ${C_WARNING_FLAGS}) diff --git a/src/fntsample.cpp b/src/fntsample.cpp index 600176d..70cbeaa 100644 --- a/src/fntsample.cpp +++ b/src/fntsample.cpp @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -22,6 +21,8 @@ #include #include #include +#include +#include #include "unicode_blocks.h" #include "static_unicode_blocks.h" @@ -314,26 +315,26 @@ static void parse_options(int argc, char *const argv[]) switch (c) { case 'b': if (unicode_blocks) { - fprintf(stderr, _("Unicode blocks file should be given at most once!\n")); + cerr << _("Unicode blocks file should be given at most once!\n"); exit(1); } unicode_blocks = read_blocks(optarg, &n); if (n == 0) { - fprintf(stderr, _("Failed to load any blocks from the blocks file!\n")); + cerr << _("Failed to load any blocks from the blocks file!\n"); exit(6); } break; case 'f': if (font_file_name) { - fprintf(stderr, _("Font file name should be given only once!\n")); + cerr << _("Font file name should be given only once!\n"); exit(1); } font_file_name = optarg; break; case 'o': if (output_file_name) { - fprintf(stderr, _("Output file name should be given only once!\n")); + cerr << _("Output file name should be given only once!\n"); exit(1); } output_file_name = optarg; @@ -344,7 +345,7 @@ static void parse_options(int argc, char *const argv[]) break; case 'd': if (other_font_file_name) { - fprintf(stderr, _("Font file name should be given only once!\n")); + cerr << _("Font file name should be given only once!\n"); exit(1); } other_font_file_name = optarg; @@ -400,12 +401,12 @@ static void parse_options(int argc, char *const argv[]) } if (font_index < 0 || other_index < 0) { - fprintf(stderr, _("Font index should be non-negative!\n")); + cerr << _("Font index should be non-negative!\n"); exit(1); } if (postscript_output && svg_output) { - fprintf(stderr, _("-s and -g cannot be used together!\n")); + cerr << _("-s and -g cannot be used together!\n"); exit(1); } @@ -443,17 +444,13 @@ static bool is_in_block(unsigned long charcode, const unicode_block *block) static void outline(cairo_surface_t *surface, int level, int page, const char *text) { if (print_outline) { - printf("%d %d %s\n", level, page, text); + fmt::print("{} {} {}\n", level, page, text); } if (write_outline && cairo_surface_get_type(surface) == CAIRO_SURFACE_TYPE_PDF) { - int len = snprintf(0, 0, "page=%d", page); - char *dest = static_cast(malloc(len + 1)); - sprintf(dest, "page=%d", page); - + auto s = fmt::format("page={}", page); /* FIXME passing level here is not correct. */ - cairo_pdf_surface_add_outline(surface, level, text, dest, CAIRO_PDF_OUTLINE_FLAG_OPEN); - free(dest); + cairo_pdf_surface_add_outline(surface, level, text, s.c_str(), CAIRO_PDF_OUTLINE_FLAG_OPEN); } } @@ -543,10 +540,10 @@ static void draw_grid(cairo_t *cr, unsigned int x_cells, unsigned long block_sta } for (unsigned int i = 0; i < x_cells; i++) { - snprintf(buf, sizeof(buf), "%03lX", block_start / 16 + i); + auto s = fmt::format("{:03X}", block_start / 16 + i); PangoRectangle r; - PangoLayout *layout = layout_text(cr, table_fonts.table_numbers, buf, &r); + PangoLayout *layout = layout_text(cr, table_fonts.table_numbers, s.c_str(), &r); cairo_move_to(cr, x_min + i * cell_width + (cell_width - pango_units_to_double(r.width)) / 2, ymin_border - 5.0); @@ -577,11 +574,10 @@ static void fill_empty_cell(cairo_t *cr, double x, double y, unsigned long charc */ static void draw_charcode(cairo_t *cr, double x, double y, FT_ULong charcode) { - char buf[9]; - snprintf(buf, sizeof(buf), "%04lX", charcode); + auto s = fmt::format("{:04X}", charcode); PangoRectangle r; - PangoLayout *layout = layout_text(cr, table_fonts.cell_numbers, buf, &r); + PangoLayout *layout = layout_text(cr, table_fonts.cell_numbers, s.c_str(), &r); cairo_move_to(cr, x + (cell_width - pango_units_to_double(r.width)) / 2.0, y + cell_height - cell_label_offset); pango_cairo_show_layout_line(cr, pango_layout_get_line_readonly(layout, 0)); @@ -755,12 +751,11 @@ static void draw_glyphs(cairo_t *cr, FT_Face ft_face, FT_Face ft_other_face) */ static void usage(const char *cmd) { - fprintf(stderr, - _("Usage: %s [ OPTIONS ] -f FONT-FILE -o OUTPUT-FILE\n" - " %s -h\n\n"), - cmd, cmd); - fprintf( - stderr, + fmt::print(cerr, + _("Usage: {0} [ OPTIONS ] -f FONT-FILE -o OUTPUT-FILE\n" + " {0} -h\n\n"), + cmd); + cerr << _("Options:\n" " --blocks-file, -b BLOCKS-FILE Read Unicode blocks information from " "BLOCKS-FILE\n" @@ -779,12 +774,12 @@ static void usage(const char *cmd) "the glyphs instead\n" " --include-range, -i RANGE Show characters in RANGE\n" " --exclude-range, -x RANGE Do not show characters in RANGE\n" - " --style, -t \"STYLE: VAL\" Set STYLE to value VAL\n")); + " --style, -t \"STYLE: VAL\" Set STYLE to value VAL\n"); - fprintf(stderr, _("\nSupported styles (and default values):\n")); + cerr << _("\nSupported styles (and default values):\n"); for (const fntsample_style *style = styles; style->name; style++) { - fprintf(stderr, "\t%s (%s)\n", style->name, style->default_val); + fmt::print(cerr, "\t{} ({})\n", style->name, style->default_val); } } @@ -844,13 +839,13 @@ void calc_font_scaling(FT_Face ft_face) /* Use some magic to find the best font size... */ double tgt_size = cell_height - cell_glyph_bot_offset - 2; if (tgt_size <= 0) { - fprintf(stderr, _("Not enough space for rendering glyphs. Make cell font smaller.\n")); + cerr << _("Not enough space for rendering glyphs. Make cell font smaller.\n"); exit(5); } double act_size = extents.ascent + extents.descent; if (act_size <= 0) { - fprintf(stderr, _("The font has strange metrics: ascent + descent = %g\n"), act_size); + fmt::print(cerr, _("The font has strange metrics: ascent + descent = {}\n"), act_size); exit(5); } @@ -884,11 +879,12 @@ static void set_repeatable_pdf_metadata(cairo_surface_t *surface) time_t now = strtoul(source_date_epoch, &endptr, 10); if (*endptr != 0) { - fprintf(stderr, _("Failed to parse environment variable SOURCE_DATE_EPOCH.\n")); + cerr << _("Failed to parse environment variable SOURCE_DATE_EPOCH.\n"); exit(1); } tm *build_time = gmtime(&now); char buffer[25]; + // TODO strftime(buffer, sizeof(buffer), "%Y-%m-%dT%H:%M:%SZ", build_time); cairo_pdf_surface_set_metadata(surface, CAIRO_PDF_METADATA_CREATE_DATE, buffer); @@ -908,7 +904,7 @@ int main(int argc, char **argv) if (error) { /* TRANSLATORS: 'freetype' is a name of a library, and should be left untranslated */ - fprintf(stderr, _("%s: freetype error\n"), argv[0]); + fmt::print(cerr, _("{}: freetype error\n"), argv[0]); exit(3); } @@ -916,7 +912,7 @@ int main(int argc, char **argv) error = FT_New_Face(library, font_file_name, font_index, &face); if (error) { - fprintf(stderr, _("%s: failed to open font file %s\n"), argv[0], font_file_name); + fmt::print(cerr, _("%{}: failed to open font file {}\n"), argv[0], font_file_name); exit(4); } @@ -926,7 +922,7 @@ int main(int argc, char **argv) error = FT_New_Face(library, other_font_file_name, other_index, &other_face); if (error) { - fprintf(stderr, _("%s: failed to create new font face\n"), argv[0]); + fmt::print(cerr, _("%{}: failed to create new font face\n"), argv[0]); exit(4); } } @@ -945,7 +941,7 @@ int main(int argc, char **argv) cairo_status_t cr_status = cairo_surface_status(surface); if (cr_status != CAIRO_STATUS_SUCCESS) { /* TRANSLATORS: 'cairo' is a name of a library, and should be left untranslated */ - fprintf(stderr, _("%s: failed to create cairo surface: %s\n"), argv[0], + fmt::print(cerr, _("{}: failed to create cairo surface: {}\n"), argv[0], cairo_status_to_string(cr_status)); exit(1); } @@ -953,7 +949,7 @@ int main(int argc, char **argv) cairo_t *cr = cairo_create(surface); cr_status = cairo_status(cr); if (cr_status != CAIRO_STATUS_SUCCESS) { - fprintf(stderr, _("%s: cairo_create failed: %s\n"), argv[0], + fmt::print(cerr, _("{}: cairo_create failed: {}\n"), argv[0], cairo_status_to_string(cr_status)); exit(1); } diff --git a/src/gen_unicode_blocks.cpp b/src/gen_unicode_blocks.cpp index 9fd45bc..8bce69c 100644 --- a/src/gen_unicode_blocks.cpp +++ b/src/gen_unicode_blocks.cpp @@ -2,44 +2,47 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ #include "unicode_blocks.h" -#include -#include +#include +#include +#include using namespace std; -static void write_header(FILE *f) +static void write_header(ostream &stream) { - fprintf(f, - "#include \"static_unicode_blocks.h\"\n" - "\n" - "const unicode_block static_unicode_blocks[] = {\n"); + stream << +R"(#include "static_unicode_blocks.h" + +const unicode_block static_unicode_blocks[] = { +)"; } -static void write_footer(FILE *f) +static void write_footer(ostream &stream) { - fprintf(f, - " {0, 0, nullptr},\n" - "};\n"); + stream << +R"( {0, 0, nullptr}, +}; +)"; } -static void write_block(FILE *f, const unicode_block *block) +static void write_block(ostream &stream, const unicode_block &block) { - fprintf(f, " {0x%04lx, 0x%04lx, \"%s\"},\n", block->start, block->end, block->name); + fmt::print(stream, " {{0x{:04x}, 0x{:04x}, \"{}\"}},\n", block.start, block.end, block.name); } -static void write_blocks(FILE *f, const unicode_block *blocks, int n) +static void write_blocks(ostream &stream, const unicode_block *blocks, int n) { - write_header(f); + write_header(stream); for (int i = 0; i < n; i++) { - write_block(f, blocks + i); + write_block(stream, blocks[i]); } - write_footer(f); + write_footer(stream); } int main(int argc, char **argv) { if (argc != 3) { - fprintf(stderr, "Usage: %s Blocks.txt output.c\n", argv[0]); + fmt::print(cerr, "Usage: {} Blocks.txt output.c\n", argv[0]); return 1; } @@ -47,19 +50,21 @@ int main(int argc, char **argv) unicode_block *blocks = read_blocks(argv[1], &n); if (!blocks) { - fprintf(stderr, "Failed to read unicode blocks file.\n"); + cerr << "Failed to read unicode blocks file.\n"; return 2; } - FILE *f = fopen(argv[2], "wb"); - if (!f) { - perror("fopen"); - return 3; + try { + ofstream f(argv[2], ios::out | ios::trunc); + f.exceptions(ios::badbit | ios::failbit); + write_blocks(f, blocks, n); + } catch (const ios_base::failure &e) { + // FIXME: How to get useful error messages? + fmt::print(cerr, "Failed to save code file: {}\n", e.what()); + return 1; } - write_blocks(f, blocks, n); free(blocks); - fclose(f); return 0; } From 7fa42d90d3132840e5eaebc18cbe1fcc4dd47453 Mon Sep 17 00:00:00 2001 From: Ievgenii Meshcheriakov Date: Fri, 1 Oct 2021 21:09:55 +0200 Subject: [PATCH 3/9] Use C++ for handling Unicode blocks and ranges Use std::array for the static copy and standard streams, regex, and containers for loadable version. --- src/CMakeLists.txt | 8 +- src/fntsample.cpp | 278 +++++++++++++++----------------- src/gen_unicode_blocks.cpp | 57 ++++--- src/loadable_unicode_blocks.cpp | 72 +++++++++ src/loadable_unicode_blocks.h | 34 ++++ src/read_blocks.cpp | 71 -------- src/static_unicode_blocks.h | 26 ++- src/unicode_blocks.cpp | 23 +++ src/unicode_blocks.h | 28 ++-- src/unicode_range.h | 16 ++ src/unicode_range_set.h | 38 +++++ src/unicode_utils.h | 27 ++++ 12 files changed, 424 insertions(+), 254 deletions(-) create mode 100644 src/loadable_unicode_blocks.cpp create mode 100644 src/loadable_unicode_blocks.h delete mode 100644 src/read_blocks.cpp create mode 100644 src/unicode_blocks.cpp create mode 100644 src/unicode_range.h create mode 100644 src/unicode_range_set.h create mode 100644 src/unicode_utils.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9bc8081..47da306 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -11,7 +11,8 @@ set( add_executable(gen-unicode-blocks EXCLUDE_FROM_ALL gen_unicode_blocks.cpp - read_blocks.cpp + unicode_blocks.cpp + loadable_unicode_blocks.cpp ) target_link_libraries(gen-unicode-blocks PRIVATE fmt::fmt) @@ -28,11 +29,12 @@ add_custom_command( add_executable(fntsample fntsample.cpp - read_blocks.cpp + unicode_blocks.cpp + loadable_unicode_blocks.cpp ${CMAKE_CURRENT_BINARY_DIR}/static_unicode_blocks.cpp ) -add_translatable_sources(fntsample.cpp read_blocks.cpp) +add_translatable_sources(fntsample.cpp unicode_blocks.cpp loadable_unicode_blocks.cpp) target_include_directories(fntsample PRIVATE ${CMAKE_CURRENT_BINARY_DIR} diff --git a/src/fntsample.cpp b/src/fntsample.cpp index 70cbeaa..25d0591 100644 --- a/src/fntsample.cpp +++ b/src/fntsample.cpp @@ -23,9 +23,16 @@ #include #include #include +#include +#include +#include +#include -#include "unicode_blocks.h" +#include "loadable_unicode_blocks.h" #include "static_unicode_blocks.h" +#include "unicode_range_set.h" +#include "unicode_utils.h" + #include "config.h" using namespace std; @@ -66,13 +73,7 @@ static option longopts[] = { {0, 0, 0, 0}, }; -struct range { - uint32_t first; - uint32_t last; - bool include; - range *next; -}; - +static const char *blocks_file; static const char *font_file_name; static const char *other_font_file_name; static const char *output_file_name; @@ -81,8 +82,7 @@ static bool svg_output; static bool print_outline; static bool write_outline; static bool no_embed; -static range *ranges; -static range *last_range; +static unicode_range_set ranges; static int font_index; static int other_index; @@ -114,8 +114,6 @@ static double cell_glyph_bot_offset; static double glyph_baseline_offset; static double font_scale; -static const unicode_block *unicode_blocks; - static void usage(const char *); static fntsample_style *find_style(const char *name) @@ -174,80 +172,82 @@ static int parse_style_string(char *s) } /* - * Update output range. - * - * Returns -1 on error. + * Parse output range. */ -static int add_range(char *range_str, bool include) +static optional parse_range(string_view s) { - uint32_t first = 0, last = 0xffffffff; - char *endptr; - - char *minus = strchr(range_str, '-'); - - if (minus) { - if (minus != range_str) { - *minus = '\0'; - first = strtoul(range_str, &endptr, 0); - if (*endptr) { - return -1; + auto parse_code = [](const char *first, const char *last) -> optional { + int base = 10; + assert(first < last); + + if (*first == '0') { + base = 8; + first++; + if (first < last && (*first == 'x' || *first == 'X')) { + base = 16; + first++; } } - if (*(minus + 1)) { - last = strtoul(minus + 1, &endptr, 0); - if (*endptr) { - return -1; - } - } else if (minus == range_str) { - return -1; + unsigned long n; + auto [ptr, ec] = from_chars(first, last, n, base); + if (ec == errc()) { + return unicode_utils::to_char32_t(n); } - } else { - first = strtoul(range_str, &endptr, 0); - if (*endptr) - return -1; - last = first; - } + return {}; + }; - if (first > last) { - return -1; - } + unicode_range r {0, unicode_utils::last_codepoint}; - range *r = static_cast(malloc(sizeof(*r))); - if (!r) { - return -1; - } + auto minus = s.find('-'); - r->first = first; - r->last = last; - r->include = include; - r->next = nullptr; + if (minus >= 0) { + // minus found + if (s.size() == 1) { + return {}; + } + + if (minus > 0) { + // not at the beginning + auto res = parse_code(s.data(), s.data() + minus); + if (!res) { + return {}; + } + r.start = *res; + } - if (ranges) { - last_range->next = r; + if (minus < s.size() - 1) { + // not at the end + auto res = parse_code(s.data() + minus + 1, s.data() + s.size()); + if (!res) { + return {}; + } + r.end = *res; + } } else { - ranges = r; + // no minus found - single codepoint + auto res = parse_code(s.data(), s.data() + s.size()); + if (!res) { + return {}; + } + r.start = r.end = *res; } - last_range = r; + if (!r.is_valid()) { + return {}; + } - return 0; + return r; } -/* - * Check if character with the given code belongs - * to output range specified by the user. - */ -static bool in_range(uint32_t c) +bool add_range(string_view s, bool include) { - bool in = ranges ? (!ranges->include) : 1; - - for (range *r = ranges; r; r = r->next) { - if ((c >= r->first) && (c <= r->last)) { - in = r->include; - } + auto res = parse_range(s); + if (!res) { + return false; } - return in; + ranges.add(*res, include); + return true; } /* @@ -263,7 +263,7 @@ static FT_ULong get_next_char(FT_Face face, FT_ULong charcode, FT_UInt *idx) do { rval = FT_Get_Next_Char(face, rval, idx); - } while (*idx && !in_range(rval)); + } while (*idx && !ranges.contains(rval)); return rval; } @@ -279,7 +279,7 @@ static FT_ULong get_first_char(FT_Face face, FT_UInt *idx) { FT_ULong rval = FT_Get_First_Char(face, idx); - if (*idx && !in_range(rval)) { + if (*idx && !ranges.contains(rval)) { rval = get_next_char(face, rval, idx); } @@ -305,7 +305,6 @@ static PangoLayout *layout_text(cairo_t *cr, PangoFontDescription *ftdesc, const static void parse_options(int argc, char *const argv[]) { for (;;) { - int n; int c = getopt_long(argc, argv, "b:f:o:hd:sglwi:x:t:n:m:ep", longopts, nullptr); if (c == -1) { @@ -314,16 +313,11 @@ static void parse_options(int argc, char *const argv[]) switch (c) { case 'b': - if (unicode_blocks) { + if (blocks_file) { cerr << _("Unicode blocks file should be given at most once!\n"); exit(1); } - - unicode_blocks = read_blocks(optarg, &n); - if (n == 0) { - cerr << _("Failed to load any blocks from the blocks file!\n"); - exit(6); - } + blocks_file = optarg; break; case 'f': if (font_file_name) { @@ -364,7 +358,7 @@ static void parse_options(int argc, char *const argv[]) break; case 'i': case 'x': - if (add_range(optarg, c == 'i')) { + if (!add_range(optarg, c == 'i')) { usage(argv[0]); exit(1); } @@ -409,33 +403,6 @@ static void parse_options(int argc, char *const argv[]) cerr << _("-s and -g cannot be used together!\n"); exit(1); } - - if (!unicode_blocks) { - unicode_blocks = static_unicode_blocks; - } -} - -/* - * Locate unicode block that contains given character code. - * Returns this block or nullptr if not found. - */ -static const unicode_block *get_unicode_block(unsigned long charcode) -{ - for (const unicode_block *block = unicode_blocks; block->name; block++) { - if ((charcode >= block->start) && (charcode <= block->end)) { - return block; - } - } - - return nullptr; -} - -/* - * Check if the given character code belongs to the given Unicode block. - */ -static bool is_in_block(unsigned long charcode, const unicode_block *block) -{ - return ((charcode >= block->start) && (charcode <= block->end)); } /* @@ -595,15 +562,16 @@ static void draw_charcode(cairo_t *cr, double x, double y, FT_ULong charcode) */ static int draw_unicode_block(cairo_t *cr, PangoLayout *layout, FT_Face ft_face, const char *font_name, unsigned long charcode, - const unicode_block *block, FT_Face ft_other_face) + const unicode_blocks::block &block, FT_Face ft_other_face) { int npages = 0; FT_UInt idx = FT_Get_Char_Index(ft_face, charcode); do { - unsigned long offset = ((charcode - block->start) / 0x100) * 0x100; - unsigned long tbl_start = block->start + offset; - unsigned long tbl_end = tbl_start + 0xFF > block->end ? block->end + 1 : tbl_start + 0x100; + unsigned long offset = ((charcode - block.r.start) / 0x100) * 0x100; + unsigned long tbl_start = block.r.start + offset; + unsigned long tbl_end + = tbl_start + 0xFF > block.r.end ? block.r.end + 1 : tbl_start + 0x100; unsigned int rows = (tbl_end - tbl_start) / 16; double x_min = (A4_WIDTH - rows * cell_width) / 2; @@ -612,7 +580,7 @@ static int draw_unicode_block(cairo_t *cr, PangoLayout *layout, FT_Face ft_face, int pos = 0; cairo_save(cr); - draw_header(cr, font_name, block->name); + draw_header(cr, font_name, block.name); memset(filled_cells, '\0', sizeof(filled_cells)); @@ -650,7 +618,7 @@ static int draw_unicode_block(cairo_t *cr, PangoLayout *layout, FT_Face ft_face, pos++; charcode = get_next_char(ft_face, charcode, &idx); - } while (idx && (charcode < tbl_end) && is_in_block(charcode, block)); + } while (idx && (charcode < tbl_end) && block.contains(charcode)); /* Fill remaining empty cells */ for (; curr_charcode < tbl_end; curr_charcode++, pos++) { @@ -671,7 +639,7 @@ static int draw_unicode_block(cairo_t *cr, PangoLayout *layout, FT_Face ft_face, npages++; cairo_show_page(cr); cairo_restore(cr); - } while (idx && is_in_block(charcode, block)); + } while (idx && block.contains(charcode)); return npages; } @@ -699,7 +667,8 @@ static PangoLayout *create_glyph_layout(cairo_t *cr, FcConfig *fc_config, FcPatt /* * The main drawing function. */ -static void draw_glyphs(cairo_t *cr, FT_Face ft_face, FT_Face ft_other_face) +static void draw_glyphs(const unicode_blocks &blocks, cairo_t *cr, FT_Face ft_face, + FT_Face ft_other_face) { FcConfig *fc_config = FcConfigCreate(); FcConfigAppFontAddFile(fc_config, (const FcChar8 *)font_file_name); @@ -727,13 +696,14 @@ static void draw_glyphs(cairo_t *cr, FT_Face ft_face, FT_Face ft_other_face) FT_ULong charcode = get_first_char(ft_face, &idx); while (idx) { - const unicode_block *block = get_unicode_block(charcode); - if (block) { - outline(surface, 1, pageno, block->name); + if (auto b = blocks.find_block(charcode); b.has_value()) { + const auto block = *b; + + outline(surface, 1, pageno, block.name); int npages = draw_unicode_block(cr, layout, ft_face, font_name, charcode, block, ft_other_face); pageno += npages; - charcode = block->end; + charcode = block.r.end; } charcode = get_next_char(ft_face, charcode, &idx); @@ -752,29 +722,29 @@ static void draw_glyphs(cairo_t *cr, FT_Face ft_face, FT_Face ft_other_face) static void usage(const char *cmd) { fmt::print(cerr, - _("Usage: {0} [ OPTIONS ] -f FONT-FILE -o OUTPUT-FILE\n" - " {0} -h\n\n"), - cmd); - cerr << - _("Options:\n" - " --blocks-file, -b BLOCKS-FILE Read Unicode blocks information from " - "BLOCKS-FILE\n" - " --font-file, -f FONT-FILE Create samples of FONT-FILE\n" - " --font-index, -n IDX Font index in FONT-FILE\n" - " --output-file, -o OUTPUT-FILE Save samples to OUTPUT-FILE\n" - " --help, -h Show this information message and exit\n" - " --other-font-file, -d OTHER-FONT Compare FONT-FILE with OTHER-FONT and highlight " - "added glyphs\n" - " --other-index, -m IDX Font index in OTHER-FONT\n" - " --postscript-output, -s Use PostScript format for output instead of PDF\n" - " --svg, -g Use SVG format for output\n" - " --print-outline, -l Print document outlines data to standard output\n" - " --write-outline, -w Write document outlines (only in PDF output)\n" - " --no-embed, -e Don't embed the font in the output file, draw " - "the glyphs instead\n" - " --include-range, -i RANGE Show characters in RANGE\n" - " --exclude-range, -x RANGE Do not show characters in RANGE\n" - " --style, -t \"STYLE: VAL\" Set STYLE to value VAL\n"); + _("Usage: {0} [ OPTIONS ] -f FONT-FILE -o OUTPUT-FILE\n" + " {0} -h\n\n"), + cmd); + cerr << _( + "Options:\n" + " --blocks-file, -b BLOCKS-FILE Read Unicode blocks information from " + "BLOCKS-FILE\n" + " --font-file, -f FONT-FILE Create samples of FONT-FILE\n" + " --font-index, -n IDX Font index in FONT-FILE\n" + " --output-file, -o OUTPUT-FILE Save samples to OUTPUT-FILE\n" + " --help, -h Show this information message and exit\n" + " --other-font-file, -d OTHER-FONT Compare FONT-FILE with OTHER-FONT and highlight " + "added glyphs\n" + " --other-index, -m IDX Font index in OTHER-FONT\n" + " --postscript-output, -s Use PostScript format for output instead of PDF\n" + " --svg, -g Use SVG format for output\n" + " --print-outline, -l Print document outlines data to standard output\n" + " --write-outline, -w Write document outlines (only in PDF output)\n" + " --no-embed, -e Don't embed the font in the output file, draw " + "the glyphs instead\n" + " --include-range, -i RANGE Show characters in RANGE\n" + " --exclude-range, -x RANGE Do not show characters in RANGE\n" + " --style, -t \"STYLE: VAL\" Set STYLE to value VAL\n"); cerr << _("\nSupported styles (and default values):\n"); @@ -899,6 +869,26 @@ int main(int argc, char **argv) parse_options(argc, argv); + unique_ptr loadable_blocks; + + if (blocks_file) { + ifstream f(blocks_file, ios::binary); + if (!f) { + fmt::print(cerr, _("{}: cannot open Unicode blocks file.\n"), argv[0]); + exit(6); + } + + // FIXME + loadable_blocks = make_unique(f); + if (loadable_blocks->error_line()) { + fmt::print(cerr, _("{}: parse error in Unicode blocks file at line {}\n"), argv[0], + loadable_blocks->error_line()); + exit(6); + } + } + + const unicode_blocks &blocks = loadable_blocks ? *loadable_blocks : get_static_blocks(); + FT_Library library; FT_Error error = FT_Init_FreeType(&library); @@ -942,7 +932,7 @@ int main(int argc, char **argv) if (cr_status != CAIRO_STATUS_SUCCESS) { /* TRANSLATORS: 'cairo' is a name of a library, and should be left untranslated */ fmt::print(cerr, _("{}: failed to create cairo surface: {}\n"), argv[0], - cairo_status_to_string(cr_status)); + cairo_status_to_string(cr_status)); exit(1); } @@ -950,7 +940,7 @@ int main(int argc, char **argv) cr_status = cairo_status(cr); if (cr_status != CAIRO_STATUS_SUCCESS) { fmt::print(cerr, _("{}: cairo_create failed: {}\n"), argv[0], - cairo_status_to_string(cr_status)); + cairo_status_to_string(cr_status)); exit(1); } @@ -961,7 +951,7 @@ int main(int argc, char **argv) cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); calc_font_scaling(face); - draw_glyphs(cr, face, other_face); + draw_glyphs(blocks, cr, face, other_face); cairo_destroy(cr); return 0; diff --git a/src/gen_unicode_blocks.cpp b/src/gen_unicode_blocks.cpp index 8bce69c..9b9237a 100644 --- a/src/gen_unicode_blocks.cpp +++ b/src/gen_unicode_blocks.cpp @@ -1,39 +1,41 @@ /* Copyright © Євгеній Мещеряков * SPDX-License-Identifier: GPL-3.0-or-later */ -#include "unicode_blocks.h" #include #include #include +#include "loadable_unicode_blocks.h" + using namespace std; static void write_header(ostream &stream) { stream << -R"(#include "static_unicode_blocks.h" + R"(#include "static_unicode_blocks.h" -const unicode_block static_unicode_blocks[] = { +constexpr static_unicode_blocks static_blocks { )"; } static void write_footer(ostream &stream) { - stream << -R"( {0, 0, nullptr}, -}; + stream << R"(}; + +const unicode_blocks &get_static_blocks() { return static_blocks; } )"; } -static void write_block(ostream &stream, const unicode_block &block) +static void write_block(ostream &stream, const unicode_blocks::block &block) { - fmt::print(stream, " {{0x{:04x}, 0x{:04x}, \"{}\"}},\n", block.start, block.end, block.name); + fmt::print(stream, " unicode_blocks::block {{{{0x{:04x}, 0x{:04x}}}, \"{}\"}},\n", + block.r.start, block.r.end, block.name); } -static void write_blocks(ostream &stream, const unicode_block *blocks, int n) +static void write_blocks(ostream &stream, const unicode_blocks &blocks) { write_header(stream); - for (int i = 0; i < n; i++) { + for (size_t i = 0; i < blocks.size(); i++) { write_block(stream, blocks[i]); } write_footer(stream); @@ -46,25 +48,34 @@ int main(int argc, char **argv) return 1; } - int n; - unicode_block *blocks = read_blocks(argv[1], &n); + const char *input_file_name = argv[1]; + const char *output_file_name = argv[2]; + + ifstream input(input_file_name, ios::binary); + if (!input) { + cerr << "Failed to open input file\n"; + return 1; + } + + loadable_unicode_blocks blocks(input); + input.close(); - if (!blocks) { - cerr << "Failed to read unicode blocks file.\n"; - return 2; + if (blocks.error_line()) { + fmt::print(cerr, "Parse error at line {}\n", blocks.error_line()); + return 1; } - try { - ofstream f(argv[2], ios::out | ios::trunc); - f.exceptions(ios::badbit | ios::failbit); - write_blocks(f, blocks, n); - } catch (const ios_base::failure &e) { - // FIXME: How to get useful error messages? - fmt::print(cerr, "Failed to save code file: {}\n", e.what()); + ofstream output(output_file_name, ios::out | ios::trunc | ios::binary); + if (!output) { + cerr << "Failed to open output file\n"; return 1; } - free(blocks); + write_blocks(output, blocks); + if (!output) { + cerr << "Failed to write the output\n"; + return 1; + } return 0; } diff --git a/src/loadable_unicode_blocks.cpp b/src/loadable_unicode_blocks.cpp new file mode 100644 index 0000000..dcb7ef0 --- /dev/null +++ b/src/loadable_unicode_blocks.cpp @@ -0,0 +1,72 @@ +/* Copyright © Євгеній Мещеряков + * SPDX-License-Identifier: GPL-3.0-or-later + */ +#include "loadable_unicode_blocks.h" +#include +#include +#include +#include "unicode_utils.h" + +using namespace std; + +loadable_unicode_blocks::loadable_unicode_blocks(istream &stream) +{ + static const regex re(R"(([[:xdigit:]]+)\.\.([[:xdigit:]]+); (.*))"); + string line; + + for (size_t line_no = 1; getline(stream, line); line_no++) { + string_view v(line); + + // Remove comment if any + if (auto c_pos = v.find('#'); c_pos != v.npos) { + v.remove_suffix(v.size() - c_pos); + } + + // Trim whitespace at the end + auto ws_pos = v.end(); + for (ws_pos--; ws_pos >= v.begin(); ws_pos--) { + auto c = *ws_pos; + if (!(c == '\n' || c == '\r' || c == ' ' || c == '\t')) { + break; + } + } + v.remove_suffix(v.size() - (ws_pos + 1 - v.begin())); + + if (v.empty()) { + continue; + } + + cmatch m; + if (!regex_match(v.data(), v.data() + v.size(), m, re)) { + _error_line = line_no; + blocks.clear(); + return; + } + + auto parse_code = [](cmatch::const_reference sm) -> optional { + unsigned long n; + auto [ptr, ec] = from_chars(sm.first, sm.second, n, 16); + if (ec == std::errc()) { + return unicode_utils::to_char32_t(n); + } + return {}; + }; + + auto start = parse_code(m[1]); + auto end = parse_code(m[2]); + if (!start || !end) { + _error_line = line_no; + blocks.clear(); + return; + } + + unicode_range r {*start, *end}; + if (!r.is_valid()) { + _error_line = line_no; + blocks.clear(); + return; + } + + blocks.push_back({r, string(m[3].first, m[3].second)}); + } +} diff --git a/src/loadable_unicode_blocks.h b/src/loadable_unicode_blocks.h new file mode 100644 index 0000000..be82ea6 --- /dev/null +++ b/src/loadable_unicode_blocks.h @@ -0,0 +1,34 @@ +/* Copyright © Євгеній Мещеряков + * SPDX-License-Identifier: GPL-3.0-or-later + */ +#ifndef LOADABLE_UNICODE_BLOCKS_H +#define LOADABLE_UNICODE_BLOCKS_H + +#include "unicode_blocks.h" +#include +#include + +class loadable_unicode_blocks final : public unicode_blocks { +public: + explicit loadable_unicode_blocks(std::istream &stream); + + size_t size() const noexcept override { return blocks.size(); } + block operator[](size_t i) const override + { + const auto &b = blocks[i]; + return {b.r, b.name.data()}; + } + + constexpr size_t error_line() const noexcept { return _error_line; } + +private: + struct entry { + unicode_range r; + std::string name; + }; + + std::vector blocks; + size_t _error_line = 0; +}; + +#endif diff --git a/src/read_blocks.cpp b/src/read_blocks.cpp deleted file mode 100644 index e47bc31..0000000 --- a/src/read_blocks.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/* Copyright © Євгеній Мещеряков - * SPDX-License-Identifier: GPL-3.0-or-later - */ -#include "unicode_blocks.h" -#include -#include -#include - -using namespace std; - -unicode_block *read_blocks(const char *file_name, int *n) -{ - int nalloc = 256; - unicode_block *blocks = static_cast(calloc(nalloc, sizeof(unicode_block))); - *n = 0; - - FILE *input_file = fopen(file_name, "r"); - if (!input_file) { - perror("fopen"); - exit(7); - } - - char *line = nullptr; - size_t len = 0; - ssize_t nread; - - while ((nread = getline(&line, &len, input_file)) != -1) { - unsigned long block_start, block_end; - char block_name[256]; - - if (nread >= (ssize_t)sizeof(block_name)) - continue; - - int matched = sscanf(line, "%lx..%lx; %[^\r\n]", &block_start, &block_end, block_name); - if (matched == 3) { - unicode_block *b = blocks + *n; - b->start = block_start; - b->end = block_end; - b->name = strdup(block_name); - if (b->name == nullptr) { - perror("strdup"); - exit(8); - } - - *n += 1; - if (*n >= nalloc) { - int new_nalloc = nalloc + 256; - unicode_block *new_blocks - = static_cast(realloc(blocks, new_nalloc * sizeof(unicode_block))); - if (new_blocks == nullptr) { - perror("realloc"); - exit(9); - } - memset(new_blocks + nalloc, 0, - (new_nalloc - nalloc) * sizeof(unicode_block)); - nalloc = new_nalloc; - blocks = new_blocks; - } - } - } - free(line); - - if (*n == 0) { - free(blocks); - return nullptr; - } else if (*n < nalloc) { - blocks = static_cast(realloc(blocks, *n * sizeof(unicode_block))); - } - - return blocks; -} diff --git a/src/static_unicode_blocks.h b/src/static_unicode_blocks.h index 9369cd6..c505d43 100644 --- a/src/static_unicode_blocks.h +++ b/src/static_unicode_blocks.h @@ -1,11 +1,29 @@ -/* - * Author: Ievgenii Meshcheriakov - * SPDX-License-Identifier: CC-PDDC +/* Copyright © Євгеній Мещеряков + * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef STATIC_UNICODE_BLOCKS_H #define STATIC_UNICODE_BLOCKS_H + #include "unicode_blocks.h" +#include + +template class static_unicode_blocks final : public unicode_blocks { +public: + template + constexpr explicit static_unicode_blocks(T &&... blocks) noexcept + : blocks {std::forward(blocks)...} + { + } + + size_t size() const noexcept override { return blocks.size(); } + block operator[](size_t i) const override { return blocks[i]; } + +private: + std::array blocks; +}; + +template static_unicode_blocks(T &&...)->static_unicode_blocks; -extern const unicode_block static_unicode_blocks[]; +const unicode_blocks &get_static_blocks(); #endif diff --git a/src/unicode_blocks.cpp b/src/unicode_blocks.cpp new file mode 100644 index 0000000..a82b8b4 --- /dev/null +++ b/src/unicode_blocks.cpp @@ -0,0 +1,23 @@ +/* Copyright © Євгеній Мещеряков + * SPDX-License-Identifier: GPL-3.0-or-later + */ +#include "unicode_blocks.h" + +using namespace std; + +/* + * Locate unicode block that contains given character code. + * Returns this block or nullptr if not found. + */ +optional unicode_blocks::find_block(char32_t c) const noexcept +{ + // TODO use binary search + for (size_t i = 0; i < size(); i++) { + const auto &block = (*this)[i]; + if (block.r.contains(c)) { + return block; + } + } + + return {}; +} diff --git a/src/unicode_blocks.h b/src/unicode_blocks.h index 374d926..28891a5 100644 --- a/src/unicode_blocks.h +++ b/src/unicode_blocks.h @@ -1,16 +1,26 @@ -/* - * Author: Ievgenii Meshcheriakov - * SPDX-License-Identifier: CC-PDDC +/* Copyright © Євгеній Мещеряков + * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef UNICODE_BLOCKS_H #define UNICODE_BLOCKS_H -struct unicode_block { - unsigned long start; - unsigned long end; - const char *name; -}; +#include "unicode_range.h" +#include + +// TODO: provide an iterator +class unicode_blocks { +public: + struct block { + unicode_range r; + const char *name; + + constexpr bool contains(char32_t c) const noexcept { return r.contains(c); } + }; -unicode_block *read_blocks(const char *file_name, int *n); + virtual size_t size() const noexcept = 0; + virtual block operator[](size_t index) const = 0; + + std::optional find_block(char32_t c) const noexcept; +}; #endif diff --git a/src/unicode_range.h b/src/unicode_range.h new file mode 100644 index 0000000..f521b30 --- /dev/null +++ b/src/unicode_range.h @@ -0,0 +1,16 @@ +/* Copyright © Євгеній Мещеряков + * SPDX-License-Identifier: GPL-3.0-or-later + */ +#ifndef UNICODE_RANGE_H +#define UNICODE_RANGE_H + +template struct basic_range { + T start, end; + + constexpr bool contains(T e) const noexcept { return start <= e && e <= end; } + constexpr bool is_valid() const noexcept { return start <= end; } +}; + +using unicode_range = basic_range; + +#endif diff --git a/src/unicode_range_set.h b/src/unicode_range_set.h new file mode 100644 index 0000000..c08af9e --- /dev/null +++ b/src/unicode_range_set.h @@ -0,0 +1,38 @@ +/* Copyright © Євгеній Мещеряков + * SPDX-License-Identifier: GPL-3.0-or-later + */ +#ifndef UNICODE_RANGE_SET_H +#define UNICODE_RANGE_SET_H + +#include "unicode_range.h" +#include + +template class basic_range_set { +public: + using range = basic_range; + + bool contains(T e) const noexcept + { + bool in = !specs.empty() || !specs.front().include; + for (auto spec : specs) { + if (spec.r.contains(e)) { + in = spec.include; + } + } + return in; + } + + void add(const range &r, bool include) noexcept { specs.push_back(range_spec {r, include}); } + +private: + struct range_spec { + range r; + bool include; + }; + + std::list specs; +}; + +using unicode_range_set = basic_range_set; + +#endif diff --git a/src/unicode_utils.h b/src/unicode_utils.h new file mode 100644 index 0000000..59fb0e1 --- /dev/null +++ b/src/unicode_utils.h @@ -0,0 +1,27 @@ +/* Copyright © Євгеній Мещеряков + * SPDX-License-Identifier: GPL-3.0-or-later + */ +#ifndef UNICODE_UTILS_H +#define UNICODE_UTILS_H + +#include +#include +#include + +namespace unicode_utils { + +constexpr char32_t last_codepoint = 0x10ffff; + +template constexpr std::optional to_char32_t(U n) noexcept +{ + static_assert(std::is_unsigned_v); + static_assert(std::numeric_limits::max() >= last_codepoint); + if (n <= last_codepoint) { + return static_cast(n); + } + return {}; +} + +} + +#endif From 4bf101c82345c21641b0da3547c6f9861187cb8e Mon Sep 17 00:00:00 2001 From: Ievgenii Meshcheriakov Date: Sun, 3 Oct 2021 22:31:15 +0200 Subject: [PATCH 4/9] Refactor page metrics into a struct --- src/fntsample.cpp | 148 ++++++++++++++++++++++++++-------------------- 1 file changed, 85 insertions(+), 63 deletions(-) diff --git a/src/fntsample.cpp b/src/fntsample.cpp index 25d0591..51b4419 100644 --- a/src/fntsample.cpp +++ b/src/fntsample.cpp @@ -39,19 +39,43 @@ using namespace std; #define _(str) gettext(str) -#define POINTS_PER_INCH 72 +constexpr double POINTS_PER_INCH = 72; -#define A4_WIDTH (8.3 * POINTS_PER_INCH) -#define A4_HEIGHT (11.7 * POINTS_PER_INCH) +class page_metrics { +public: + explicit page_metrics(unsigned num_columns) noexcept + : num_columns(num_columns) + , table_width(num_columns * cell_width) + , x_min((page_width - table_width) / 2) + , x_max(page_width - x_min) + { + } + + static constexpr unsigned num_rows = 16; + static constexpr unsigned max_num_columns = 16; + const unsigned num_columns; + + // NOTE: A4 paper size + static constexpr double page_width = 8.3 * POINTS_PER_INCH; + static constexpr double page_height = 11.7 * POINTS_PER_INCH; -#define xmin_border (POINTS_PER_INCH / 1.5) -#define ymin_border POINTS_PER_INCH -#define cell_width ((A4_WIDTH - 2 * xmin_border) / 16) -#define cell_height ((A4_HEIGHT - 2 * ymin_border) / 16) + static constexpr double min_horiz_border = POINTS_PER_INCH / 1.5; + static constexpr double vert_border = POINTS_PER_INCH; + static constexpr double cell_width = (page_width - 2 * min_horiz_border) / max_num_columns; + static constexpr double cell_height = (page_height - 2 * vert_border) / num_rows; -static double cell_x(double x_min, int pos) { return x_min + cell_width * (pos / 16); } + static constexpr double table_height = page_height - vert_border * 2; + const double table_width; + const double x_min; + const double x_max; -static double cell_y(int pos) { return ymin_border + cell_height * (pos % 16); } + double cell_x(unsigned pos) const noexcept { return x_min + cell_width * (pos / num_rows); } + + double cell_y(unsigned pos) const noexcept + { + return vert_border + cell_height * (pos % num_rows); + } +}; static option longopts[] = { {"blocks-file", 1, 0, 'b'}, @@ -430,12 +454,12 @@ static void draw_header(cairo_t *cr, const char *face_name, const char *block_na PangoRectangle r; PangoLayout *layout = layout_text(cr, table_fonts.font_name, face_name, &r); - cairo_move_to(cr, (A4_WIDTH - pango_units_to_double(r.width)) / 2.0, 30.0); + cairo_move_to(cr, (page_metrics::page_width - pango_units_to_double(r.width)) / 2.0, 30.0); pango_cairo_show_layout_line(cr, pango_layout_get_line_readonly(layout, 0)); g_object_unref(layout); layout = layout_text(cr, table_fonts.header, block_name, &r); - cairo_move_to(cr, (A4_WIDTH - pango_units_to_double(r.width)) / 2.0, 50.0); + cairo_move_to(cr, (page_metrics::page_width - pango_units_to_double(r.width)) / 2.0, 50.0); pango_cairo_show_layout_line(cr, pango_layout_get_line_readonly(layout, 0)); g_object_unref(layout); } @@ -448,7 +472,7 @@ static void highlight_cell(cairo_t *cr, double x, double y) { cairo_save(cr); cairo_set_source_rgb(cr, 1.0, 1.0, 0.6); - cairo_rectangle(cr, x, y, cell_width, cell_height); + cairo_rectangle(cr, x, y, page_metrics::cell_width, page_metrics::cell_height); cairo_fill(cr); cairo_restore(cr); } @@ -456,64 +480,59 @@ static void highlight_cell(cairo_t *cr, double x, double y) /* * Draw table grid with row and column numbers. */ -static void draw_grid(cairo_t *cr, unsigned int x_cells, unsigned long block_start) +static void draw_grid(cairo_t *cr, const page_metrics &page, unsigned long block_start) { - const double x_min = (A4_WIDTH - x_cells * cell_width) / 2; - const double x_max = (A4_WIDTH + x_cells * cell_width) / 2; - const double table_height = A4_HEIGHT - ymin_border * 2; - cairo_set_line_width(cr, 1.0); - cairo_rectangle(cr, x_min, ymin_border, x_max - x_min, table_height); - cairo_move_to(cr, x_min, ymin_border); - cairo_line_to(cr, x_min, ymin_border - 15.0); - cairo_move_to(cr, x_max, ymin_border); - cairo_line_to(cr, x_max, ymin_border - 15.0); + cairo_rectangle(cr, page.x_min, page.vert_border, page.table_width, page.table_height); + cairo_move_to(cr, page.x_min, page.vert_border); + cairo_line_to(cr, page.x_min, page.vert_border - 15.0); + cairo_move_to(cr, page.x_max, page.vert_border); + cairo_line_to(cr, page.x_max, page.vert_border - 15.0); cairo_stroke(cr); cairo_set_line_width(cr, 0.5); /* draw horizontal lines */ - for (int i = 1; i < 16; i++) { - // TODO: use better name instead of just POINTS_PER_INCH - cairo_move_to(cr, x_min, POINTS_PER_INCH + i * table_height / 16); - cairo_line_to(cr, x_max, POINTS_PER_INCH + i * table_height / 16); + for (unsigned row = 1; row < page.num_rows; row++) { + const auto y = page.vert_border + row * page.cell_height; + cairo_move_to(cr, page.x_min, y); + cairo_line_to(cr, page.x_max, y); } /* draw vertical lines */ - for (unsigned int i = 1; i < x_cells; i++) { - cairo_move_to(cr, x_min + i * cell_width, ymin_border); - cairo_line_to(cr, x_min + i * cell_width, A4_HEIGHT - ymin_border); + for (unsigned col = 1; col < page.num_columns; col++) { + const auto x = page.x_min + col * page.cell_width; + cairo_move_to(cr, x, page.vert_border); + cairo_line_to(cr, x, page.page_height - page.vert_border); } cairo_stroke(cr); - /* draw glyph numbers */ - char buf[17]; - buf[1] = '\0'; -#define hexdigs "0123456789ABCDEF" + string buf; - for (int i = 0; i < 16; i++) { - buf[0] = hexdigs[i]; + /* draw glyph numbers */ + for (unsigned row = 0; row < page.num_rows; row++) { + buf = fmt::format("{:X}", row); PangoRectangle r; - PangoLayout *layout = layout_text(cr, table_fonts.table_numbers, buf, &r); - cairo_move_to(cr, x_min - pango_units_to_double(PANGO_RBEARING(r)) - 5.0, - POINTS_PER_INCH + (i + 0.5) * table_height / 16 - + pango_units_to_double(PANGO_DESCENT(r)) / 2); + PangoLayout *layout = layout_text(cr, table_fonts.table_numbers, buf.c_str(), &r); + const auto y = page.vert_border + (row + 0.5) * page.cell_height + + pango_units_to_double(PANGO_DESCENT(r)) / 2; + + cairo_move_to(cr, page.x_min - pango_units_to_double(PANGO_RBEARING(r)) - 5.0, y); pango_cairo_show_layout_line(cr, pango_layout_get_line_readonly(layout, 0)); - cairo_move_to(cr, x_min + x_cells * cell_width + 5.0, - POINTS_PER_INCH + (i + 0.5) * table_height / 16 - + pango_units_to_double(PANGO_DESCENT(r)) / 2); + cairo_move_to(cr, page.x_max + 5.0, y); pango_cairo_show_layout_line(cr, pango_layout_get_line_readonly(layout, 0)); g_object_unref(layout); } - for (unsigned int i = 0; i < x_cells; i++) { - auto s = fmt::format("{:03X}", block_start / 16 + i); + for (unsigned col = 0; col < page.num_columns; col++) { + buf = fmt::format("{:03X}", block_start / page.num_rows + col); PangoRectangle r; - PangoLayout *layout = layout_text(cr, table_fonts.table_numbers, s.c_str(), &r); + PangoLayout *layout = layout_text(cr, table_fonts.table_numbers, buf.c_str(), &r); cairo_move_to(cr, - x_min + i * cell_width + (cell_width - pango_units_to_double(r.width)) / 2, - ymin_border - 5.0); + page.x_min + col * page.cell_width + + (page.cell_width - pango_units_to_double(r.width)) / 2, + page.vert_border - 5.0); pango_cairo_show_layout_line(cr, pango_layout_get_line_readonly(layout, 0)); g_object_unref(layout); } @@ -531,7 +550,7 @@ static void fill_empty_cell(cairo_t *cr, double x, double y, unsigned long charc else cairo_set_source_rgb(cr, 0.5, 0.5, 0.5); } - cairo_rectangle(cr, x, y, cell_width, cell_height); + cairo_rectangle(cr, x, y, page_metrics::cell_width, page_metrics::cell_height); cairo_fill(cr); cairo_restore(cr); } @@ -545,8 +564,8 @@ static void draw_charcode(cairo_t *cr, double x, double y, FT_ULong charcode) PangoRectangle r; PangoLayout *layout = layout_text(cr, table_fonts.cell_numbers, s.c_str(), &r); - cairo_move_to(cr, x + (cell_width - pango_units_to_double(r.width)) / 2.0, - y + cell_height - cell_label_offset); + cairo_move_to(cr, x + (page_metrics::cell_width - pango_units_to_double(r.width)) / 2.0, + y + page_metrics::cell_height - cell_label_offset); pango_cairo_show_layout_line(cr, pango_layout_get_line_readonly(layout, 0)); g_object_unref(layout); } @@ -572,8 +591,7 @@ static int draw_unicode_block(cairo_t *cr, PangoLayout *layout, FT_Face ft_face, unsigned long tbl_start = block.r.start + offset; unsigned long tbl_end = tbl_start + 0xFF > block.r.end ? block.r.end + 1 : tbl_start + 0x100; - unsigned int rows = (tbl_end - tbl_start) / 16; - double x_min = (A4_WIDTH - rows * cell_width) / 2; + const page_metrics page((tbl_end - tbl_start) / page.num_rows); bool filled_cells[256]; /* 16x16 glyphs max */ unsigned long curr_charcode = tbl_start; @@ -591,12 +609,12 @@ static int draw_unicode_block(cairo_t *cr, PangoLayout *layout, FT_Face ft_face, do { /* fill empty cells before the current glyph */ for (; curr_charcode < charcode; curr_charcode++, pos++) { - fill_empty_cell(cr, cell_x(x_min, pos), cell_y(pos), curr_charcode); + fill_empty_cell(cr, page.cell_x(pos), page.cell_y(pos), curr_charcode); } /* if it is new glyph - highlight the cell */ if (ft_other_face && !FT_Get_Char_Index(ft_other_face, charcode)) { - highlight_cell(cr, cell_x(x_min, pos), cell_y(pos)); + highlight_cell(cr, page.cell_x(pos), page.cell_y(pos)); } /* draw the character */ @@ -605,7 +623,8 @@ static int draw_unicode_block(cairo_t *cr, PangoLayout *layout, FT_Face ft_face, pango_layout_set_text(layout, buf, len); double baseline = pango_units_to_double(pango_layout_get_baseline(layout)); - cairo_move_to(cr, cell_x(x_min, pos), cell_y(pos) + glyph_baseline_offset - baseline); + cairo_move_to(cr, page.cell_x(pos), + page.cell_y(pos) + glyph_baseline_offset - baseline); if (no_embed) { pango_cairo_layout_path(cr, layout); @@ -622,7 +641,7 @@ static int draw_unicode_block(cairo_t *cr, PangoLayout *layout, FT_Face ft_face, /* Fill remaining empty cells */ for (; curr_charcode < tbl_end; curr_charcode++, pos++) { - fill_empty_cell(cr, cell_x(x_min, pos), cell_y(pos), curr_charcode); + fill_empty_cell(cr, page.cell_x(pos), page.cell_y(pos), curr_charcode); } /* @@ -631,11 +650,11 @@ static int draw_unicode_block(cairo_t *cr, PangoLayout *layout, FT_Face ft_face, */ for (unsigned long i = 0; i < tbl_end - tbl_start; i++) { if (filled_cells[i]) { - draw_charcode(cr, cell_x(x_min, i), cell_y(i), i + tbl_start); + draw_charcode(cr, page.cell_x(i), page.cell_y(i), i + tbl_start); } } - draw_grid(cr, rows, tbl_start); + draw_grid(cr, page, tbl_start); npages++; cairo_show_page(cr); cairo_restore(cr); @@ -654,7 +673,7 @@ static PangoLayout *create_glyph_layout(cairo_t *cr, FcConfig *fc_config, FcPatt PangoFontDescription *font_desc = pango_fc_font_description_from_pattern(fc_font, FALSE); PangoLayout *layout = pango_layout_new(context); pango_layout_set_font_description(layout, font_desc); - pango_layout_set_width(layout, pango_units_from_double(cell_width)); + pango_layout_set_width(layout, pango_units_from_double(page_metrics::cell_width)); pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER); g_object_unref(context); @@ -807,7 +826,7 @@ void calc_font_scaling(FT_Face ft_face) cairo_scaled_font_extents(cr_font, &extents); /* Use some magic to find the best font size... */ - double tgt_size = cell_height - cell_glyph_bot_offset - 2; + double tgt_size = page_metrics::cell_height - cell_glyph_bot_offset - 2; if (tgt_size <= 0) { cerr << _("Not enough space for rendering glyphs. Make cell font smaller.\n"); exit(5); @@ -920,11 +939,14 @@ int main(int argc, char **argv) cairo_surface_t *surface; if (postscript_output) { - surface = cairo_ps_surface_create(output_file_name, A4_WIDTH, A4_HEIGHT); + surface = cairo_ps_surface_create(output_file_name, page_metrics::page_width, + page_metrics::page_height); } else if (svg_output) { - surface = cairo_svg_surface_create(output_file_name, A4_WIDTH, A4_HEIGHT); + surface = cairo_svg_surface_create(output_file_name, page_metrics::page_width, + page_metrics::page_height); } else { - surface = cairo_pdf_surface_create(output_file_name, A4_WIDTH, A4_HEIGHT); /* A4 paper */ + surface = cairo_pdf_surface_create(output_file_name, page_metrics::page_width, + page_metrics::page_height); set_repeatable_pdf_metadata(surface); } From 1c85c0857c5ddc0c066548735d9fd63a0d206ec5 Mon Sep 17 00:00:00 2001 From: Ievgenii Meshcheriakov Date: Sun, 3 Oct 2021 22:36:27 +0200 Subject: [PATCH 5/9] Use bitset to store filled cells --- src/fntsample.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/fntsample.cpp b/src/fntsample.cpp index 51b4419..144ce6a 100644 --- a/src/fntsample.cpp +++ b/src/fntsample.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include "loadable_unicode_blocks.h" #include "static_unicode_blocks.h" @@ -593,15 +594,13 @@ static int draw_unicode_block(cairo_t *cr, PangoLayout *layout, FT_Face ft_face, = tbl_start + 0xFF > block.r.end ? block.r.end + 1 : tbl_start + 0x100; const page_metrics page((tbl_end - tbl_start) / page.num_rows); - bool filled_cells[256]; /* 16x16 glyphs max */ + bitset<256> filled_cells; /* 16x16 glyphs max */ unsigned long curr_charcode = tbl_start; int pos = 0; cairo_save(cr); draw_header(cr, font_name, block.name); - memset(filled_cells, '\0', sizeof(filled_cells)); - /* * Fill empty cells and calculate coordinates of the glyphs. * Also highlight cells if needed. @@ -632,7 +631,7 @@ static int draw_unicode_block(cairo_t *cr, PangoLayout *layout, FT_Face ft_face, pango_cairo_show_layout(cr, layout); } - filled_cells[pos] = true; + filled_cells.set(pos); curr_charcode++; pos++; @@ -649,7 +648,7 @@ static int draw_unicode_block(cairo_t *cr, PangoLayout *layout, FT_Face ft_face, * font and the cell font for each filled cell. */ for (unsigned long i = 0; i < tbl_end - tbl_start; i++) { - if (filled_cells[i]) { + if (filled_cells.test(i)) { draw_charcode(cr, page.cell_x(i), page.cell_y(i), i + tbl_start); } } From 84653ee6819d8f19b041364920549b8f04de16f2 Mon Sep 17 00:00:00 2001 From: Ievgenii Meshcheriakov Date: Wed, 6 Oct 2021 23:16:32 +0200 Subject: [PATCH 6/9] Use cairomm and pangomm --- .github/workflows/ci.yml | 4 +- CMakeLists.txt | 4 + src/CMakeLists.txt | 2 +- src/fntsample.cpp | 349 +++++++++++++++++++-------------------- 4 files changed, 175 insertions(+), 184 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e2a5068..b958496 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,13 +21,13 @@ jobs: blocks: -DUNICODE_BLOCKS=/usr/share/unicode/Blocks.txt install_packages: > sudo apt update && - sudo apt install cmake gettext libcairo2-dev libglib2.0-dev libfmt-dev libfreetype6-dev libpango1.0-dev ninja-build pkg-config unicode-data + sudo apt install cmake gettext libcairo2-dev libcairomm-1.0-dev libglib2.0-dev libfmt-dev libfreetype6-dev libpango1.0-dev libpangomm-1.4-dev ninja-build pkg-config unicode-data - os: macos-latest env: - CMAKE_PREFIX_PATH: /usr/local/opt/gettext install_packages: > brew update && - brew install cairo cmake fmt fontconfig freetype gettext glib pango ninja pkg-config + brew install cairo cairomm cmake fmt fontconfig freetype gettext glib pango pangomm ninja pkg-config runs-on: ${{matrix.os}} diff --git a/CMakeLists.txt b/CMakeLists.txt index 5d97629..8daae11 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,6 +34,10 @@ pkg_check_modules(pkgs REQUIRED IMPORTED_TARGET pangoft2>=1.37.0 ) +# FIXME: this is ugly +pkg_search_module(cairomm REQUIRED IMPORTED_TARGET cairomm cairomm-1.16 cairomm-1.0) +pkg_search_module(pangomm REQUIRED IMPORTED_TARGET pangomm pangomm-2.48 pangomm-1.4) + include(DownloadUnicodeBlocks) download_unicode_blocks() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 47da306..5a260d2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -41,7 +41,7 @@ target_include_directories(fntsample PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ) -target_link_libraries(fntsample PRIVATE Intl::Intl PkgConfig::pkgs fmt::fmt) +target_link_libraries(fntsample PRIVATE Intl::Intl PkgConfig::pkgs fmt::fmt PkgConfig::cairomm PkgConfig::pangomm) target_compile_options(fntsample PRIVATE ${C_WARNING_FLAGS}) diff --git a/src/fntsample.cpp b/src/fntsample.cpp index 144ce6a..9874347 100644 --- a/src/fntsample.cpp +++ b/src/fntsample.cpp @@ -5,18 +5,17 @@ // TODO: freetype 2.10.3, do not include ft2build.h anymore #include #include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include #include #include #include #include #include #include -#include #include #include #include @@ -40,6 +39,20 @@ using namespace std; #define _(str) gettext(str) +// This is also available in cairomm master +class SaveGuard final { +public: + explicit SaveGuard(Cairo::RefPtr &context) + : ctx(context) + { + ctx->save(); + } + ~SaveGuard() { ctx->restore(); } + +private: + Cairo::RefPtr ctx; +}; + constexpr double POINTS_PER_INCH = 72; class page_metrics { @@ -126,10 +139,10 @@ static fntsample_style styles[] = { }; struct table_fonts { - PangoFontDescription *header; - PangoFontDescription *font_name; - PangoFontDescription *table_numbers; - PangoFontDescription *cell_numbers; + Pango::FontDescription header; + Pango::FontDescription font_name; + Pango::FontDescription table_numbers; + Pango::FontDescription cell_numbers; }; static table_fonts table_fonts; @@ -316,15 +329,18 @@ static FT_ULong get_first_char(FT_Face face, FT_UInt *idx) * Updates 'r' with text extents. * Returned layout should be freed using g_object_unref(). */ -static PangoLayout *layout_text(cairo_t *cr, PangoFontDescription *ftdesc, const char *text, - PangoRectangle *r) +static auto layout_text(Cairo::RefPtr &cr, Pango::FontDescription &ftdesc, + const char *text) { - PangoLayout *layout = pango_cairo_create_layout(cr); - pango_layout_set_font_description(layout, ftdesc); - pango_layout_set_text(layout, text, -1); - pango_layout_get_extents(layout, r, nullptr); + auto layout = Pango::Layout::create(cr); - return layout; + layout->set_font_description(ftdesc); + layout->set_text(text); + Pango::Rectangle ink_rect, logical_rect; + layout->get_extents(ink_rect, logical_rect); + + // FIXME: can logical rect be used instead? + return make_tuple(layout, ink_rect); } static void parse_options(int argc, char *const argv[]) @@ -433,79 +449,84 @@ static void parse_options(int argc, char *const argv[]) /* * Format and print/write outline information, if requested by the user. */ -static void outline(cairo_surface_t *surface, int level, int page, const char *text) +static void outline(Cairo::RefPtr &cr, int level, int page, const char *text) { if (print_outline) { fmt::print("{} {} {}\n", level, page, text); } - if (write_outline && cairo_surface_get_type(surface) == CAIRO_SURFACE_TYPE_PDF) { - auto s = fmt::format("page={}", page); - /* FIXME passing level here is not correct. */ - cairo_pdf_surface_add_outline(surface, level, text, s.c_str(), CAIRO_PDF_OUTLINE_FLAG_OPEN); + if (write_outline) { + // TODO: cache surface + if (auto surface = cr->get_target(); surface->get_type() == Cairo::Surface::Type::PDF) { + auto s = fmt::format("page={}", page); + /* FIXME passing level here is not correct. */ + cairo_pdf_surface_add_outline(surface->cobj(), level, text, s.c_str(), + CAIRO_PDF_OUTLINE_FLAG_OPEN); + } } } +static void draw_header_line(Cairo::RefPtr &cr, Pango::FontDescription &ftdesc, + const char *text, double y) +{ + auto [layout, r] = layout_text(cr, ftdesc, text); + cr->move_to((page_metrics::page_width - pango_units_to_double(r.get_width())) / 2.0, y); + as_const(layout)->get_line(0)->show_in_cairo_context(cr); +} + /* * Draw header of a page. * Header shows font name and current Unicode block. */ -static void draw_header(cairo_t *cr, const char *face_name, const char *block_name) +static void draw_header(Cairo::RefPtr &cr, const char *face_name, + const char *block_name) { - PangoRectangle r; - - PangoLayout *layout = layout_text(cr, table_fonts.font_name, face_name, &r); - cairo_move_to(cr, (page_metrics::page_width - pango_units_to_double(r.width)) / 2.0, 30.0); - pango_cairo_show_layout_line(cr, pango_layout_get_line_readonly(layout, 0)); - g_object_unref(layout); - - layout = layout_text(cr, table_fonts.header, block_name, &r); - cairo_move_to(cr, (page_metrics::page_width - pango_units_to_double(r.width)) / 2.0, 50.0); - pango_cairo_show_layout_line(cr, pango_layout_get_line_readonly(layout, 0)); - g_object_unref(layout); + draw_header_line(cr, table_fonts.font_name, face_name, 30.0); + draw_header_line(cr, table_fonts.header, block_name, 50.0); } /* * Highlight the cell with given coordinates. * Used to highlight new glyphs. */ -static void highlight_cell(cairo_t *cr, double x, double y) +static void highlight_cell(Cairo::RefPtr &cr, double x, double y) { - cairo_save(cr); - cairo_set_source_rgb(cr, 1.0, 1.0, 0.6); - cairo_rectangle(cr, x, y, page_metrics::cell_width, page_metrics::cell_height); - cairo_fill(cr); - cairo_restore(cr); + SaveGuard saver(cr); + cr->set_source_rgb(1.0, 1.0, 0.6); + cr->rectangle(x, y, page_metrics::cell_width, page_metrics::cell_height); + cr->fill(); } /* * Draw table grid with row and column numbers. */ -static void draw_grid(cairo_t *cr, const page_metrics &page, unsigned long block_start) +static void draw_grid(Cairo::RefPtr &cr, const page_metrics &page, + unsigned long block_start) { - cairo_set_line_width(cr, 1.0); - cairo_rectangle(cr, page.x_min, page.vert_border, page.table_width, page.table_height); - cairo_move_to(cr, page.x_min, page.vert_border); - cairo_line_to(cr, page.x_min, page.vert_border - 15.0); - cairo_move_to(cr, page.x_max, page.vert_border); - cairo_line_to(cr, page.x_max, page.vert_border - 15.0); - cairo_stroke(cr); - - cairo_set_line_width(cr, 0.5); + cr->set_line_width(1.0); + cr->rectangle(page.x_min, page.vert_border, page.table_width, page.table_height); + cr->move_to(page.x_min, page.vert_border); + cr->line_to(page.x_min, page.vert_border - 15.0); + cr->move_to(page.x_max, page.vert_border); + cr->line_to(page.x_max, page.vert_border - 15.0); + cr->stroke(); + + cr->set_line_width(0.5); + /* draw horizontal lines */ for (unsigned row = 1; row < page.num_rows; row++) { const auto y = page.vert_border + row * page.cell_height; - cairo_move_to(cr, page.x_min, y); - cairo_line_to(cr, page.x_max, y); + cr->move_to(page.x_min, y); + cr->line_to(page.x_max, y); } /* draw vertical lines */ for (unsigned col = 1; col < page.num_columns; col++) { const auto x = page.x_min + col * page.cell_width; - cairo_move_to(cr, x, page.vert_border); - cairo_line_to(cr, x, page.page_height - page.vert_border); + cr->move_to(x, page.vert_border); + cr->line_to(x, page.page_height - page.vert_border); } - cairo_stroke(cr); + cr->stroke(); string buf; @@ -513,62 +534,58 @@ static void draw_grid(cairo_t *cr, const page_metrics &page, unsigned long block for (unsigned row = 0; row < page.num_rows; row++) { buf = fmt::format("{:X}", row); - PangoRectangle r; - PangoLayout *layout = layout_text(cr, table_fonts.table_numbers, buf.c_str(), &r); + auto [layout, r] = layout_text(cr, table_fonts.table_numbers, buf.c_str()); const auto y = page.vert_border + (row + 0.5) * page.cell_height - + pango_units_to_double(PANGO_DESCENT(r)) / 2; + + pango_units_to_double(r.get_descent()) / 2; - cairo_move_to(cr, page.x_min - pango_units_to_double(PANGO_RBEARING(r)) - 5.0, y); - pango_cairo_show_layout_line(cr, pango_layout_get_line_readonly(layout, 0)); - cairo_move_to(cr, page.x_max + 5.0, y); - pango_cairo_show_layout_line(cr, pango_layout_get_line_readonly(layout, 0)); - g_object_unref(layout); + cr->move_to(page.x_min - pango_units_to_double(r.get_rbearing()) - 5.0, y); + auto line = as_const(layout)->get_line(0); + line->show_in_cairo_context(cr); + cr->move_to(page.x_max + 5.0, y); + line->show_in_cairo_context(cr); } for (unsigned col = 0; col < page.num_columns; col++) { buf = fmt::format("{:03X}", block_start / page.num_rows + col); - PangoRectangle r; - PangoLayout *layout = layout_text(cr, table_fonts.table_numbers, buf.c_str(), &r); - cairo_move_to(cr, - page.x_min + col * page.cell_width - + (page.cell_width - pango_units_to_double(r.width)) / 2, - page.vert_border - 5.0); - pango_cairo_show_layout_line(cr, pango_layout_get_line_readonly(layout, 0)); - g_object_unref(layout); + auto [layout, r] = layout_text(cr, table_fonts.table_numbers, buf.c_str()); + cr->move_to(page.x_min + col * page.cell_width + + (page.cell_width - pango_units_to_double(r.get_width())) / 2, + page.vert_border - 5.0); + as_const(layout)->get_line(0)->show_in_cairo_context(cr); } } /* * Fill empty cell. Color of the fill depends on the character properties. */ -static void fill_empty_cell(cairo_t *cr, double x, double y, unsigned long charcode) +static void fill_empty_cell(Cairo::RefPtr &cr, double x, double y, + unsigned long charcode) { - cairo_save(cr); + SaveGuard saver(cr); if (g_unichar_isdefined(charcode)) { - if (g_unichar_iscntrl(charcode)) - cairo_set_source_rgb(cr, 0.0, 0.0, 0.5); - else - cairo_set_source_rgb(cr, 0.5, 0.5, 0.5); - } - cairo_rectangle(cr, x, y, page_metrics::cell_width, page_metrics::cell_height); - cairo_fill(cr); - cairo_restore(cr); + if (g_unichar_iscntrl(charcode)) { + cr->set_source_rgb(0.0, 0.0, 0.5); + } else { + cr->set_source_rgb(0.5, 0.5, 0.5); + } + } + cr->rectangle(x, y, page_metrics::cell_width, page_metrics::cell_height); + cr->fill(); } /* * Draw label with character code. */ -static void draw_charcode(cairo_t *cr, double x, double y, FT_ULong charcode) +static void draw_charcode(Cairo::RefPtr &cr, double x, double y, FT_ULong charcode) { auto s = fmt::format("{:04X}", charcode); - PangoRectangle r; - PangoLayout *layout = layout_text(cr, table_fonts.cell_numbers, s.c_str(), &r); - cairo_move_to(cr, x + (page_metrics::cell_width - pango_units_to_double(r.width)) / 2.0, - y + page_metrics::cell_height - cell_label_offset); - pango_cairo_show_layout_line(cr, pango_layout_get_line_readonly(layout, 0)); - g_object_unref(layout); + // FIXME + auto [layout, r] = layout_text(cr, table_fonts.cell_numbers, s.c_str()); + cr->move_to(x + (page_metrics::cell_width - pango_units_to_double(r.get_width())) / 2.0, + y + page_metrics::cell_height - cell_label_offset); + as_const(layout)->get_line(0)->show_in_cairo_context(cr); } /* @@ -580,7 +597,8 @@ static void draw_charcode(cairo_t *cr, double x, double y, FT_ULong charcode) * * Returns number of pages drawn. */ -static int draw_unicode_block(cairo_t *cr, PangoLayout *layout, FT_Face ft_face, +static int draw_unicode_block(Cairo::RefPtr &cr, + Glib::RefPtr &layout, FT_Face ft_face, const char *font_name, unsigned long charcode, const unicode_blocks::block &block, FT_Face ft_other_face) { @@ -598,7 +616,7 @@ static int draw_unicode_block(cairo_t *cr, PangoLayout *layout, FT_Face ft_face, unsigned long curr_charcode = tbl_start; int pos = 0; - cairo_save(cr); + SaveGuard saver(cr); draw_header(cr, font_name, block.name); /* @@ -619,16 +637,16 @@ static int draw_unicode_block(cairo_t *cr, PangoLayout *layout, FT_Face ft_face, /* draw the character */ char buf[9]; gint len = g_unichar_to_utf8((gunichar)charcode, buf); - pango_layout_set_text(layout, buf, len); + buf[len] = 0; + layout->set_text(buf); - double baseline = pango_units_to_double(pango_layout_get_baseline(layout)); - cairo_move_to(cr, page.cell_x(pos), - page.cell_y(pos) + glyph_baseline_offset - baseline); + double baseline = pango_units_to_double(layout->get_baseline()); + cr->move_to(page.cell_x(pos), page.cell_y(pos) + glyph_baseline_offset - baseline); if (no_embed) { - pango_cairo_layout_path(cr, layout); + layout->add_to_cairo_context(cr); } else { - pango_cairo_show_layout(cr, layout); + layout->show_in_cairo_context(cr); } filled_cells.set(pos); @@ -655,29 +673,25 @@ static int draw_unicode_block(cairo_t *cr, PangoLayout *layout, FT_Face ft_face, draw_grid(cr, page, tbl_start); npages++; - cairo_show_page(cr); - cairo_restore(cr); + cr->show_page(); } while (idx && block.contains(charcode)); return npages; } -static PangoLayout *create_glyph_layout(cairo_t *cr, FcConfig *fc_config, FcPattern *fc_font) +static auto create_glyph_layout(Cairo::RefPtr &cr, FcConfig *fc_config, + FcPattern *fc_font) { - PangoFontMap *fontmap = pango_cairo_font_map_new_for_font_type(CAIRO_FONT_TYPE_FT); - pango_fc_font_map_set_config(PANGO_FC_FONT_MAP(fontmap), fc_config); - PangoContext *context = pango_font_map_create_context(fontmap); - pango_cairo_update_context(cr, context); - - PangoFontDescription *font_desc = pango_fc_font_description_from_pattern(fc_font, FALSE); - PangoLayout *layout = pango_layout_new(context); - pango_layout_set_font_description(layout, font_desc); - pango_layout_set_width(layout, pango_units_from_double(page_metrics::cell_width)); - pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER); + auto fontmap = Glib::wrap(pango_cairo_font_map_new_for_font_type(CAIRO_FONT_TYPE_FT)); + pango_fc_font_map_set_config(PANGO_FC_FONT_MAP(fontmap->gobj()), fc_config); + auto context = fontmap->create_context(); + context->update_from_cairo_context(cr); - g_object_unref(context); - g_object_unref(fontmap); - pango_font_description_free(font_desc); + auto font_desc = Glib::wrap(pango_fc_font_description_from_pattern(fc_font, FALSE)); + auto layout = Pango::Layout::create(context); + layout->set_font_description(font_desc); + layout->set_width(pango_units_from_double(page_metrics::cell_width)); + layout->set_alignment(Pango::Alignment::CENTER); return layout; } @@ -685,8 +699,8 @@ static PangoLayout *create_glyph_layout(cairo_t *cr, FcConfig *fc_config, FcPatt /* * The main drawing function. */ -static void draw_glyphs(const unicode_blocks &blocks, cairo_t *cr, FT_Face ft_face, - FT_Face ft_other_face) +static void draw_glyphs(const unicode_blocks &blocks, Cairo::RefPtr &cr, + FT_Face ft_face, FT_Face ft_other_face) { FcConfig *fc_config = FcConfigCreate(); FcConfigAppFontAddFile(fc_config, (const FcChar8 *)font_file_name); @@ -703,12 +717,10 @@ static void draw_glyphs(const unicode_blocks &blocks, cairo_t *cr, FT_Face ft_fa font_name = "Unknown"; } - cairo_surface_t *surface = cairo_get_target(cr); - int pageno = 1; - outline(surface, 0, pageno, font_name); + outline(cr, 0, pageno, font_name); - PangoLayout *layout = create_glyph_layout(cr, fc_config, fc_font); + auto layout = create_glyph_layout(cr, fc_config, fc_font); FT_UInt idx; FT_ULong charcode = get_first_char(ft_face, &idx); @@ -717,7 +729,7 @@ static void draw_glyphs(const unicode_blocks &blocks, cairo_t *cr, FT_Face ft_fa if (auto b = blocks.find_block(charcode); b.has_value()) { const auto block = *b; - outline(surface, 1, pageno, block.name); + outline(cr, 1, pageno, block.name); int npages = draw_unicode_block(cr, layout, ft_face, font_name, charcode, block, ft_other_face); pageno += npages; @@ -727,8 +739,6 @@ static void draw_glyphs(const unicode_blocks &blocks, cairo_t *cr, FT_Face ft_fa charcode = get_next_char(ft_face, charcode, &idx); } - g_object_unref(layout); - FcPatternDestroy(fc_pat); FcFontSetDestroy(fc_fontset); FcConfigDestroy(fc_config); @@ -776,29 +786,26 @@ static void usage(const char *cmd) */ static void init_table_fonts(void) { - /* FIXME is this correct? */ - PangoCairoFontMap *map = (PangoCairoFontMap *)pango_cairo_font_map_get_default(); - + // FIXME https://gitlab.gnome.org/GNOME/pangomm/-/issues/15 + auto map = reinterpret_cast(pango_cairo_font_map_get_default()); pango_cairo_font_map_set_resolution(map, POINTS_PER_INCH); - table_fonts.header = pango_font_description_from_string(get_style("header-font")); - table_fonts.font_name = pango_font_description_from_string(get_style("font-name-font")); - table_fonts.table_numbers = pango_font_description_from_string(get_style("table-numbers-font")); - table_fonts.cell_numbers = pango_font_description_from_string(get_style("cell-numbers-font")); + table_fonts.header = Pango::FontDescription(get_style("header-font")); + table_fonts.font_name = Pango::FontDescription(get_style("font-name-font")); + table_fonts.table_numbers = Pango::FontDescription(get_style("table-numbers-font")); + table_fonts.cell_numbers = Pango::FontDescription(get_style("cell-numbers-font")); } /* * Calculate various offsets. */ -static void calculate_offsets(cairo_t *cr) +static void calculate_offsets(Cairo::RefPtr &cr) { - PangoRectangle extents; /* Assume that vertical extents does not depend on actual text */ - PangoLayout *l = layout_text(cr, table_fonts.cell_numbers, "0123456789ABCDEF", &extents); - g_object_unref(l); + auto [layout, extents] = layout_text(cr, table_fonts.cell_numbers, "0123456789ABCDEF"); /* Unsolved mistery of pango's font metrics.... */ - double digits_ascent = pango_units_to_double(PANGO_DESCENT(extents)); - double digits_descent = -pango_units_to_double(PANGO_ASCENT(extents)); + double digits_ascent = pango_units_to_double(extents.get_descent()); + double digits_descent = -pango_units_to_double(extents.get_ascent()); cell_label_offset = digits_descent + 2; cell_glyph_bot_offset = cell_label_offset + digits_ascent + 2; @@ -809,20 +816,18 @@ static void calculate_offsets(cairo_t *cr) */ void calc_font_scaling(FT_Face ft_face) { - cairo_font_face_t *cr_face = cairo_ft_font_face_create_for_ft_face(ft_face, 0); - cairo_font_options_t *options = cairo_font_options_create(); + auto cr_face = Cairo::FtFontFace::create(ft_face, 0); + auto options = Cairo::FontOptions(); /* First create font with size 1 and measure it */ - cairo_matrix_t font_matrix; - cairo_matrix_init_identity(&font_matrix); - cairo_matrix_t ctm; - cairo_matrix_init_identity(&ctm); + auto font_matrix = Cairo::identity_matrix(); + const auto ctm = Cairo::identity_matrix(); /* Turn off rounding, so we can get real metrics */ - cairo_font_options_set_hint_metrics(options, CAIRO_HINT_METRICS_OFF); - cairo_scaled_font_t *cr_font = cairo_scaled_font_create(cr_face, &font_matrix, &ctm, options); - cairo_font_extents_t extents; - cairo_scaled_font_extents(cr_font, &extents); + options.set_hint_metrics(Cairo::FontOptions::HintMetrics::OFF); + auto cr_font = Cairo::ScaledFont::create(cr_face, font_matrix, ctm, options); + Cairo::FontExtents extents; + cr_font->get_extents(extents); /* Use some magic to find the best font size... */ double tgt_size = page_metrics::cell_height - cell_glyph_bot_offset - 2; @@ -838,27 +843,24 @@ void calc_font_scaling(FT_Face ft_face) } font_scale = tgt_size / act_size; - if (font_scale > 1) + if (font_scale > 1) { font_scale = trunc(font_scale); // just to make numbers nicer - if (font_scale > 20) - font_scale = 20; // Do not make font larger than in previous versions - - cairo_scaled_font_destroy(cr_font); + } + font_scale = min(font_scale, 20.0); // Do not make font larger than in previous versions /* Create the font once again, but this time scaled */ - cairo_matrix_init_scale(&font_matrix, font_scale, font_scale); - cr_font = cairo_scaled_font_create(cr_face, &font_matrix, &ctm, options); - cairo_scaled_font_extents(cr_font, &extents); + font_matrix = Cairo::scaling_matrix(font_scale, font_scale); + cr_font = Cairo::ScaledFont::create(cr_face, font_matrix, ctm, options); + cr_font->get_extents(extents); glyph_baseline_offset = (tgt_size - (extents.ascent + extents.descent)) / 2 + 2 + extents.ascent; - cairo_scaled_font_destroy(cr_font); } /* * Configure DPF surface metadata so fntsample can be used with * repeatable builds. */ -static void set_repeatable_pdf_metadata(cairo_surface_t *surface) +static void set_repeatable_pdf_metadata(Cairo::RefPtr &surface) { char *source_date_epoch = getenv("SOURCE_DATE_EPOCH"); @@ -875,7 +877,7 @@ static void set_repeatable_pdf_metadata(cairo_surface_t *surface) // TODO strftime(buffer, sizeof(buffer), "%Y-%m-%dT%H:%M:%SZ", build_time); - cairo_pdf_surface_set_metadata(surface, CAIRO_PDF_METADATA_CREATE_DATE, buffer); + cairo_pdf_surface_set_metadata(surface->cobj(), CAIRO_PDF_METADATA_CREATE_DATE, buffer); } } @@ -935,45 +937,30 @@ int main(int argc, char **argv) } } - cairo_surface_t *surface; + Pango::init(); + + Cairo::RefPtr surface; if (postscript_output) { - surface = cairo_ps_surface_create(output_file_name, page_metrics::page_width, - page_metrics::page_height); - } else if (svg_output) { - surface = cairo_svg_surface_create(output_file_name, page_metrics::page_width, + surface = Cairo::PsSurface::create(output_file_name, page_metrics::page_width, page_metrics::page_height); + } else if (svg_output) { + surface = Cairo::SvgSurface::create(output_file_name, page_metrics::page_width, + page_metrics::page_height); } else { - surface = cairo_pdf_surface_create(output_file_name, page_metrics::page_width, - page_metrics::page_height); + surface = Cairo::PdfSurface::create(output_file_name, page_metrics::page_width, + page_metrics::page_height); set_repeatable_pdf_metadata(surface); } - cairo_status_t cr_status = cairo_surface_status(surface); - if (cr_status != CAIRO_STATUS_SUCCESS) { - /* TRANSLATORS: 'cairo' is a name of a library, and should be left untranslated */ - fmt::print(cerr, _("{}: failed to create cairo surface: {}\n"), argv[0], - cairo_status_to_string(cr_status)); - exit(1); - } - - cairo_t *cr = cairo_create(surface); - cr_status = cairo_status(cr); - if (cr_status != CAIRO_STATUS_SUCCESS) { - fmt::print(cerr, _("{}: cairo_create failed: {}\n"), argv[0], - cairo_status_to_string(cr_status)); - exit(1); - } - - cairo_surface_destroy(surface); + auto cr = Cairo::Context::create(surface); init_table_fonts(); calculate_offsets(cr); - cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); + cr->set_source_rgb(0.0, 0.0, 0.0); calc_font_scaling(face); draw_glyphs(blocks, cr, face, other_face); - cairo_destroy(cr); return 0; } From c42336e2e994452bb28002a9324dce28e8064054 Mon Sep 17 00:00:00 2001 From: Ievgenii Meshcheriakov Date: Wed, 6 Oct 2021 23:54:40 +0200 Subject: [PATCH 7/9] Disable some errors --- src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5a260d2..74d32a5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -3,7 +3,7 @@ configure_file(fntsample.1.in fntsample.1 @ONLY) set( C_WARNING_FLAGS - -Wcast-align -Werror-implicit-function-declaration -Wchar-subscripts + -Wcast-align -Wchar-subscripts -Wall -W -Wpointer-arith -Wwrite-strings -Wformat-security -Wmissing-format-attribute -fno-common -Wundef CACHE STRING "Warning flags for C compiler" From 3b4396e17f9f49ff9c48c01db193bb79af7398dd Mon Sep 17 00:00:00 2001 From: Ievgenii Meshcheriakov Date: Thu, 7 Oct 2021 00:00:36 +0200 Subject: [PATCH 8/9] Don't try formatting char32_t --- src/gen_unicode_blocks.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/gen_unicode_blocks.cpp b/src/gen_unicode_blocks.cpp index 9b9237a..7d6e741 100644 --- a/src/gen_unicode_blocks.cpp +++ b/src/gen_unicode_blocks.cpp @@ -29,7 +29,8 @@ const unicode_blocks &get_static_blocks() { return static_blocks; } static void write_block(ostream &stream, const unicode_blocks::block &block) { fmt::print(stream, " unicode_blocks::block {{{{0x{:04x}, 0x{:04x}}}, \"{}\"}},\n", - block.r.start, block.r.end, block.name); + static_cast(block.r.start), static_cast(block.r.end), + block.name); } static void write_blocks(ostream &stream, const unicode_blocks &blocks) From 81dbecaf01ed8c9fc2d4e2379568694f3be0dd7b Mon Sep 17 00:00:00 2001 From: Ievgenii Meshcheriakov Date: Thu, 7 Oct 2021 00:06:33 +0200 Subject: [PATCH 9/9] Fix check --- src/fntsample.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fntsample.cpp b/src/fntsample.cpp index 9874347..c750f00 100644 --- a/src/fntsample.cpp +++ b/src/fntsample.cpp @@ -239,7 +239,7 @@ static optional parse_range(string_view s) auto minus = s.find('-'); - if (minus >= 0) { + if (minus != s.npos) { // minus found if (s.size() == 1) { return {};