From 21f1054d4718213c5b4e4abba7fe74979e93febe Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Wed, 4 Aug 2021 20:23:48 -0600 Subject: [PATCH 01/33] [util/hb-subset] Keep face around instead of font --- util/hb-subset.cc | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/util/hb-subset.cc b/util/hb-subset.cc index f05064245..ce10c2a72 100644 --- a/util/hb-subset.cc +++ b/util/hb-subset.cc @@ -37,12 +37,12 @@ struct subset_consumer_t { subset_consumer_t (option_parser_t *parser) - : failed (false), options (parser), subset_options (parser), font (nullptr), input (nullptr) {} + : failed (false), options (parser), subset_options (parser), face (nullptr), input (nullptr) {} void init (hb_buffer_t *buffer_, const font_options_t *font_opts) { - font = hb_font_reference (font_opts->get_font ()); + face = hb_face_reference (hb_font_get_face (font_opts->get_font ())); input = hb_subset_input_reference (subset_options.get_input ()); } @@ -55,7 +55,6 @@ struct subset_consumer_t hb_set_t *codepoints = hb_subset_input_unicode_set (input); if (0 == strcmp (text, "*")) { - hb_face_t *face = hb_font_get_face (font); hb_face_collect_unicodes (face, codepoints); return; } @@ -96,10 +95,9 @@ struct subset_consumer_t void finish (const font_options_t *font_opts) { - hb_face_t *face = hb_font_get_face (font); - hb_face_t *new_face = nullptr; - for (unsigned i = 0; i < subset_options.num_iterations; i++) { + for (unsigned i = 0; i < subset_options.num_iterations; i++) + { hb_face_destroy (new_face); new_face = hb_subset_or_fail (face, input); } @@ -114,7 +112,7 @@ struct subset_consumer_t hb_subset_input_destroy (input); hb_face_destroy (new_face); - hb_font_destroy (font); + hb_face_destroy (face); } public: @@ -123,7 +121,8 @@ struct subset_consumer_t private: output_options_t options; subset_options_t subset_options; - hb_font_t *font; + + hb_face_t *face; hb_subset_input_t *input; }; From e57dd6688937072ac1b5bf2cd45c8fb9e867abe1 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Thu, 5 Aug 2021 10:33:31 -0600 Subject: [PATCH 02/33] [util] Initialize struct members inline --- util/hb-ot-shape-closure.cc | 13 +-- util/hb-shape.cc | 20 ++-- util/hb-subset.cc | 10 +- util/options.hh | 212 +++++++++++++----------------------- util/shape-consumer.hh | 14 +-- util/view-cairo.hh | 20 ++-- 6 files changed, 109 insertions(+), 180 deletions(-) diff --git a/util/hb-ot-shape-closure.cc b/util/hb-ot-shape-closure.cc index 33f531b37..2e3ee2a33 100644 --- a/util/hb-ot-shape-closure.cc +++ b/util/hb-ot-shape-closure.cc @@ -32,9 +32,8 @@ struct shape_closure_consumer_t : option_group_t { - shape_closure_consumer_t (option_parser_t *parser) : - shaper (parser), - show_glyph_names (true) + shape_closure_consumer_t (option_parser_t *parser) + : shaper (parser) { add_options (parser); } @@ -104,11 +103,11 @@ struct shape_closure_consumer_t : option_group_t protected: shape_options_t shaper; - hb_bool_t show_glyph_names; + hb_bool_t show_glyph_names = false; - hb_set_t *glyphs; - hb_font_t *font; - hb_buffer_t *buffer; + hb_set_t *glyphs = nullptr; + hb_font_t *font = nullptr; + hb_buffer_t *buffer = nullptr; }; int diff --git a/util/hb-shape.cc b/util/hb-shape.cc index d6b6152e6..d2b86f1a8 100644 --- a/util/hb-shape.cc +++ b/util/hb-shape.cc @@ -31,13 +31,9 @@ struct output_buffer_t { output_buffer_t (option_parser_t *parser) - : options (parser, hb_buffer_serialize_list_formats ()), - format (parser), - gs (nullptr), - line_no (0), - font (nullptr), - output_format (HB_BUFFER_SERIALIZE_FORMAT_INVALID), - format_flags (HB_BUFFER_SERIALIZE_FLAG_DEFAULT) {} + : options (parser, hb_buffer_serialize_list_formats ()), + format (parser) + {} void init (hb_buffer_t *buffer, const font_options_t *font_opts) { @@ -147,11 +143,11 @@ struct output_buffer_t output_options_t options; format_options_t format; - GString *gs; - unsigned int line_no; - hb_font_t *font; - hb_buffer_serialize_format_t output_format; - hb_buffer_serialize_flags_t format_flags; + GString *gs = nullptr; + unsigned int line_no = 0; + hb_font_t *font = nullptr; + hb_buffer_serialize_format_t output_format = HB_BUFFER_SERIALIZE_FORMAT_INVALID; + hb_buffer_serialize_flags_t format_flags = HB_BUFFER_SERIALIZE_FLAG_DEFAULT; }; template diff --git a/util/hb-subset.cc b/util/hb-subset.cc index ce10c2a72..2d66d913c 100644 --- a/util/hb-subset.cc +++ b/util/hb-subset.cc @@ -37,7 +37,9 @@ struct subset_consumer_t { subset_consumer_t (option_parser_t *parser) - : failed (false), options (parser), subset_options (parser), face (nullptr), input (nullptr) {} + : options (parser), + subset_options (parser) + {} void init (hb_buffer_t *buffer_, const font_options_t *font_opts) @@ -116,14 +118,14 @@ struct subset_consumer_t } public: - bool failed; + bool failed = false; private: output_options_t options; subset_options_t subset_options; - hb_face_t *face; - hb_subset_input_t *input; + hb_face_t *face = nullptr; + hb_subset_input_t *input = nullptr; }; template diff --git a/util/options.hh b/util/options.hh index 11cd9b20b..802fec3bc 100644 --- a/util/options.hh +++ b/util/options.hh @@ -67,12 +67,10 @@ struct option_group_t struct option_parser_t { option_parser_t (const char *usage) + : usage_str (usage), + context (g_option_context_new (usage)), + to_free (g_ptr_array_new ()) { - memset (this, 0, sizeof (*this)); - usage_str = usage; - context = g_option_context_new (usage); - to_free = g_ptr_array_new (); - add_main_options (); } @@ -121,14 +119,6 @@ struct view_options_t : option_group_t { view_options_t (option_parser_t *parser) { - annotate = false; - fore = nullptr; - back = nullptr; - line_space = 0; - have_font_extents = false; - font_extents.ascent = font_extents.descent = font_extents.line_gap = 0; - margin.t = margin.r = margin.b = margin.l = DEFAULT_MARGIN; - add_options (parser); } ~view_options_t () override @@ -139,17 +129,17 @@ struct view_options_t : option_group_t void add_options (option_parser_t *parser) override; - hb_bool_t annotate; - char *fore; - char *back; - double line_space; - bool have_font_extents; + hb_bool_t annotate = false; + char *fore = nullptr; + char *back = nullptr; + double line_space = 0; + bool have_font_extents = false; struct font_extents_t { double ascent, descent, line_gap; - } font_extents; + } font_extents = {0., 0., 0.}; struct margin_t { double t, r, b, l; - } margin; + } margin = {DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN}; }; @@ -157,18 +147,6 @@ struct shape_options_t : option_group_t { shape_options_t (option_parser_t *parser) { - direction = language = script = nullptr; - bot = eot = preserve_default_ignorables = remove_default_ignorables = false; - features = nullptr; - num_features = 0; - shapers = nullptr; - utf8_clusters = false; - invisible_glyph = 0; - cluster_level = HB_BUFFER_CLUSTER_LEVEL_DEFAULT; - normalize_glyphs = false; - verify = false; - num_iterations = 1; - add_options (parser); } ~shape_options_t () override @@ -431,25 +409,25 @@ struct shape_options_t : option_group_t } /* Buffer properties */ - char *direction; - char *language; - char *script; + char *direction = nullptr; + char *language = nullptr; + char *script = nullptr; /* Buffer flags */ - hb_bool_t bot; - hb_bool_t eot; - hb_bool_t preserve_default_ignorables; - hb_bool_t remove_default_ignorables; + hb_bool_t bot = false; + hb_bool_t eot = false; + hb_bool_t preserve_default_ignorables = false; + hb_bool_t remove_default_ignorables = false; - hb_feature_t *features; - unsigned int num_features; - char **shapers; - hb_bool_t utf8_clusters; - hb_codepoint_t invisible_glyph; - hb_buffer_cluster_level_t cluster_level; - hb_bool_t normalize_glyphs; - hb_bool_t verify; - unsigned int num_iterations; + hb_feature_t *features = nullptr; + unsigned int num_features = 0; + char **shapers = nullptr; + hb_bool_t utf8_clusters = false; + hb_codepoint_t invisible_glyph = 0; + hb_buffer_cluster_level_t cluster_level = HB_BUFFER_CLUSTER_LEVEL_DEFAULT; + hb_bool_t normalize_glyphs = false; + hb_bool_t verify = false; + unsigned int num_iterations = 1; }; @@ -458,23 +436,11 @@ struct font_options_t : option_group_t font_options_t (option_parser_t *parser, int default_font_size_, unsigned int subpixel_bits_) + : default_font_size (default_font_size_), + subpixel_bits (subpixel_bits_), + font_size_x (default_font_size_), + font_size_y (default_font_size_) { - variations = nullptr; - num_variations = 0; - default_font_size = default_font_size_; - x_ppem = 0; - y_ppem = 0; - ptem = 0.; - subpixel_bits = subpixel_bits_; - font_file = nullptr; - face_index = 0; - font_size_x = font_size_y = default_font_size; - font_funcs = nullptr; - ft_load_flags = 2; - - blob = nullptr; - font = nullptr; - add_options (parser); } ~font_options_t () override @@ -489,23 +455,23 @@ struct font_options_t : option_group_t hb_font_t *get_font () const; - char *font_file; - mutable hb_blob_t *blob; - unsigned face_index; - hb_variation_t *variations; - unsigned int num_variations; - int default_font_size; - int x_ppem; - int y_ppem; - double ptem; - unsigned int subpixel_bits; - mutable double font_size_x; - mutable double font_size_y; - char *font_funcs; - int ft_load_flags; + char *font_file = nullptr; + mutable hb_blob_t *blob = nullptr; + unsigned face_index = 0; + hb_variation_t *variations = nullptr; + unsigned int num_variations = 0; + int default_font_size = FONT_SIZE_UPEM; + int x_ppem = 0; + int y_ppem = 0; + double ptem = 0.; + unsigned int subpixel_bits = 0; + mutable double font_size_x = FONT_SIZE_UPEM; + mutable double font_size_y = FONT_SIZE_UPEM; + char *font_funcs = nullptr; + int ft_load_flags = 2; private: - mutable hb_font_t *font; + mutable hb_font_t *font = nullptr; static struct cache_t { @@ -528,18 +494,6 @@ struct text_options_t : option_group_t { text_options_t (option_parser_t *parser) { - text_before = nullptr; - text_after = nullptr; - - text_len = -1; - text = nullptr; - text_file = nullptr; - - fp = nullptr; - gs = nullptr; - line = nullptr; - line_len = UINT_MAX; - add_options (parser); } ~text_options_t () override @@ -565,32 +519,26 @@ struct text_options_t : option_group_t const char *get_line (unsigned int *len, int eol = '\n'); - char *text_before; - char *text_after; + char *text_before = nullptr; + char *text_after = nullptr; - int text_len; - char *text; - char *text_file; + int text_len = -1; + char *text = nullptr; + char *text_file = nullptr; private: - FILE *fp; - GString *gs; - char *line; - unsigned int line_len; + FILE *fp = nullptr; + GString *gs = nullptr; + char *line = nullptr; + unsigned int line_len = UINT_MAX; }; struct output_options_t : option_group_t { output_options_t (option_parser_t *parser, const char **supported_formats_ = nullptr) + : supported_formats (supported_formats_) { - output_file = nullptr; - output_format = nullptr; - supported_formats = supported_formats_; - explicit_output_format = false; - - fp = nullptr; - add_options (parser); } ~output_options_t () override @@ -623,28 +571,17 @@ struct output_options_t : option_group_t FILE *get_file_handle (); - char *output_file; - char *output_format; - const char **supported_formats; - bool explicit_output_format; + char *output_file = nullptr; + char *output_format = nullptr; + const char **supported_formats = nullptr; + bool explicit_output_format = false; - mutable FILE *fp; + mutable FILE *fp = nullptr; }; struct format_options_t : option_group_t { format_options_t (option_parser_t *parser) { - show_glyph_names = true; - show_positions = true; - show_advances = true; - show_clusters = true; - show_text = false; - show_unicode = false; - show_line_num = false; - show_extents = false; - show_flags = false; - trace = false; - add_options (parser); } @@ -677,24 +614,23 @@ struct format_options_t : option_group_t GString *gs); - hb_bool_t show_glyph_names; - hb_bool_t show_positions; - hb_bool_t show_advances; - hb_bool_t show_clusters; - hb_bool_t show_text; - hb_bool_t show_unicode; - hb_bool_t show_line_num; - hb_bool_t show_extents; - hb_bool_t show_flags; - hb_bool_t trace; + hb_bool_t show_glyph_names = true; + hb_bool_t show_positions = true; + hb_bool_t show_advances = true; + hb_bool_t show_clusters = true; + hb_bool_t show_text = false; + hb_bool_t show_unicode = false; + hb_bool_t show_line_num = false; + hb_bool_t show_extents = false; + hb_bool_t show_flags = false; + hb_bool_t trace = false; }; struct subset_options_t : option_group_t { subset_options_t (option_parser_t *parser) + : input (hb_subset_input_create_or_fail ()) { - input = hb_subset_input_create_or_fail (); - num_iterations = 1; add_options (parser); } @@ -715,8 +651,6 @@ struct subset_options_t : option_group_t return &flags[sizeof(int) * 8 - 1]; } - unsigned num_iterations; - hb_subset_input_t * get_input () { hb_subset_flags_t flags_set = HB_SUBSET_FLAGS_DEFAULT; @@ -729,9 +663,9 @@ struct subset_options_t : option_group_t return input; } + unsigned num_iterations = 1; + hb_subset_input_t *input = nullptr; hb_bool_t flags[sizeof(int) * 8] = {0}; - - hb_subset_input_t *input; }; /* fallback implementation for scalbn()/scalbnf() for pre-2013 MSVC */ diff --git a/util/shape-consumer.hh b/util/shape-consumer.hh index da0d88033..eada0535b 100644 --- a/util/shape-consumer.hh +++ b/util/shape-consumer.hh @@ -35,11 +35,9 @@ template struct shape_consumer_t { shape_consumer_t (option_parser_t *parser) - : failed (false), - shaper (parser), - output (parser), - font (nullptr), - buffer (nullptr) {} + : shaper (parser), + output (parser) + {} void init (hb_buffer_t *buffer_, const font_options_t *font_opts) @@ -87,14 +85,14 @@ struct shape_consumer_t } public: - bool failed; + bool failed = false; protected: shape_options_t shaper; output_t output; - hb_font_t *font; - hb_buffer_t *buffer; + hb_font_t *font = nullptr; + hb_buffer_t *buffer = nullptr; }; diff --git a/util/view-cairo.hh b/util/view-cairo.hh index 1f51f0e93..af58f15db 100644 --- a/util/view-cairo.hh +++ b/util/view-cairo.hh @@ -35,11 +35,11 @@ struct view_cairo_t { view_cairo_t (option_parser_t *parser) - : output_options (parser, helper_cairo_supported_formats), - view_options (parser), - direction (HB_DIRECTION_INVALID), - lines (0), scale_bits (0) {} - ~view_cairo_t () { + : output_options (parser, helper_cairo_supported_formats), + view_options (parser) + {} + ~view_cairo_t () + { cairo_debug_reset_static_data (); } @@ -82,14 +82,14 @@ struct view_cairo_t protected: + void render (const font_options_t *font_opts); + output_options_t output_options; view_options_t view_options; - void render (const font_options_t *font_opts); - - hb_direction_t direction; // Remove this, make segment_properties accessible - GArray *lines; - int scale_bits; + hb_direction_t direction = HB_DIRECTION_INVALID; // Remove this, make segment_properties accessible + GArray *lines = nullptr; + int scale_bits = 0; }; #endif From e109f9a6f9954f55a7ca2f336aa41a0235dca37b Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Thu, 5 Aug 2021 11:05:51 -0600 Subject: [PATCH 03/33] [util] Collect cmdline arg options outside of constructor --- util/hb-ot-shape-closure.cc | 8 ++------ util/hb-shape.cc | 11 ++++++++--- util/hb-subset.cc | 13 +++++++------ util/main-font-text.hh | 16 +++++++++++----- util/options.hh | 37 ++++++------------------------------- util/shape-consumer.hh | 9 +++++---- util/view-cairo.hh | 11 ++++++++--- 7 files changed, 47 insertions(+), 58 deletions(-) diff --git a/util/hb-ot-shape-closure.cc b/util/hb-ot-shape-closure.cc index 2e3ee2a33..7996c11f3 100644 --- a/util/hb-ot-shape-closure.cc +++ b/util/hb-ot-shape-closure.cc @@ -32,14 +32,10 @@ struct shape_closure_consumer_t : option_group_t { - shape_closure_consumer_t (option_parser_t *parser) - : shaper (parser) - { - add_options (parser); - } - void add_options (struct option_parser_t *parser) override { + shaper.add_options (parser); + GOptionEntry entries[] = { {"no-glyph-names", 0, G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &this->show_glyph_names, "Use glyph indices instead of names", nullptr}, diff --git a/util/hb-shape.cc b/util/hb-shape.cc index d2b86f1a8..8cf1901a2 100644 --- a/util/hb-shape.cc +++ b/util/hb-shape.cc @@ -30,11 +30,16 @@ struct output_buffer_t { - output_buffer_t (option_parser_t *parser) - : options (parser, hb_buffer_serialize_list_formats ()), - format (parser) + output_buffer_t () + : options (hb_buffer_serialize_list_formats ()) {} + void add_options (option_parser_t *parser) + { + options.add_options (parser); + format.add_options (parser); + } + void init (hb_buffer_t *buffer, const font_options_t *font_opts) { options.get_file_handle (); diff --git a/util/hb-subset.cc b/util/hb-subset.cc index 2d66d913c..4ff193137 100644 --- a/util/hb-subset.cc +++ b/util/hb-subset.cc @@ -36,10 +36,11 @@ struct subset_consumer_t { - subset_consumer_t (option_parser_t *parser) - : options (parser), - subset_options (parser) - {} + void add_options (option_parser_t *parser) + { + output_options.add_options (parser); + subset_options.add_options (parser); + } void init (hb_buffer_t *buffer_, const font_options_t *font_opts) @@ -108,7 +109,7 @@ struct subset_consumer_t if (!failed) { hb_blob_t *result = hb_face_reference_blob (new_face); - write_file (options.output_file, result); + write_file (output_options.output_file, result); hb_blob_destroy (result); } @@ -121,7 +122,7 @@ struct subset_consumer_t bool failed = false; private: - output_options_t options; + output_options_t output_options; subset_options_t subset_options; hb_face_t *face = nullptr; diff --git a/util/main-font-text.hh b/util/main-font-text.hh index 7915c3786..ddeab3da9 100644 --- a/util/main-font-text.hh +++ b/util/main-font-text.hh @@ -51,14 +51,21 @@ template struct shape_consumer_t { - shape_consumer_t (option_parser_t *parser) - : shaper (parser), - output (parser) - {} + void add_options (option_parser_t *parser) + { + shaper.add_options (parser); + output.add_options (parser); + } void init (hb_buffer_t *buffer_, const font_options_t *font_opts) diff --git a/util/view-cairo.hh b/util/view-cairo.hh index af58f15db..dbd99670c 100644 --- a/util/view-cairo.hh +++ b/util/view-cairo.hh @@ -34,15 +34,20 @@ struct view_cairo_t { - view_cairo_t (option_parser_t *parser) - : output_options (parser, helper_cairo_supported_formats), - view_options (parser) + view_cairo_t () + : output_options (helper_cairo_supported_formats) {} ~view_cairo_t () { cairo_debug_reset_static_data (); } + void add_options (option_parser_t *parser) + { + output_options.add_options (parser); + view_options.add_options (parser); + } + void init (hb_buffer_t *buffer, const font_options_t *font_opts) { lines = g_array_new (false, false, sizeof (helper_cairo_line_t)); From 09e4d7dd306ceb58064b92ec3cfc3ad7900a7802 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Thu, 5 Aug 2021 11:21:20 -0600 Subject: [PATCH 04/33] [util] Simplify output_options_t --- util/hb-ot-shape-closure.cc | 2 +- util/hb-shape.cc | 9 +++------ util/options.cc | 3 ++- util/options.hh | 21 ++++++++------------- util/view-cairo.hh | 5 +---- 5 files changed, 15 insertions(+), 25 deletions(-) diff --git a/util/hb-ot-shape-closure.cc b/util/hb-ot-shape-closure.cc index 7996c11f3..5930cdc2c 100644 --- a/util/hb-ot-shape-closure.cc +++ b/util/hb-ot-shape-closure.cc @@ -32,7 +32,7 @@ struct shape_closure_consumer_t : option_group_t { - void add_options (struct option_parser_t *parser) override + void add_options (struct option_parser_t *parser) { shaper.add_options (parser); diff --git a/util/hb-shape.cc b/util/hb-shape.cc index 8cf1901a2..eb5ec4b0d 100644 --- a/util/hb-shape.cc +++ b/util/hb-shape.cc @@ -30,13 +30,9 @@ struct output_buffer_t { - output_buffer_t () - : options (hb_buffer_serialize_list_formats ()) - {} - void add_options (option_parser_t *parser) { - options.add_options (parser); + options.add_options (parser, hb_buffer_serialize_list_formats ()); format.add_options (parser); } @@ -59,7 +55,7 @@ struct output_buffer_t if (options.explicit_output_format) fail (false, "Unknown output format `%s'; supported formats are: %s", options.output_format, - g_strjoinv ("/", const_cast (options.supported_formats))); + g_strjoinv ("/", const_cast (hb_buffer_serialize_list_formats ()))); else /* Just default to TEXT if not explicitly requested and the * file extension is not recognized. */ @@ -145,6 +141,7 @@ struct output_buffer_t protected: + output_options_t options; format_options_t format; diff --git a/util/options.cc b/util/options.cc index 94595a8a7..7a31b799d 100644 --- a/util/options.cc +++ b/util/options.cc @@ -653,7 +653,8 @@ text_options_t::add_options (option_parser_t *parser) } void -output_options_t::add_options (option_parser_t *parser) +output_options_t::add_options (option_parser_t *parser, + const char **supported_formats) { const char *text; diff --git a/util/options.hh b/util/options.hh index 9925e26b6..eab576cf6 100644 --- a/util/options.hh +++ b/util/options.hh @@ -57,8 +57,6 @@ struct option_group_t { virtual ~option_group_t () {} - virtual void add_options (struct option_parser_t *parser) = 0; - virtual void pre_parse (GError **error G_GNUC_UNUSED) {} virtual void post_parse (GError **error G_GNUC_UNUSED) {} }; @@ -123,7 +121,7 @@ struct view_options_t : option_group_t g_free (back); } - void add_options (option_parser_t *parser) override; + void add_options (option_parser_t *parser); hb_bool_t annotate = false; char *fore = nullptr; @@ -150,7 +148,7 @@ struct shape_options_t : option_group_t g_strfreev (shapers); } - void add_options (option_parser_t *parser) override; + void add_options (option_parser_t *parser); void setup_buffer (hb_buffer_t *buffer) { @@ -440,7 +438,7 @@ struct font_options_t : option_group_t hb_font_destroy (font); } - void add_options (option_parser_t *parser) override; + void add_options (option_parser_t *parser); hb_font_t *get_font () const; @@ -493,7 +491,7 @@ struct text_options_t : option_group_t fclose (fp); } - void add_options (option_parser_t *parser) override; + void add_options (option_parser_t *parser); void post_parse (GError **error G_GNUC_UNUSED) override { if (text && text_file) @@ -520,9 +518,6 @@ struct text_options_t : option_group_t struct output_options_t : option_group_t { - output_options_t (const char **supported_formats_ = nullptr) - : supported_formats (supported_formats_) - {} ~output_options_t () override { g_free (output_file); @@ -531,7 +526,8 @@ struct output_options_t : option_group_t fclose (fp); } - void add_options (option_parser_t *parser) override; + void add_options (option_parser_t *parser, + const char **supported_formats = nullptr); void post_parse (GError **error G_GNUC_UNUSED) override { @@ -555,7 +551,6 @@ struct output_options_t : option_group_t char *output_file = nullptr; char *output_format = nullptr; - const char **supported_formats = nullptr; bool explicit_output_format = false; mutable FILE *fp = nullptr; @@ -563,7 +558,7 @@ struct output_options_t : option_group_t struct format_options_t : option_group_t { - void add_options (option_parser_t *parser) override; + void add_options (option_parser_t *parser); void serialize (hb_buffer_t *buffer, hb_font_t *font, @@ -614,7 +609,7 @@ struct subset_options_t : option_group_t hb_subset_input_destroy (input); } - void add_options (option_parser_t *parser) override; + void add_options (option_parser_t *parser); hb_bool_t* bool_for(hb_subset_flags_t flag) { diff --git a/util/view-cairo.hh b/util/view-cairo.hh index dbd99670c..622bbd530 100644 --- a/util/view-cairo.hh +++ b/util/view-cairo.hh @@ -34,9 +34,6 @@ struct view_cairo_t { - view_cairo_t () - : output_options (helper_cairo_supported_formats) - {} ~view_cairo_t () { cairo_debug_reset_static_data (); @@ -44,7 +41,7 @@ struct view_cairo_t void add_options (option_parser_t *parser) { - output_options.add_options (parser); + output_options.add_options (parser, helper_cairo_supported_formats); view_options.add_options (parser); } From fe90fbeba3b9ddca20e1ecc0feb99ddb8132f961 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Thu, 5 Aug 2021 11:22:20 -0600 Subject: [PATCH 05/33] [util] Remove unused pre_parse callback logic --- util/options.cc | 13 +------------ util/options.hh | 1 - 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/util/options.cc b/util/options.cc index 7a31b799d..19d8a1c8d 100644 --- a/util/options.cc +++ b/util/options.cc @@ -108,17 +108,6 @@ option_parser_t::add_main_options () g_option_context_add_main_entries (context, entries, nullptr); } -static gboolean -pre_parse (GOptionContext *context G_GNUC_UNUSED, - GOptionGroup *group G_GNUC_UNUSED, - gpointer data, - GError **error) -{ - option_group_t *option_group = (option_group_t *) data; - option_group->pre_parse (error); - return !*error; -} - static gboolean post_parse (GOptionContext *context G_GNUC_UNUSED, GOptionGroup *group G_GNUC_UNUSED, @@ -140,7 +129,7 @@ option_parser_t::add_group (GOptionEntry *entries, GOptionGroup *group = g_option_group_new (name, description, help_description, static_cast(option_group), nullptr); g_option_group_add_entries (group, entries); - g_option_group_set_parse_hooks (group, pre_parse, post_parse); + g_option_group_set_parse_hooks (group, nullptr, post_parse); g_option_context_add_group (context, group); } diff --git a/util/options.hh b/util/options.hh index eab576cf6..1062eef20 100644 --- a/util/options.hh +++ b/util/options.hh @@ -57,7 +57,6 @@ struct option_group_t { virtual ~option_group_t () {} - virtual void pre_parse (GError **error G_GNUC_UNUSED) {} virtual void post_parse (GError **error G_GNUC_UNUSED) {} }; From 3362b48f5a5eee9fe6378787ffce3304b341e474 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Thu, 5 Aug 2021 11:30:39 -0600 Subject: [PATCH 06/33] [util] Templatize add_group logic --- util/options.cc | 25 ------------------------- util/options.hh | 22 +++++++++++++++++++++- 2 files changed, 21 insertions(+), 26 deletions(-) diff --git a/util/options.cc b/util/options.cc index 19d8a1c8d..14f04b8f1 100644 --- a/util/options.cc +++ b/util/options.cc @@ -108,31 +108,6 @@ option_parser_t::add_main_options () g_option_context_add_main_entries (context, entries, nullptr); } -static gboolean -post_parse (GOptionContext *context G_GNUC_UNUSED, - GOptionGroup *group G_GNUC_UNUSED, - gpointer data, - GError **error) -{ - option_group_t *option_group = static_cast(data); - option_group->post_parse (error); - return !*error; -} - -void -option_parser_t::add_group (GOptionEntry *entries, - const gchar *name, - const gchar *description, - const gchar *help_description, - option_group_t *option_group) -{ - GOptionGroup *group = g_option_group_new (name, description, help_description, - static_cast(option_group), nullptr); - g_option_group_add_entries (group, entries); - g_option_group_set_parse_hooks (group, nullptr, post_parse); - g_option_context_add_group (context, group); -} - void option_parser_t::parse (int *argc, char ***argv) { diff --git a/util/options.hh b/util/options.hh index 1062eef20..d738d2473 100644 --- a/util/options.hh +++ b/util/options.hh @@ -82,11 +82,31 @@ struct option_parser_t void add_main_options (); + template + static gboolean + post_parse (GOptionContext *context G_GNUC_UNUSED, + GOptionGroup *group G_GNUC_UNUSED, + gpointer data, + GError **error) + { + auto *thiz = static_cast(data); + thiz->post_parse (error); + return !*error; + } + + template void add_group (GOptionEntry *entries, const gchar *name, const gchar *description, const gchar *help_description, - option_group_t *option_group); + Type *closure) + { + GOptionGroup *group = g_option_group_new (name, description, help_description, + static_cast(closure), nullptr); + g_option_group_add_entries (group, entries); + g_option_group_set_parse_hooks (group, nullptr, post_parse); + g_option_context_add_group (context, group); + } void free_later (char *p) { g_ptr_array_add (to_free, p); From 8588173ee6ace5926e090120fbcad85e5be1fab5 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Thu, 5 Aug 2021 11:43:25 -0600 Subject: [PATCH 07/33] [util] Rework post_parse() handling --- util/options.hh | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/util/options.hh b/util/options.hh index d738d2473..21405e339 100644 --- a/util/options.hh +++ b/util/options.hh @@ -56,8 +56,6 @@ void fail (hb_bool_t suggest_help, const char *format, ...) G_GNUC_NORETURN G_GN struct option_group_t { virtual ~option_group_t () {} - - virtual void post_parse (GError **error G_GNUC_UNUSED) {} }; @@ -82,6 +80,12 @@ struct option_parser_t void add_main_options (); + static void + post_parse_ (void *thiz, GError **error) {} + template + static auto + post_parse_ (Type *thiz, GError **error) -> decltype (thiz->post_parse (error)) + { thiz->post_parse (error); } template static gboolean post_parse (GOptionContext *context G_GNUC_UNUSED, @@ -89,8 +93,7 @@ struct option_parser_t gpointer data, GError **error) { - auto *thiz = static_cast(data); - thiz->post_parse (error); + option_parser_t::post_parse_ (static_cast (data), error); return !*error; } @@ -512,7 +515,8 @@ struct text_options_t : option_group_t void add_options (option_parser_t *parser); - void post_parse (GError **error G_GNUC_UNUSED) override { + void post_parse (GError **error G_GNUC_UNUSED) + { if (text && text_file) g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, @@ -548,7 +552,7 @@ struct output_options_t : option_group_t void add_options (option_parser_t *parser, const char **supported_formats = nullptr); - void post_parse (GError **error G_GNUC_UNUSED) override + void post_parse (GError **error G_GNUC_UNUSED) { if (output_format) explicit_output_format = true; From 463411a1de4b9ca2e267a1ce0f9667da17a73d1e Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Thu, 5 Aug 2021 11:44:35 -0600 Subject: [PATCH 08/33] [util] Remove unused option_group_t --- util/hb-ot-shape-closure.cc | 2 +- util/options.hh | 31 +++++++++++++------------------ 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/util/hb-ot-shape-closure.cc b/util/hb-ot-shape-closure.cc index 5930cdc2c..14c4663ac 100644 --- a/util/hb-ot-shape-closure.cc +++ b/util/hb-ot-shape-closure.cc @@ -30,7 +30,7 @@ #include #endif -struct shape_closure_consumer_t : option_group_t +struct shape_closure_consumer_t { void add_options (struct option_parser_t *parser) { diff --git a/util/options.hh b/util/options.hh index 21405e339..5bf9224de 100644 --- a/util/options.hh +++ b/util/options.hh @@ -53,11 +53,6 @@ void fail (hb_bool_t suggest_help, const char *format, ...) G_GNUC_NORETURN G_GNUC_PRINTF (2, 3); -struct option_group_t -{ - virtual ~option_group_t () {} -}; - struct option_parser_t { @@ -135,9 +130,9 @@ struct option_parser_t #define FONT_SIZE_UPEM 0x7FFFFFFF #define FONT_SIZE_NONE 0 -struct view_options_t : option_group_t +struct view_options_t { - ~view_options_t () override + ~view_options_t () { g_free (fore); g_free (back); @@ -159,9 +154,9 @@ struct view_options_t : option_group_t }; -struct shape_options_t : option_group_t +struct shape_options_t { - ~shape_options_t () override + ~shape_options_t () { g_free (direction); g_free (language); @@ -443,7 +438,7 @@ struct shape_options_t : option_group_t }; -struct font_options_t : option_group_t +struct font_options_t { font_options_t (int default_font_size_, unsigned int subpixel_bits_) @@ -452,7 +447,7 @@ struct font_options_t : option_group_t font_size_x (default_font_size_), font_size_y (default_font_size_) {} - ~font_options_t () override + ~font_options_t () { g_free (font_file); free (variations); @@ -499,9 +494,9 @@ struct font_options_t : option_group_t }; -struct text_options_t : option_group_t +struct text_options_t { - ~text_options_t () override + ~text_options_t () { g_free (text_before); g_free (text_after); @@ -539,9 +534,9 @@ struct text_options_t : option_group_t unsigned int line_len = UINT_MAX; }; -struct output_options_t : option_group_t +struct output_options_t { - ~output_options_t () override + ~output_options_t () { g_free (output_file); g_free (output_format); @@ -579,7 +574,7 @@ struct output_options_t : option_group_t mutable FILE *fp = nullptr; }; -struct format_options_t : option_group_t +struct format_options_t { void add_options (option_parser_t *parser); @@ -622,12 +617,12 @@ struct format_options_t : option_group_t hb_bool_t trace = false; }; -struct subset_options_t : option_group_t +struct subset_options_t { subset_options_t () : input (hb_subset_input_create_or_fail ()) {} - ~subset_options_t () override + ~subset_options_t () { hb_subset_input_destroy (input); } From e014c6fa2d4b1cb341d065d582d1dd995c04e5b5 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Thu, 5 Aug 2021 13:24:59 -0600 Subject: [PATCH 09/33] [util/main-font-text] Move eol from template arg to main() arg --- util/hb-shape.cc | 11 +++++------ util/hb-subset.cc | 11 +++++------ util/main-font-text.hh | 4 ++-- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/util/hb-shape.cc b/util/hb-shape.cc index eb5ec4b0d..c8eac5d00 100644 --- a/util/hb-shape.cc +++ b/util/hb-shape.cc @@ -152,12 +152,11 @@ struct output_buffer_t hb_buffer_serialize_flags_t format_flags = HB_BUFFER_SERIALIZE_FLAG_DEFAULT; }; -template -using driver_t = main_font_text_t, FONT_SIZE_UPEM, 0, eol>; - int main (int argc, char **argv) { + using driver_t = main_font_text_t, FONT_SIZE_UPEM, 0>; + if (argc == 2 && !strcmp (argv[1], "--batch")) { unsigned int ret = 0; @@ -182,13 +181,13 @@ main (int argc, char **argv) start_offset = argc == 2 && p[0] != '\0' && p[0] != ':' && p[1] == ':' && (p[2] == '\\' || p[2] == '/') ? 2 : 0; } - driver_t driver; - ret |= driver.main (argc, args); + driver_t driver; + ret |= driver.main (argc, args, EOF); fflush (stdout); } return ret; } - driver_t<> driver; + driver_t driver; return driver.main (argc, argv); } diff --git a/util/hb-subset.cc b/util/hb-subset.cc index 4ff193137..1420785ac 100644 --- a/util/hb-subset.cc +++ b/util/hb-subset.cc @@ -129,12 +129,11 @@ struct subset_consumer_t hb_subset_input_t *input = nullptr; }; -template -using driver_t = main_font_text_t; - int main (int argc, char **argv) { + using driver_t = main_font_text_t; + if (argc == 2 && !strcmp (argv[1], "--batch")) { unsigned int ret = 0; @@ -157,8 +156,8 @@ main (int argc, char **argv) args[argc++] = p = e; } - driver_t driver; - int result = driver.main (argc, args); + driver_t driver; + int result = driver.main (argc, args, EOF); fprintf (stdout, result == 0 ? "success\n" : "failure\n"); fflush (stdout); ret |= result; @@ -166,6 +165,6 @@ main (int argc, char **argv) return ret; } - driver_t<> driver; + driver_t driver; return driver.main (argc, argv); } diff --git a/util/main-font-text.hh b/util/main-font-text.hh index ddeab3da9..bbd6c28db 100644 --- a/util/main-font-text.hh +++ b/util/main-font-text.hh @@ -47,7 +47,7 @@ locale_to_utf8 (char *s) return t; } -template +template struct main_font_text_t { main_font_text_t () @@ -62,7 +62,7 @@ struct main_font_text_t } int - main (int argc, char **argv) + main (int argc, char **argv, int eol = '\n') { option_parser_t options ("[FONT-FILE] [TEXT]"); add_options (&options); From fc0339eef0fa302f74ca9a3b90430288dac29df3 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Thu, 5 Aug 2021 13:35:46 -0600 Subject: [PATCH 10/33] [util] Simplify shape-consumer by removing buffer arg --- util/hb-ot-shape-closure.cc | 5 ++--- util/hb-subset.cc | 3 +-- util/main-font-text.hh | 4 +--- util/shape-consumer.hh | 5 ++--- 4 files changed, 6 insertions(+), 11 deletions(-) diff --git a/util/hb-ot-shape-closure.cc b/util/hb-ot-shape-closure.cc index 14c4663ac..9886bdad5 100644 --- a/util/hb-ot-shape-closure.cc +++ b/util/hb-ot-shape-closure.cc @@ -48,13 +48,12 @@ struct shape_closure_consumer_t this); } - void init (hb_buffer_t *buffer_, - const font_options_t *font_opts) + void init (const font_options_t *font_opts) { glyphs = hb_set_create (); font = hb_font_reference (font_opts->get_font ()); failed = false; - buffer = hb_buffer_reference (buffer_); + buffer = hb_buffer_create (); } void consume_line (const char *text, unsigned int text_len, diff --git a/util/hb-subset.cc b/util/hb-subset.cc index 1420785ac..b88f5c415 100644 --- a/util/hb-subset.cc +++ b/util/hb-subset.cc @@ -42,8 +42,7 @@ struct subset_consumer_t subset_options.add_options (parser); } - void init (hb_buffer_t *buffer_, - const font_options_t *font_opts) + void init (const font_options_t *font_opts) { face = hb_face_reference (hb_font_get_face (font_opts->get_font ())); input = hb_subset_input_reference (subset_options.get_input ()); diff --git a/util/main-font-text.hh b/util/main-font-text.hh index bbd6c28db..8e012d288 100644 --- a/util/main-font-text.hh +++ b/util/main-font-text.hh @@ -78,9 +78,7 @@ struct main_font_text_t if (!input.text && !input.text_file) input.text_file = g_strdup ("-"); - hb_buffer_t *buffer = hb_buffer_create (); - consumer.init (buffer, &font_opts); - hb_buffer_destroy (buffer); + consumer.init (&font_opts); unsigned int text_len; const char *text; diff --git a/util/shape-consumer.hh b/util/shape-consumer.hh index 1d1dbdfde..6a0d767c0 100644 --- a/util/shape-consumer.hh +++ b/util/shape-consumer.hh @@ -40,12 +40,11 @@ struct shape_consumer_t output.add_options (parser); } - void init (hb_buffer_t *buffer_, - const font_options_t *font_opts) + void init (const font_options_t *font_opts) { font = hb_font_reference (font_opts->get_font ()); failed = false; - buffer = hb_buffer_reference (buffer_); + buffer = hb_buffer_create (); output.init (buffer, font_opts); } From 71440dbd90d77b2af35c69fac90e9342e9a9e439 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Thu, 5 Aug 2021 13:48:59 -0600 Subject: [PATCH 11/33] [util] Move font-size and upem to be extern variables --- util/hb-ot-shape-closure.cc | 5 ++++- util/hb-shape.cc | 5 ++++- util/hb-subset.cc | 5 ++++- util/hb-view.cc | 6 +++--- util/main-font-text.hh | 6 +----- util/options.cc | 6 +++--- util/options.hh | 17 ++++++----------- 7 files changed, 25 insertions(+), 25 deletions(-) diff --git a/util/hb-ot-shape-closure.cc b/util/hb-ot-shape-closure.cc index 9886bdad5..a21754940 100644 --- a/util/hb-ot-shape-closure.cc +++ b/util/hb-ot-shape-closure.cc @@ -26,6 +26,9 @@ #include "main-font-text.hh" +const unsigned DEFAULT_FONT_SIZE = FONT_SIZE_NONE; +const unsigned SUBPIXEL_BITS = 0; + #ifdef HAVE_FREETYPE #include #endif @@ -108,6 +111,6 @@ struct shape_closure_consumer_t int main (int argc, char **argv) { - main_font_text_t driver; + main_font_text_t driver; return driver.main (argc, argv); } diff --git a/util/hb-shape.cc b/util/hb-shape.cc index c8eac5d00..91d4489d2 100644 --- a/util/hb-shape.cc +++ b/util/hb-shape.cc @@ -28,6 +28,9 @@ #include "main-font-text.hh" #include "shape-consumer.hh" +const unsigned DEFAULT_FONT_SIZE = FONT_SIZE_UPEM; +const unsigned SUBPIXEL_BITS = 0; + struct output_buffer_t { void add_options (option_parser_t *parser) @@ -155,7 +158,7 @@ struct output_buffer_t int main (int argc, char **argv) { - using driver_t = main_font_text_t, FONT_SIZE_UPEM, 0>; + using driver_t = main_font_text_t>; if (argc == 2 && !strcmp (argv[1], "--batch")) { diff --git a/util/hb-subset.cc b/util/hb-subset.cc index b88f5c415..7bd8ab1eb 100644 --- a/util/hb-subset.cc +++ b/util/hb-subset.cc @@ -30,6 +30,9 @@ #include "main-font-text.hh" #include "hb-subset.h" +const unsigned DEFAULT_FONT_SIZE = FONT_SIZE_NONE; +const unsigned SUBPIXEL_BITS = 0; + /* * Command line interface to the harfbuzz font subsetter. */ @@ -131,7 +134,7 @@ struct subset_consumer_t int main (int argc, char **argv) { - using driver_t = main_font_text_t; + using driver_t = main_font_text_t; if (argc == 2 && !strcmp (argv[1], "--batch")) { diff --git a/util/hb-view.cc b/util/hb-view.cc index 69a4c9504..8289b6d1b 100644 --- a/util/hb-view.cc +++ b/util/hb-view.cc @@ -29,12 +29,12 @@ #include "shape-consumer.hh" #include "view-cairo.hh" -#define DEFAULT_FONT_SIZE 256 -#define SUBPIXEL_BITS 6 +const unsigned DEFAULT_FONT_SIZE = 256; +const unsigned SUBPIXEL_BITS = 6; int main (int argc, char **argv) { - main_font_text_t, DEFAULT_FONT_SIZE, SUBPIXEL_BITS> driver; + main_font_text_t> driver; return driver.main (argc, argv); } diff --git a/util/main-font-text.hh b/util/main-font-text.hh index 8e012d288..b75fc551b 100644 --- a/util/main-font-text.hh +++ b/util/main-font-text.hh @@ -47,13 +47,9 @@ locale_to_utf8 (char *s) return t; } -template +template struct main_font_text_t { - main_font_text_t () - : font_opts (default_font_size, subpixel_bits) - {} - void add_options (option_parser_t *parser) { font_opts.add_options (parser); diff --git a/util/options.cc b/util/options.cc index 14f04b8f1..9e6548e13 100644 --- a/util/options.cc +++ b/util/options.cc @@ -547,11 +547,11 @@ font_options_t::add_options (option_parser_t *parser) } char *font_size_text; - if (default_font_size == FONT_SIZE_UPEM) + if (DEFAULT_FONT_SIZE == FONT_SIZE_UPEM) font_size_text = (char *) "Font size (default: upem)"; else { - font_size_text = g_strdup_printf ("Font size (default: %d)", default_font_size); + font_size_text = g_strdup_printf ("Font size (default: %d)", DEFAULT_FONT_SIZE); parser->free_later (font_size_text); } @@ -559,7 +559,7 @@ font_options_t::add_options (option_parser_t *parser) { {"font-file", 0, 0, G_OPTION_ARG_STRING, &this->font_file, "Set font file-name", "filename"}, {"face-index", 0, 0, G_OPTION_ARG_INT, &this->face_index, "Set face index (default: 0)", "index"}, - {"font-size", 0, default_font_size ? 0 : G_OPTION_FLAG_HIDDEN, + {"font-size", 0, DEFAULT_FONT_SIZE ? 0 : G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_font_size, font_size_text, "1/2 integers or 'upem'"}, {"font-ppem", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_font_ppem, "Set x,y pixels per EM (default: 0; disabled)", "1/2 integers"}, {"font-ptem", 0, 0, G_OPTION_ARG_DOUBLE, &this->ptem, "Set font point-size (default: 0; disabled)", "point-size"}, diff --git a/util/options.hh b/util/options.hh index 5bf9224de..c91028a95 100644 --- a/util/options.hh +++ b/util/options.hh @@ -130,6 +130,9 @@ struct option_parser_t #define FONT_SIZE_UPEM 0x7FFFFFFF #define FONT_SIZE_NONE 0 +extern const unsigned DEFAULT_FONT_SIZE; +extern const unsigned SUBPIXEL_BITS; + struct view_options_t { ~view_options_t () @@ -440,13 +443,6 @@ struct shape_options_t struct font_options_t { - font_options_t (int default_font_size_, - unsigned int subpixel_bits_) - : default_font_size (default_font_size_), - subpixel_bits (subpixel_bits_), - font_size_x (default_font_size_), - font_size_y (default_font_size_) - {} ~font_options_t () { g_free (font_file); @@ -464,13 +460,12 @@ struct font_options_t unsigned face_index = 0; hb_variation_t *variations = nullptr; unsigned int num_variations = 0; - int default_font_size = FONT_SIZE_UPEM; int x_ppem = 0; int y_ppem = 0; double ptem = 0.; - unsigned int subpixel_bits = 0; - mutable double font_size_x = FONT_SIZE_UPEM; - mutable double font_size_y = FONT_SIZE_UPEM; + unsigned int subpixel_bits = SUBPIXEL_BITS; + mutable double font_size_x = DEFAULT_FONT_SIZE; + mutable double font_size_y = DEFAULT_FONT_SIZE; char *font_funcs = nullptr; int ft_load_flags = 2; From b3a2f2bfcf483b525c55de211e3c644dba860e51 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Thu, 5 Aug 2021 14:03:48 -0600 Subject: [PATCH 12/33] [util/main-font-text] Simplify --- util/main-font-text.hh | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/util/main-font-text.hh b/util/main-font-text.hh index b75fc551b..2bca9251c 100644 --- a/util/main-font-text.hh +++ b/util/main-font-text.hh @@ -50,18 +50,18 @@ locale_to_utf8 (char *s) template struct main_font_text_t { - void add_options (option_parser_t *parser) - { - font_opts.add_options (parser); - input.add_options (parser); - consumer.add_options (parser); - } - int main (int argc, char **argv, int eol = '\n') { + + font_options_t font_opts; + text_options_t input; + consumer_t consumer; + option_parser_t options ("[FONT-FILE] [TEXT]"); - add_options (&options); + font_opts.add_options (&options); + input.add_options (&options); + consumer.add_options (&options); options.parse (&argc, &argv); argc--, argv++; @@ -85,11 +85,6 @@ struct main_font_text_t return consumer.failed ? 1 : 0; } - - protected: - font_options_t font_opts; - text_options_t input; - consumer_t consumer; }; #endif From 869e20e09f96ba6cddc5db7e9aa6fde29d84ed78 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Fri, 6 Aug 2021 18:09:31 -0600 Subject: [PATCH 13/33] [util] separate face options from font options --- util/helper-cairo.cc | 2 +- util/options.cc | 87 ++++++++++++++++++++++++++++---------------- util/options.hh | 64 ++++++++++++++++++-------------- 3 files changed, 93 insertions(+), 60 deletions(-) diff --git a/util/helper-cairo.cc b/util/helper-cairo.cc index deb9f0d2c..d65051546 100644 --- a/util/helper-cairo.cc +++ b/util/helper-cairo.cc @@ -93,7 +93,7 @@ helper_cairo_create_scaled_font (const font_options_t *font_opts) } unsigned int blob_length; - const char *blob_data = hb_blob_get_data (font_opts->blob, &blob_length); + const char *blob_data = hb_blob_get_data (font_opts->get_blob (), &blob_length); if (FT_New_Memory_Face (ft_library, (const FT_Byte *) blob_data, diff --git a/util/options.cc b/util/options.cc index 9e6548e13..88082051a 100644 --- a/util/options.cc +++ b/util/options.cc @@ -525,9 +525,27 @@ parse_font_ppem (const char *name G_GNUC_UNUSED, } } +void +face_options_t::add_options (option_parser_t *parser) +{ + GOptionEntry entries[] = + { + {"font-file", 0, 0, G_OPTION_ARG_STRING, &this->font_file, "Set font file-name", "filename"}, + {"face-index", 0, 0, G_OPTION_ARG_INT, &this->face_index, "Set face index (default: 0)", "index"}, + {nullptr} + }; + parser->add_group (entries, + "face", + "Font-face options:", + "Options for the font face", + this); +} + void font_options_t::add_options (option_parser_t *parser) { + face_options_t::add_options (parser); + char *text = nullptr; { @@ -557,8 +575,6 @@ font_options_t::add_options (option_parser_t *parser) GOptionEntry entries[] = { - {"font-file", 0, 0, G_OPTION_ARG_STRING, &this->font_file, "Set font file-name", "filename"}, - {"face-index", 0, 0, G_OPTION_ARG_INT, &this->face_index, "Set face index (default: 0)", "index"}, {"font-size", 0, DEFAULT_FONT_SIZE ? 0 : G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_font_size, font_size_text, "1/2 integers or 'upem'"}, {"font-ppem", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_font_ppem, "Set x,y pixels per EM (default: 0; disabled)", "1/2 integers"}, @@ -569,8 +585,8 @@ font_options_t::add_options (option_parser_t *parser) }; parser->add_group (entries, "font", - "Font options:", - "Options for the font", + "Font-instance options:", + "Options for the font instance", this); const gchar *variations_help = "Comma-separated list of font variations\n" @@ -646,15 +662,21 @@ output_options_t::add_options (option_parser_t *parser, } -font_options_t::cache_t font_options_t::cache {}; +face_options_t::cache_t face_options_t::cache {}; -hb_font_t * -font_options_t::get_font () const +hb_blob_t * +face_options_t::get_blob () const { - if (font) - return font; + // XXX This does the job for now; will move to post_parse. + return cache.blob; +} + +hb_face_t * +face_options_t::get_face () const +{ + if (face) + return face; - /* Create the blob */ if (!font_file) fail (true, "No font file set"); @@ -670,40 +692,42 @@ font_options_t::get_font () const #endif } - if (cache.font_path && 0 == strcmp (cache.font_path, font_path)) - blob = hb_blob_reference (cache.blob); - else + if (!cache.font_path || 0 != strcmp (cache.font_path, font_path)) { - blob = hb_blob_create_from_file_or_fail (font_path); + hb_blob_destroy (cache.blob); + cache.blob = hb_blob_create_from_file_or_fail (font_path); - if (!blob) + free ((char *) cache.font_path); + cache.font_path = strdup (font_path); + + if (!cache.blob) fail (false, "%s: Failed reading file", font_path); - /* Update caches. */ - hb_face_destroy (cache.face); cache.face = nullptr; cache.face_index = (unsigned) -1; - - free ((char *) cache.font_path); - cache.font_path = strdup (font_path); - hb_blob_destroy (cache.blob); - cache.blob = hb_blob_reference (blob); } - hb_face_t *face = nullptr; - if (cache.face_index == face_index) - face = hb_face_reference (cache.face); - else + if (cache.face_index != face_index) { - face = hb_face_create (blob, face_index); - hb_blob_destroy (blob); - - cache.face_index = face_index; hb_face_destroy (cache.face); - cache.face = hb_face_reference (face); + cache.face = hb_face_create (cache.blob, face_index); + cache.face_index = face_index; } + face = cache.face; + + return face; +} + + +hb_font_t * +font_options_t::get_font () const +{ + if (font) + return font; + + auto *face = get_face (); font = hb_font_create (face); @@ -718,7 +742,6 @@ font_options_t::get_font () const int scale_x = (int) scalbnf (font_size_x, subpixel_bits); int scale_y = (int) scalbnf (font_size_y, subpixel_bits); hb_font_set_scale (font, scale_x, scale_y); - hb_face_destroy (face); hb_font_set_variations (font, variations, num_variations); diff --git a/util/options.hh b/util/options.hh index c91028a95..87678246f 100644 --- a/util/options.hh +++ b/util/options.hh @@ -441,36 +441,12 @@ struct shape_options_t }; -struct font_options_t +struct face_options_t { - ~font_options_t () - { - g_free (font_file); - free (variations); - g_free (font_funcs); - hb_font_destroy (font); - } - void add_options (option_parser_t *parser); - hb_font_t *get_font () const; - - char *font_file = nullptr; - mutable hb_blob_t *blob = nullptr; - unsigned face_index = 0; - hb_variation_t *variations = nullptr; - unsigned int num_variations = 0; - int x_ppem = 0; - int y_ppem = 0; - double ptem = 0.; - unsigned int subpixel_bits = SUBPIXEL_BITS; - mutable double font_size_x = DEFAULT_FONT_SIZE; - mutable double font_size_y = DEFAULT_FONT_SIZE; - char *font_funcs = nullptr; - int ft_load_flags = 2; - - private: - mutable hb_font_t *font = nullptr; + hb_blob_t *get_blob () const; + hb_face_t *get_face () const; static struct cache_t { @@ -486,6 +462,40 @@ struct font_options_t unsigned face_index = (unsigned) -1; hb_face_t *face = nullptr; } cache; + + char *font_file = nullptr; + unsigned face_index = 0; + private: + mutable hb_face_t *face = nullptr; +}; + +struct font_options_t : face_options_t +{ + ~font_options_t () + { + g_free (font_file); + free (variations); + g_free (font_funcs); + hb_font_destroy (font); + } + + void add_options (option_parser_t *parser); + + hb_font_t *get_font () const; + + hb_variation_t *variations = nullptr; + unsigned int num_variations = 0; + int x_ppem = 0; + int y_ppem = 0; + double ptem = 0.; + unsigned int subpixel_bits = SUBPIXEL_BITS; + mutable double font_size_x = DEFAULT_FONT_SIZE; + mutable double font_size_y = DEFAULT_FONT_SIZE; + char *font_funcs = nullptr; + int ft_load_flags = 2; + + private: + mutable hb_font_t *font = nullptr; }; From 6500f68f1fd0614c7165fc9bea6d37a3cf17a8b4 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Fri, 6 Aug 2021 18:46:31 -0600 Subject: [PATCH 14/33] [util] Change hb-subset to use face_options_t instead of font_options_t --- util/hb-subset.cc | 9 +++++---- util/main-font-text.hh | 3 +-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/util/hb-subset.cc b/util/hb-subset.cc index 7bd8ab1eb..4519c38b3 100644 --- a/util/hb-subset.cc +++ b/util/hb-subset.cc @@ -30,6 +30,7 @@ #include "main-font-text.hh" #include "hb-subset.h" +// XXX Remove eventually const unsigned DEFAULT_FONT_SIZE = FONT_SIZE_NONE; const unsigned SUBPIXEL_BITS = 0; @@ -45,9 +46,9 @@ struct subset_consumer_t subset_options.add_options (parser); } - void init (const font_options_t *font_opts) + void init (const face_options_t *face_opts) { - face = hb_face_reference (hb_font_get_face (font_opts->get_font ())); + face = hb_face_reference (face_opts->get_face ()); input = hb_subset_input_reference (subset_options.get_input ()); } @@ -98,7 +99,7 @@ struct subset_consumer_t return true; } - void finish (const font_options_t *font_opts) + void finish (const face_options_t *face_opts) { hb_face_t *new_face = nullptr; for (unsigned i = 0; i < subset_options.num_iterations; i++) @@ -134,7 +135,7 @@ struct subset_consumer_t int main (int argc, char **argv) { - using driver_t = main_font_text_t; + using driver_t = main_font_text_t; if (argc == 2 && !strcmp (argv[1], "--batch")) { diff --git a/util/main-font-text.hh b/util/main-font-text.hh index 2bca9251c..f45f2c034 100644 --- a/util/main-font-text.hh +++ b/util/main-font-text.hh @@ -47,7 +47,7 @@ locale_to_utf8 (char *s) return t; } -template +template struct main_font_text_t { int @@ -88,4 +88,3 @@ struct main_font_text_t }; #endif - From b1db0e4a0f2c1aa1f64176b0da9ca4ea5585d0ee Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Fri, 6 Aug 2021 18:55:40 -0600 Subject: [PATCH 15/33] [util] Fold view-cairo.cc into view-cairo.hh --- util/Makefile.sources | 1 - util/meson.build | 1 - util/view-cairo.cc | 140 ------------------------------------------ util/view-cairo.hh | 110 +++++++++++++++++++++++++++++++++ 4 files changed, 110 insertions(+), 142 deletions(-) delete mode 100644 util/view-cairo.cc diff --git a/util/Makefile.sources b/util/Makefile.sources index bcf85f562..dae0a4a41 100644 --- a/util/Makefile.sources +++ b/util/Makefile.sources @@ -10,7 +10,6 @@ HB_VIEW_sources = \ helper-cairo.hh \ helper-cairo-ansi.cc \ helper-cairo-ansi.hh \ - view-cairo.cc \ view-cairo.hh \ $(NULL) diff --git a/util/meson.build b/util/meson.build index 5aa7585e8..dd00d44d8 100644 --- a/util/meson.build +++ b/util/meson.build @@ -4,7 +4,6 @@ hb_view_sources = [ 'ansi-print.cc', 'helper-cairo.cc', 'helper-cairo-ansi.cc', - 'view-cairo.cc', ] hb_shape_sources = [ diff --git a/util/view-cairo.cc b/util/view-cairo.cc deleted file mode 100644 index 0db3306af..000000000 --- a/util/view-cairo.cc +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright © 2011 Google, Inc. - * - * This is part of HarfBuzz, a text shaping library. - * - * Permission is hereby granted, without written agreement and without - * license or royalty fees, to use, copy, modify, and distribute this - * software and its documentation for any purpose, provided that the - * above copyright notice and the following two paragraphs appear in - * all copies of this software. - * - * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR - * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES - * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN - * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * - * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, - * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS - * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO - * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - * - * Google Author(s): Behdad Esfahbod - */ - -#include "view-cairo.hh" - -#include - - -void -view_cairo_t::render (const font_options_t *font_opts) -{ - bool vertical = HB_DIRECTION_IS_VERTICAL (direction); - int vert = vertical ? 1 : 0; - int horiz = vertical ? 0 : 1; - - int x_sign = font_opts->font_size_x < 0 ? -1 : +1; - int y_sign = font_opts->font_size_y < 0 ? -1 : +1; - - hb_font_t *font = font_opts->get_font(); - - view_options_t::font_extents_t extents = view_options.font_extents; - if (!view_options.have_font_extents) - { - hb_font_extents_t hb_extents; - hb_font_get_extents_for_direction (font, direction, &hb_extents); - extents.ascent = scalbn ((double) hb_extents.ascender, scale_bits); - extents.descent = -scalbn ((double) hb_extents.descender, scale_bits); - extents.line_gap = scalbn ((double) hb_extents.line_gap, scale_bits); - } - - double ascent = y_sign * extents.ascent; - double descent = y_sign * extents.descent; - double line_gap = y_sign * extents.line_gap + view_options.line_space; - double leading = ascent + descent + line_gap; - - /* Calculate surface size. */ - double w = 0, h = 0; - (vertical ? w : h) = (int) lines->len * leading - (extents.line_gap + view_options.line_space); - (vertical ? h : w) = 0; - for (unsigned int i = 0; i < lines->len; i++) { - helper_cairo_line_t &line = g_array_index (lines, helper_cairo_line_t, i); - double x_advance, y_advance; - line.get_advance (&x_advance, &y_advance); - if (vertical) - h = MAX (h, y_sign * y_advance); - else - w = MAX (w, x_sign * x_advance); - } - - cairo_scaled_font_t *scaled_font = helper_cairo_create_scaled_font (font_opts); - - /* See if font needs color. */ - cairo_content_t content = CAIRO_CONTENT_ALPHA; - if (helper_cairo_scaled_font_has_color (scaled_font)) - content = CAIRO_CONTENT_COLOR; - - /* Create surface. */ - cairo_t *cr = helper_cairo_create_context (w + view_options.margin.l + view_options.margin.r, - h + view_options.margin.t + view_options.margin.b, - &view_options, &output_options, content); - cairo_set_scaled_font (cr, scaled_font); - - /* Setup coordinate system. */ - cairo_translate (cr, view_options.margin.l, view_options.margin.t); - if (vertical) - cairo_translate (cr, - w - ascent, /* We currently always stack lines right to left */ - y_sign < 0 ? h : 0); - else - { - cairo_translate (cr, - x_sign < 0 ? w : 0, - y_sign < 0 ? descent : ascent); - } - - /* Draw. */ - cairo_translate (cr, +vert * leading, -horiz * leading); - for (unsigned int i = 0; i < lines->len; i++) - { - helper_cairo_line_t &l = g_array_index (lines, helper_cairo_line_t, i); - - cairo_translate (cr, -vert * leading, +horiz * leading); - - if (view_options.annotate) { - cairo_save (cr); - - /* Draw actual glyph origins */ - cairo_set_source_rgba (cr, 1., 0., 0., .5); - cairo_set_line_width (cr, 5); - cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND); - for (unsigned i = 0; i < l.num_glyphs; i++) { - cairo_move_to (cr, l.glyphs[i].x, l.glyphs[i].y); - cairo_rel_line_to (cr, 0, 0); - } - cairo_stroke (cr); - - cairo_restore (cr); - } - - if (0 && cairo_surface_get_type (cairo_get_target (cr)) == CAIRO_SURFACE_TYPE_IMAGE) { - /* cairo_show_glyphs() doesn't support subpixel positioning */ - cairo_glyph_path (cr, l.glyphs, l.num_glyphs); - cairo_fill (cr); - } else if (l.num_clusters) - cairo_show_text_glyphs (cr, - l.utf8, l.utf8_len, - l.glyphs, l.num_glyphs, - l.clusters, l.num_clusters, - l.cluster_flags); - else - cairo_show_glyphs (cr, l.glyphs, l.num_glyphs); - } - - /* Clean up. */ - helper_cairo_destroy_context (cr); - cairo_scaled_font_destroy (scaled_font); -} diff --git a/util/view-cairo.hh b/util/view-cairo.hh index 622bbd530..265f31486 100644 --- a/util/view-cairo.hh +++ b/util/view-cairo.hh @@ -94,4 +94,114 @@ struct view_cairo_t int scale_bits = 0; }; +inline void +view_cairo_t::render (const font_options_t *font_opts) +{ + bool vertical = HB_DIRECTION_IS_VERTICAL (direction); + int vert = vertical ? 1 : 0; + int horiz = vertical ? 0 : 1; + + int x_sign = font_opts->font_size_x < 0 ? -1 : +1; + int y_sign = font_opts->font_size_y < 0 ? -1 : +1; + + hb_font_t *font = font_opts->get_font(); + + view_options_t::font_extents_t extents = view_options.font_extents; + if (!view_options.have_font_extents) + { + hb_font_extents_t hb_extents; + hb_font_get_extents_for_direction (font, direction, &hb_extents); + extents.ascent = scalbn ((double) hb_extents.ascender, scale_bits); + extents.descent = -scalbn ((double) hb_extents.descender, scale_bits); + extents.line_gap = scalbn ((double) hb_extents.line_gap, scale_bits); + } + + double ascent = y_sign * extents.ascent; + double descent = y_sign * extents.descent; + double line_gap = y_sign * extents.line_gap + view_options.line_space; + double leading = ascent + descent + line_gap; + + /* Calculate surface size. */ + double w = 0, h = 0; + (vertical ? w : h) = (int) lines->len * leading - (extents.line_gap + view_options.line_space); + (vertical ? h : w) = 0; + for (unsigned int i = 0; i < lines->len; i++) { + helper_cairo_line_t &line = g_array_index (lines, helper_cairo_line_t, i); + double x_advance, y_advance; + line.get_advance (&x_advance, &y_advance); + if (vertical) + h = MAX (h, y_sign * y_advance); + else + w = MAX (w, x_sign * x_advance); + } + + cairo_scaled_font_t *scaled_font = helper_cairo_create_scaled_font (font_opts); + + /* See if font needs color. */ + cairo_content_t content = CAIRO_CONTENT_ALPHA; + if (helper_cairo_scaled_font_has_color (scaled_font)) + content = CAIRO_CONTENT_COLOR; + + /* Create surface. */ + cairo_t *cr = helper_cairo_create_context (w + view_options.margin.l + view_options.margin.r, + h + view_options.margin.t + view_options.margin.b, + &view_options, &output_options, content); + cairo_set_scaled_font (cr, scaled_font); + + /* Setup coordinate system. */ + cairo_translate (cr, view_options.margin.l, view_options.margin.t); + if (vertical) + cairo_translate (cr, + w - ascent, /* We currently always stack lines right to left */ + y_sign < 0 ? h : 0); + else + { + cairo_translate (cr, + x_sign < 0 ? w : 0, + y_sign < 0 ? descent : ascent); + } + + /* Draw. */ + cairo_translate (cr, +vert * leading, -horiz * leading); + for (unsigned int i = 0; i < lines->len; i++) + { + helper_cairo_line_t &l = g_array_index (lines, helper_cairo_line_t, i); + + cairo_translate (cr, -vert * leading, +horiz * leading); + + if (view_options.annotate) { + cairo_save (cr); + + /* Draw actual glyph origins */ + cairo_set_source_rgba (cr, 1., 0., 0., .5); + cairo_set_line_width (cr, 5); + cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND); + for (unsigned i = 0; i < l.num_glyphs; i++) { + cairo_move_to (cr, l.glyphs[i].x, l.glyphs[i].y); + cairo_rel_line_to (cr, 0, 0); + } + cairo_stroke (cr); + + cairo_restore (cr); + } + + if (0 && cairo_surface_get_type (cairo_get_target (cr)) == CAIRO_SURFACE_TYPE_IMAGE) { + /* cairo_show_glyphs() doesn't support subpixel positioning */ + cairo_glyph_path (cr, l.glyphs, l.num_glyphs); + cairo_fill (cr); + } else if (l.num_clusters) + cairo_show_text_glyphs (cr, + l.utf8, l.utf8_len, + l.glyphs, l.num_glyphs, + l.clusters, l.num_clusters, + l.cluster_flags); + else + cairo_show_glyphs (cr, l.glyphs, l.num_glyphs); + } + + /* Clean up. */ + helper_cairo_destroy_context (cr); + cairo_scaled_font_destroy (scaled_font); +} + #endif From 4fe43ccbf172d633c17ea4d5c308310d4597062f Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Fri, 6 Aug 2021 19:00:45 -0600 Subject: [PATCH 16/33] [util] Fold ansi-print.cc into ansi-print.hh --- util/Makefile.sources | 1 - util/ansi-print.cc | 427 ------------------------------------------ util/ansi-print.hh | 391 +++++++++++++++++++++++++++++++++++++- util/meson.build | 1 - 4 files changed, 389 insertions(+), 431 deletions(-) delete mode 100644 util/ansi-print.cc diff --git a/util/Makefile.sources b/util/Makefile.sources index dae0a4a41..8583d22e2 100644 --- a/util/Makefile.sources +++ b/util/Makefile.sources @@ -4,7 +4,6 @@ HB_VIEW_sources = \ options.hh \ main-font-text.hh \ shape-consumer.hh \ - ansi-print.cc \ ansi-print.hh \ helper-cairo.cc \ helper-cairo.hh \ diff --git a/util/ansi-print.cc b/util/ansi-print.cc deleted file mode 100644 index 80b3704ba..000000000 --- a/util/ansi-print.cc +++ /dev/null @@ -1,427 +0,0 @@ -/* - * Copyright © 2012 Google, Inc. - * - * This is part of HarfBuzz, a text shaping library. - * - * Permission is hereby granted, without written agreement and without - * license or royalty fees, to use, copy, modify, and distribute this - * software and its documentation for any purpose, provided that the - * above copyright notice and the following two paragraphs appear in - * all copies of this software. - * - * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR - * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES - * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN - * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * - * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, - * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS - * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO - * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - * - * Google Author(s): Behdad Esfahbod - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "ansi-print.hh" - -#include -#include -#include -#include -#include -#include -#include -#ifdef HAVE_UNISTD_H -#include /* for isatty() */ -#endif - -#if defined (_MSC_VER) && (_MSC_VER < 1800) -static inline long int -lround (double x) -{ - if (x >= 0) - return floor (x + 0.5); - else - return ceil (x - 0.5); -} -#endif - -#define ESC_E (char)27 - -#define MIN(a,b) ((a) < (b) ? (a) : (b)) - -#define CELL_W 8 -#define CELL_H (2 * CELL_W) - -struct color_diff_t -{ - int dot (const color_diff_t &o) - { return v[0]*o.v[0] + v[1]*o.v[1] + v[2]*o.v[2] + v[3]*o.v[3]; } - - int v[4]; -}; - -struct color_t -{ - static color_t from_ansi (unsigned int x) - { - color_t c = {(0xFFu<<24) | ((0xFFu*(x&1))<<16) | ((0xFFu*((x >> 1)&1))<<8) | (0xFFu*((x >> 2)&1))}; - return c; - } - unsigned int to_ansi () - { - return ((v >> 23) & 1) | ((v >> 14)&2) | ((v >> 5)&4); - } - - color_diff_t diff (const color_t &o) - { - color_diff_t d; - for (unsigned int i = 0; i < 4; i++) - d.v[i] = (int) ((v >> (i*8))&0xFF) - (int) ((o.v >> (i*8))&0xFF); - return d; - } - - uint32_t v; -}; - -struct image_t -{ - public: - - image_t (unsigned int width_, - unsigned int height_, - const uint32_t *data_, - unsigned int stride_) : - width (width_), - height (height_), - own_data (false), - data ((color_t *) data_), - stride (stride_) {} - image_t (unsigned int width_, - unsigned int height_) : - width (width_), - height (height_), - own_data (true), - data ((color_t *) malloc (sizeof (data[0]) * width * height)), - stride (width) {} - ~image_t () - { if (own_data) free (data); } - - color_t &operator () (unsigned int x, unsigned int y) - { return data[x + y * stride]; } - - color_t operator () (unsigned int x, unsigned int y) const - { return data[x + y * stride]; } - - void - copy_sub_image (const image_t &s, - unsigned int x, unsigned int y, - unsigned int w, unsigned int h) - { - assert (x < width); - assert (y < height); - for (unsigned int row = 0; row < h; row++) { - color_t *p = data + x + MIN (y + row, height - 1) * stride; - color_t *q = s.data + row * s.stride; - if (x + w <= width) - for (unsigned int col = 0; col < w; col++) - *q++ = *p++; - else { - unsigned int limit = width - x; - for (unsigned int col = 0; col < limit; col++) - *q++ = *p++; - p--; - for (unsigned int col = limit; col < w; col++) - *q++ = *p; - } - } - } - - const unsigned int width; - const unsigned int height; - - private: - bool own_data; - color_t * const data; - const unsigned int stride; -}; - -struct biimage_t -{ - public: - - biimage_t (unsigned int width, unsigned int height) : - width (width), - height (height), - bg (0), fg (0), unicolor (true), - data ((uint8_t *) malloc (sizeof (data[0]) * width * height)) {} - ~biimage_t () - { free (data); } - - void set (const image_t &image) - { - assert (image.width == width); - assert (image.height == height); - int freq[8] = {0}; - for (unsigned int y = 0; y < height; y++) - for (unsigned int x = 0; x < width; x++) { - color_t c = image (x, y); - freq[c.to_ansi ()]++; - } - bg = 0; - for (unsigned int i = 1; i < 8; i++) - if (freq[bg] < freq[i]) - bg = i; - fg = 0; - for (unsigned int i = 1; i < 8; i++) - if (i != bg && freq[fg] < freq[i]) - fg = i; - if (fg == bg || freq[fg] == 0) { - fg = bg; - unicolor = true; - } - else - unicolor = false; - - /* Set the data... */ - - if (unicolor) { - memset (data, 0, sizeof (data[0]) * width * height); - return; - } - - color_t bgc = color_t::from_ansi (bg); - color_t fgc = color_t::from_ansi (fg); - color_diff_t diff = fgc.diff (bgc); - int dd = diff.dot (diff); - for (unsigned int y = 0; y < height; y++) - for (unsigned int x = 0; x < width; x++) { - int d = diff.dot (image (x, y).diff (bgc)); - (*this)(x, y) = d < 0 ? 0 : d > dd ? 255 : lround (d * 255. / dd); - } - } - - uint8_t &operator () (unsigned int x, unsigned int y) - { return data[x + y * width]; } - - uint8_t operator () (unsigned int x, unsigned int y) const - { return data[x + y * width]; } - - const unsigned int width; - const unsigned int height; - unsigned int bg; - unsigned int fg; - bool unicolor; - - private: - uint8_t * const data; -}; - -static const char * -block_best (const biimage_t &bi, bool *inverse) -{ - assert (bi.width <= CELL_W); - assert (bi.height <= CELL_H); - - unsigned int score = UINT_MAX; - unsigned int row_sum[CELL_H] = {0}; - unsigned int col_sum[CELL_W] = {0}; - unsigned int row_sum_i[CELL_H] = {0}; - unsigned int col_sum_i[CELL_W] = {0}; - unsigned int quad[2][2] = {{0}}; - unsigned int quad_i[2][2] = {{0}}; - unsigned int total = 0; - unsigned int total_i = 0; - for (unsigned int y = 0; y < bi.height; y++) - for (unsigned int x = 0; x < bi.width; x++) { - unsigned int c = bi (x, y); - unsigned int c_i = 255 - c; - row_sum[y] += c; - row_sum_i[y] += c_i; - col_sum[x] += c; - col_sum_i[x] += c_i; - quad[2 * y / bi.height][2 * x / bi.width] += c; - quad_i[2 * y / bi.height][2 * x / bi.width] += c_i; - total += c; - total_i += c_i; - } - - /* Make the sums cummulative */ - for (unsigned int i = 1; i < bi.height; i++) { - row_sum[i] += row_sum[i - 1]; - row_sum_i[i] += row_sum_i[i - 1]; - } - for (unsigned int i = 1; i < bi.width; i++) { - col_sum[i] += col_sum[i - 1]; - col_sum_i[i] += col_sum_i[i - 1]; - } - - const char *best_c = " "; - - /* Maybe empty is better! */ - if (total < score) { - score = total; - *inverse = false; - best_c = " "; - } - /* Maybe full is better! */ - if (total_i < score) { - score = total_i; - *inverse = true; - best_c = " "; - } - - /* Find best lower line */ - if (1) { - unsigned int best_s = UINT_MAX; - bool best_inv = false; - int best_i = 0; - for (unsigned int i = 0; i < bi.height - 1; i++) - { - unsigned int s; - s = row_sum[i] + total_i - row_sum_i[i]; - if (s < best_s) { - best_s = s; - best_i = i; - best_inv = false; - } - s = row_sum_i[i] + total - row_sum[i]; - if (s < best_s) { - best_s = s; - best_i = i; - best_inv = true; - } - } - if (best_s < score) { - static const char *lower[7] = {"▁", "▂", "▃", "▄", "▅", "▆", "▇"}; - unsigned int which = lround ((double) ((best_i + 1) * 8) / bi.height); - if (1 <= which && which <= 7) { - score = best_s; - *inverse = best_inv; - best_c = lower[7 - which]; - } - } - } - - /* Find best left line */ - if (1) { - unsigned int best_s = UINT_MAX; - bool best_inv = false; - int best_i = 0; - for (unsigned int i = 0; i < bi.width - 1; i++) - { - unsigned int s; - s = col_sum[i] + total_i - col_sum_i[i]; - if (s < best_s) { - best_s = s; - best_i = i; - best_inv = true; - } - s = col_sum_i[i] + total - col_sum[i]; - if (s < best_s) { - best_s = s; - best_i = i; - best_inv = false; - } - } - if (best_s < score) { - static const char *left [7] = {"▏", "▎", "▍", "▌", "▋", "▊", "▉"}; - unsigned int which = lround ((double) ((best_i + 1) * 8) / bi.width); - if (1 <= which && which <= 7) { - score = best_s; - *inverse = best_inv; - best_c = left[which - 1]; - } - } - } - - /* Find best quadrant */ - if (1) { - unsigned int q = 0; - unsigned int qs = 0; - for (unsigned int i = 0; i < 2; i++) - for (unsigned int j = 0; j < 2; j++) - if (quad[i][j] > quad_i[i][j]) { - q += 1 << (2 * i + j); - qs += quad_i[i][j]; - } else - qs += quad[i][j]; - if (qs < score) { - const char *c = nullptr; - bool inv = false; - switch (q) { - case 1: c = "▟"; inv = true; break; - case 2: c = "▙"; inv = true; break; - case 4: c = "▖"; inv = false; break; - case 8: c = "▗"; inv = false; break; - case 9: c = "▚"; inv = false; break; - case 6: c = "▞"; inv = false; break; - case 7: c = "▜"; inv = true; break; - case 11: c = "▜"; inv = true; break; - case 13: c = "▙"; inv = true; break; - case 14: c = "▟"; inv = true; break; - } - if (c) { - score = qs; - *inverse = inv; - best_c = c; - } - } - } - - return best_c; -} - -void -ansi_print_image_rgb24 (const uint32_t *data, - unsigned int width, - unsigned int height, - unsigned int stride) -{ - image_t image (width, height, data, stride); - - unsigned int rows = (height + CELL_H - 1) / CELL_H; - unsigned int cols = (width + CELL_W - 1) / CELL_W; - image_t cell (CELL_W, CELL_H); - biimage_t bi (CELL_W, CELL_H); - unsigned int last_bg = -1, last_fg = -1; - for (unsigned int row = 0; row < rows; row++) { - for (unsigned int col = 0; col < cols; col++) { - image.copy_sub_image (cell, col * CELL_W, row * CELL_H, CELL_W, CELL_H); - bi.set (cell); - if (bi.unicolor) { - if (last_bg != bi.bg) { - printf ("%c[%dm", ESC_E, 40 + bi.bg); - last_bg = bi.bg; - } - printf (" "); - } else { - /* Figure out the closest character to the biimage */ - bool inverse = false; - const char *c = block_best (bi, &inverse); - if (inverse) { - if (last_bg != bi.fg || last_fg != bi.bg) { - printf ("%c[%d;%dm", ESC_E, 30 + bi.bg, 40 + bi.fg); - last_bg = bi.fg; - last_fg = bi.bg; - } - } else { - if (last_bg != bi.bg || last_fg != bi.fg) { - printf ("%c[%d;%dm", ESC_E, 40 + bi.bg, 30 + bi.fg); - last_bg = bi.bg; - last_fg = bi.fg; - } - } - printf ("%s", c); - } - } - printf ("%c[0m\n", ESC_E); /* Reset */ - last_bg = last_fg = -1; - } -} diff --git a/util/ansi-print.hh b/util/ansi-print.hh index 9640d892d..d251e54ec 100644 --- a/util/ansi-print.hh +++ b/util/ansi-print.hh @@ -29,11 +29,398 @@ #include "hb.hh" -void +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include /* for isatty() */ +#endif + +#if defined (_MSC_VER) && (_MSC_VER < 1800) +static inline long int +lround (double x) +{ + if (x >= 0) + return floor (x + 0.5); + else + return ceil (x - 0.5); +} +#endif + +#define ESC_E (char)27 + +#define CELL_W 8 +#define CELL_H (2 * CELL_W) + +struct color_diff_t +{ + int dot (const color_diff_t &o) + { return v[0]*o.v[0] + v[1]*o.v[1] + v[2]*o.v[2] + v[3]*o.v[3]; } + + int v[4]; +}; + +struct color_t +{ + static color_t from_ansi (unsigned int x) + { + color_t c = {(0xFFu<<24) | ((0xFFu*(x&1))<<16) | ((0xFFu*((x >> 1)&1))<<8) | (0xFFu*((x >> 2)&1))}; + return c; + } + unsigned int to_ansi () + { + return ((v >> 23) & 1) | ((v >> 14)&2) | ((v >> 5)&4); + } + + color_diff_t diff (const color_t &o) + { + color_diff_t d; + for (unsigned int i = 0; i < 4; i++) + d.v[i] = (int) ((v >> (i*8))&0xFF) - (int) ((o.v >> (i*8))&0xFF); + return d; + } + + uint32_t v; +}; + +struct image_t +{ + public: + + image_t (unsigned int width_, + unsigned int height_, + const uint32_t *data_, + unsigned int stride_) : + width (width_), + height (height_), + own_data (false), + data ((color_t *) data_), + stride (stride_) {} + image_t (unsigned int width_, + unsigned int height_) : + width (width_), + height (height_), + own_data (true), + data ((color_t *) malloc (sizeof (data[0]) * width * height)), + stride (width) {} + ~image_t () + { if (own_data) free (data); } + + color_t &operator () (unsigned int x, unsigned int y) + { return data[x + y * stride]; } + + color_t operator () (unsigned int x, unsigned int y) const + { return data[x + y * stride]; } + + void + copy_sub_image (const image_t &s, + unsigned int x, unsigned int y, + unsigned int w, unsigned int h) + { + assert (x < width); + assert (y < height); + for (unsigned int row = 0; row < h; row++) { + color_t *p = data + x + hb_min (y + row, height - 1) * stride; + color_t *q = s.data + row * s.stride; + if (x + w <= width) + for (unsigned int col = 0; col < w; col++) + *q++ = *p++; + else { + unsigned int limit = width - x; + for (unsigned int col = 0; col < limit; col++) + *q++ = *p++; + p--; + for (unsigned int col = limit; col < w; col++) + *q++ = *p; + } + } + } + + const unsigned int width; + const unsigned int height; + + private: + bool own_data; + color_t * const data; + const unsigned int stride; +}; + +struct biimage_t +{ + public: + + biimage_t (unsigned int width, unsigned int height) : + width (width), + height (height), + bg (0), fg (0), unicolor (true), + data ((uint8_t *) malloc (sizeof (data[0]) * width * height)) {} + ~biimage_t () + { free (data); } + + void set (const image_t &image) + { + assert (image.width == width); + assert (image.height == height); + int freq[8] = {0}; + for (unsigned int y = 0; y < height; y++) + for (unsigned int x = 0; x < width; x++) { + color_t c = image (x, y); + freq[c.to_ansi ()]++; + } + bg = 0; + for (unsigned int i = 1; i < 8; i++) + if (freq[bg] < freq[i]) + bg = i; + fg = 0; + for (unsigned int i = 1; i < 8; i++) + if (i != bg && freq[fg] < freq[i]) + fg = i; + if (fg == bg || freq[fg] == 0) { + fg = bg; + unicolor = true; + } + else + unicolor = false; + + /* Set the data... */ + + if (unicolor) { + memset (data, 0, sizeof (data[0]) * width * height); + return; + } + + color_t bgc = color_t::from_ansi (bg); + color_t fgc = color_t::from_ansi (fg); + color_diff_t diff = fgc.diff (bgc); + int dd = diff.dot (diff); + for (unsigned int y = 0; y < height; y++) + for (unsigned int x = 0; x < width; x++) { + int d = diff.dot (image (x, y).diff (bgc)); + (*this)(x, y) = d < 0 ? 0 : d > dd ? 255 : lround (d * 255. / dd); + } + } + + uint8_t &operator () (unsigned int x, unsigned int y) + { return data[x + y * width]; } + + uint8_t operator () (unsigned int x, unsigned int y) const + { return data[x + y * width]; } + + const unsigned int width; + const unsigned int height; + unsigned int bg; + unsigned int fg; + bool unicolor; + + private: + uint8_t * const data; +}; + +static const char * +block_best (const biimage_t &bi, bool *inverse) +{ + assert (bi.width <= CELL_W); + assert (bi.height <= CELL_H); + + unsigned int score = UINT_MAX; + unsigned int row_sum[CELL_H] = {0}; + unsigned int col_sum[CELL_W] = {0}; + unsigned int row_sum_i[CELL_H] = {0}; + unsigned int col_sum_i[CELL_W] = {0}; + unsigned int quad[2][2] = {{0}}; + unsigned int quad_i[2][2] = {{0}}; + unsigned int total = 0; + unsigned int total_i = 0; + for (unsigned int y = 0; y < bi.height; y++) + for (unsigned int x = 0; x < bi.width; x++) { + unsigned int c = bi (x, y); + unsigned int c_i = 255 - c; + row_sum[y] += c; + row_sum_i[y] += c_i; + col_sum[x] += c; + col_sum_i[x] += c_i; + quad[2 * y / bi.height][2 * x / bi.width] += c; + quad_i[2 * y / bi.height][2 * x / bi.width] += c_i; + total += c; + total_i += c_i; + } + + /* Make the sums cummulative */ + for (unsigned int i = 1; i < bi.height; i++) { + row_sum[i] += row_sum[i - 1]; + row_sum_i[i] += row_sum_i[i - 1]; + } + for (unsigned int i = 1; i < bi.width; i++) { + col_sum[i] += col_sum[i - 1]; + col_sum_i[i] += col_sum_i[i - 1]; + } + + const char *best_c = " "; + + /* Maybe empty is better! */ + if (total < score) { + score = total; + *inverse = false; + best_c = " "; + } + /* Maybe full is better! */ + if (total_i < score) { + score = total_i; + *inverse = true; + best_c = " "; + } + + /* Find best lower line */ + if (1) { + unsigned int best_s = UINT_MAX; + bool best_inv = false; + int best_i = 0; + for (unsigned int i = 0; i < bi.height - 1; i++) + { + unsigned int s; + s = row_sum[i] + total_i - row_sum_i[i]; + if (s < best_s) { + best_s = s; + best_i = i; + best_inv = false; + } + s = row_sum_i[i] + total - row_sum[i]; + if (s < best_s) { + best_s = s; + best_i = i; + best_inv = true; + } + } + if (best_s < score) { + static const char *lower[7] = {"▁", "▂", "▃", "▄", "▅", "▆", "▇"}; + unsigned int which = lround ((double) ((best_i + 1) * 8) / bi.height); + if (1 <= which && which <= 7) { + score = best_s; + *inverse = best_inv; + best_c = lower[7 - which]; + } + } + } + + /* Find best left line */ + if (1) { + unsigned int best_s = UINT_MAX; + bool best_inv = false; + int best_i = 0; + for (unsigned int i = 0; i < bi.width - 1; i++) + { + unsigned int s; + s = col_sum[i] + total_i - col_sum_i[i]; + if (s < best_s) { + best_s = s; + best_i = i; + best_inv = true; + } + s = col_sum_i[i] + total - col_sum[i]; + if (s < best_s) { + best_s = s; + best_i = i; + best_inv = false; + } + } + if (best_s < score) { + static const char *left [7] = {"▏", "▎", "▍", "▌", "▋", "▊", "▉"}; + unsigned int which = lround ((double) ((best_i + 1) * 8) / bi.width); + if (1 <= which && which <= 7) { + score = best_s; + *inverse = best_inv; + best_c = left[which - 1]; + } + } + } + + /* Find best quadrant */ + if (1) { + unsigned int q = 0; + unsigned int qs = 0; + for (unsigned int i = 0; i < 2; i++) + for (unsigned int j = 0; j < 2; j++) + if (quad[i][j] > quad_i[i][j]) { + q += 1 << (2 * i + j); + qs += quad_i[i][j]; + } else + qs += quad[i][j]; + if (qs < score) { + const char *c = nullptr; + bool inv = false; + switch (q) { + case 1: c = "▟"; inv = true; break; + case 2: c = "▙"; inv = true; break; + case 4: c = "▖"; inv = false; break; + case 8: c = "▗"; inv = false; break; + case 9: c = "▚"; inv = false; break; + case 6: c = "▞"; inv = false; break; + case 7: c = "▜"; inv = true; break; + case 11: c = "▜"; inv = true; break; + case 13: c = "▙"; inv = true; break; + case 14: c = "▟"; inv = true; break; + } + if (c) { + score = qs; + *inverse = inv; + best_c = c; + } + } + } + + return best_c; +} + +static inline void ansi_print_image_rgb24 (const uint32_t *data, unsigned int width, unsigned int height, - unsigned int stride); + unsigned int stride) +{ + image_t image (width, height, data, stride); + unsigned int rows = (height + CELL_H - 1) / CELL_H; + unsigned int cols = (width + CELL_W - 1) / CELL_W; + image_t cell (CELL_W, CELL_H); + biimage_t bi (CELL_W, CELL_H); + unsigned int last_bg = -1, last_fg = -1; + for (unsigned int row = 0; row < rows; row++) { + for (unsigned int col = 0; col < cols; col++) { + image.copy_sub_image (cell, col * CELL_W, row * CELL_H, CELL_W, CELL_H); + bi.set (cell); + if (bi.unicolor) { + if (last_bg != bi.bg) { + printf ("%c[%dm", ESC_E, 40 + bi.bg); + last_bg = bi.bg; + } + printf (" "); + } else { + /* Figure out the closest character to the biimage */ + bool inverse = false; + const char *c = block_best (bi, &inverse); + if (inverse) { + if (last_bg != bi.fg || last_fg != bi.bg) { + printf ("%c[%d;%dm", ESC_E, 30 + bi.bg, 40 + bi.fg); + last_bg = bi.fg; + last_fg = bi.bg; + } + } else { + if (last_bg != bi.bg || last_fg != bi.fg) { + printf ("%c[%d;%dm", ESC_E, 40 + bi.bg, 30 + bi.fg); + last_bg = bi.bg; + last_fg = bi.fg; + } + } + printf ("%s", c); + } + } + printf ("%c[0m\n", ESC_E); /* Reset */ + last_bg = last_fg = -1; + } +} #endif diff --git a/util/meson.build b/util/meson.build index dd00d44d8..d53712c02 100644 --- a/util/meson.build +++ b/util/meson.build @@ -1,7 +1,6 @@ hb_view_sources = [ 'hb-view.cc', 'options.cc', - 'ansi-print.cc', 'helper-cairo.cc', 'helper-cairo-ansi.cc', ] From c40e00796c6ae900a9e540ad9e345a4514fdc4eb Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Fri, 6 Aug 2021 19:04:27 -0600 Subject: [PATCH 17/33] [util] Fold helper-cairo-ansi.cc into helper-cairo-ansi.hh --- util/Makefile.sources | 1 - util/helper-cairo-ansi.cc | 202 -------------------------------------- util/helper-cairo-ansi.hh | 174 +++++++++++++++++++++++++++++++- util/meson.build | 1 - 4 files changed, 172 insertions(+), 206 deletions(-) delete mode 100644 util/helper-cairo-ansi.cc diff --git a/util/Makefile.sources b/util/Makefile.sources index 8583d22e2..e128a523f 100644 --- a/util/Makefile.sources +++ b/util/Makefile.sources @@ -7,7 +7,6 @@ HB_VIEW_sources = \ ansi-print.hh \ helper-cairo.cc \ helper-cairo.hh \ - helper-cairo-ansi.cc \ helper-cairo-ansi.hh \ view-cairo.hh \ $(NULL) diff --git a/util/helper-cairo-ansi.cc b/util/helper-cairo-ansi.cc deleted file mode 100644 index 3db833bfa..000000000 --- a/util/helper-cairo-ansi.cc +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright © 2012 Google, Inc. - * - * This is part of HarfBuzz, a text shaping library. - * - * Permission is hereby granted, without written agreement and without - * license or royalty fees, to use, copy, modify, and distribute this - * software and its documentation for any purpose, provided that the - * above copyright notice and the following two paragraphs appear in - * all copies of this software. - * - * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR - * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES - * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN - * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * - * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, - * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS - * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO - * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - * - * Google Author(s): Behdad Esfahbod - */ - -#include "helper-cairo-ansi.hh" -#include "options.hh" -#include "ansi-print.hh" - -#ifdef HAVE_CHAFA -# include - -/* Similar to ansi-print.cc */ -# define CELL_W 8 -# define CELL_H (2 * CELL_W) - -static void -chafa_print_image_rgb24 (const void *data, int width, int height, int stride) -{ - ChafaTermInfo *term_info; - ChafaSymbolMap *symbol_map; - ChafaCanvasConfig *config; - ChafaCanvas *canvas; - GString *gs; - unsigned int cols = (width + CELL_W - 1) / CELL_W; - unsigned int rows = (height + CELL_H - 1) / CELL_H; - gchar **environ; - ChafaCanvasMode mode; - ChafaPixelMode pixel_mode; - - /* Adapt to terminal; use sixels if available, and fall back to symbols - * with as many colors as are supported */ - - environ = g_get_environ (); - term_info = chafa_term_db_detect (chafa_term_db_get_default (), - environ); - - pixel_mode = CHAFA_PIXEL_MODE_SYMBOLS; - - if (chafa_term_info_have_seq (term_info, CHAFA_TERM_SEQ_BEGIN_SIXELS)) - { - pixel_mode = CHAFA_PIXEL_MODE_SIXELS; - mode = CHAFA_CANVAS_MODE_TRUECOLOR; - } -// else if (chafa_term_info_have_seq (term_info, CHAFA_TERM_SEQ_SET_COLOR_FGBG_DIRECT)) -// mode = CHAFA_CANVAS_MODE_TRUECOLOR; - else if (chafa_term_info_have_seq (term_info, CHAFA_TERM_SEQ_SET_COLOR_FGBG_256)) - mode = CHAFA_CANVAS_MODE_INDEXED_240; - else if (chafa_term_info_have_seq (term_info, CHAFA_TERM_SEQ_SET_COLOR_FGBG_16)) - mode = CHAFA_CANVAS_MODE_INDEXED_16; - else if (chafa_term_info_have_seq (term_info, CHAFA_TERM_SEQ_INVERT_COLORS)) - mode = CHAFA_CANVAS_MODE_FGBG_BGFG; - else - mode = CHAFA_CANVAS_MODE_FGBG; - - /* Create the configuration */ - - symbol_map = chafa_symbol_map_new (); - chafa_symbol_map_add_by_tags (symbol_map, - (ChafaSymbolTags) (CHAFA_SYMBOL_TAG_BLOCK - | CHAFA_SYMBOL_TAG_SPACE)); - - config = chafa_canvas_config_new (); - chafa_canvas_config_set_canvas_mode (config, mode); - chafa_canvas_config_set_pixel_mode (config, pixel_mode); - chafa_canvas_config_set_cell_geometry (config, 10, 20); - chafa_canvas_config_set_geometry (config, cols, rows); - chafa_canvas_config_set_symbol_map (config, symbol_map); - chafa_canvas_config_set_color_extractor (config, CHAFA_COLOR_EXTRACTOR_MEDIAN); - chafa_canvas_config_set_work_factor (config, 1.0f); - - /* Create canvas, draw to it and render output string */ - - canvas = chafa_canvas_new (config); - chafa_canvas_draw_all_pixels (canvas, - /* Cairo byte order is host native */ - G_BYTE_ORDER == G_LITTLE_ENDIAN - ? CHAFA_PIXEL_BGRA8_PREMULTIPLIED - : CHAFA_PIXEL_ARGB8_PREMULTIPLIED, - (const guint8 *) data, - width, - height, - stride); - gs = chafa_canvas_print (canvas, term_info); - - /* Print the string */ - - fwrite (gs->str, sizeof (char), gs->len, stdout); - - if (pixel_mode != CHAFA_PIXEL_MODE_SIXELS) - fputc ('\n', stdout); - - /* Free resources */ - - g_string_free (gs, TRUE); - chafa_canvas_unref (canvas); - chafa_canvas_config_unref (config); - chafa_symbol_map_unref (symbol_map); - chafa_term_info_unref (term_info); - g_strfreev (environ); -} - -#endif /* HAVE_CHAFA */ - -cairo_status_t -helper_cairo_surface_write_to_ansi_stream (cairo_surface_t *surface, - cairo_write_func_t write_func, - void *closure) -{ - unsigned int width = cairo_image_surface_get_width (surface); - unsigned int height = cairo_image_surface_get_height (surface); - if (cairo_image_surface_get_format (surface) != CAIRO_FORMAT_RGB24) { - cairo_surface_t *new_surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height); - cairo_t *cr = cairo_create (new_surface); - if (cairo_image_surface_get_format (surface) == CAIRO_FORMAT_A8) { - cairo_set_source_rgb (cr, 0., 0., 0.); - cairo_paint (cr); - cairo_set_source_rgb (cr, 1., 1., 1.); - cairo_mask_surface (cr, surface, 0, 0); - } else { - cairo_set_source_rgb (cr, 1., 1., 1.); - cairo_paint (cr); - cairo_set_source_surface (cr, surface, 0, 0); - cairo_paint (cr); - } - cairo_destroy (cr); - surface = new_surface; - } else - cairo_surface_reference (surface); - - unsigned int stride = cairo_image_surface_get_stride (surface); - const uint32_t *data = (uint32_t *) (void *) cairo_image_surface_get_data (surface); - - /* We don't have rows to spare on the terminal window... - * Find the tight image top/bottom and only print in between. */ - - /* Use corner color as background color. */ - uint32_t bg_color = data ? * (uint32_t *) data : 0; - - /* Drop first row while empty */ - while (height) - { - unsigned int i; - for (i = 0; i < width; i++) - if (data[i] != bg_color) - break; - if (i < width) - break; - data += stride / 4; - height--; - } - - /* Drop last row while empty */ - unsigned int orig_height = height; - while (height) - { - const uint32_t *row = data + (height - 1) * stride / 4; - unsigned int i; - for (i = 0; i < width; i++) - if (row[i] != bg_color) - break; - if (i < width) - break; - height--; - } - if (height < orig_height) - height++; /* Add one last blank row for padding. */ - - if (width && height) - { -#ifdef HAVE_CHAFA - if (true) - chafa_print_image_rgb24 (data, width, height, stride); - else -#endif - ansi_print_image_rgb24 (data, width, height, stride / 4); - } - - cairo_surface_destroy (surface); - return CAIRO_STATUS_SUCCESS; -} diff --git a/util/helper-cairo-ansi.hh b/util/helper-cairo-ansi.hh index bc2313219..8c8a1eece 100644 --- a/util/helper-cairo-ansi.hh +++ b/util/helper-cairo-ansi.hh @@ -31,10 +31,180 @@ #include -cairo_status_t +#include "ansi-print.hh" + +#ifdef HAVE_CHAFA +# include + +/* Similar to ansi-print.cc */ +# define CELL_W 8 +# define CELL_H (2 * CELL_W) + +static void +chafa_print_image_rgb24 (const void *data, int width, int height, int stride) +{ + ChafaTermInfo *term_info; + ChafaSymbolMap *symbol_map; + ChafaCanvasConfig *config; + ChafaCanvas *canvas; + GString *gs; + unsigned int cols = (width + CELL_W - 1) / CELL_W; + unsigned int rows = (height + CELL_H - 1) / CELL_H; + gchar **environ; + ChafaCanvasMode mode; + ChafaPixelMode pixel_mode; + + /* Adapt to terminal; use sixels if available, and fall back to symbols + * with as many colors as are supported */ + + environ = g_get_environ (); + term_info = chafa_term_db_detect (chafa_term_db_get_default (), + environ); + + pixel_mode = CHAFA_PIXEL_MODE_SYMBOLS; + + if (chafa_term_info_have_seq (term_info, CHAFA_TERM_SEQ_BEGIN_SIXELS)) + { + pixel_mode = CHAFA_PIXEL_MODE_SIXELS; + mode = CHAFA_CANVAS_MODE_TRUECOLOR; + } +// else if (chafa_term_info_have_seq (term_info, CHAFA_TERM_SEQ_SET_COLOR_FGBG_DIRECT)) +// mode = CHAFA_CANVAS_MODE_TRUECOLOR; + else if (chafa_term_info_have_seq (term_info, CHAFA_TERM_SEQ_SET_COLOR_FGBG_256)) + mode = CHAFA_CANVAS_MODE_INDEXED_240; + else if (chafa_term_info_have_seq (term_info, CHAFA_TERM_SEQ_SET_COLOR_FGBG_16)) + mode = CHAFA_CANVAS_MODE_INDEXED_16; + else if (chafa_term_info_have_seq (term_info, CHAFA_TERM_SEQ_INVERT_COLORS)) + mode = CHAFA_CANVAS_MODE_FGBG_BGFG; + else + mode = CHAFA_CANVAS_MODE_FGBG; + + /* Create the configuration */ + + symbol_map = chafa_symbol_map_new (); + chafa_symbol_map_add_by_tags (symbol_map, + (ChafaSymbolTags) (CHAFA_SYMBOL_TAG_BLOCK + | CHAFA_SYMBOL_TAG_SPACE)); + + config = chafa_canvas_config_new (); + chafa_canvas_config_set_canvas_mode (config, mode); + chafa_canvas_config_set_pixel_mode (config, pixel_mode); + chafa_canvas_config_set_cell_geometry (config, 10, 20); + chafa_canvas_config_set_geometry (config, cols, rows); + chafa_canvas_config_set_symbol_map (config, symbol_map); + chafa_canvas_config_set_color_extractor (config, CHAFA_COLOR_EXTRACTOR_MEDIAN); + chafa_canvas_config_set_work_factor (config, 1.0f); + + /* Create canvas, draw to it and render output string */ + + canvas = chafa_canvas_new (config); + chafa_canvas_draw_all_pixels (canvas, + /* Cairo byte order is host native */ + G_BYTE_ORDER == G_LITTLE_ENDIAN + ? CHAFA_PIXEL_BGRA8_PREMULTIPLIED + : CHAFA_PIXEL_ARGB8_PREMULTIPLIED, + (const guint8 *) data, + width, + height, + stride); + gs = chafa_canvas_print (canvas, term_info); + + /* Print the string */ + + fwrite (gs->str, sizeof (char), gs->len, stdout); + + if (pixel_mode != CHAFA_PIXEL_MODE_SIXELS) + fputc ('\n', stdout); + + /* Free resources */ + + g_string_free (gs, TRUE); + chafa_canvas_unref (canvas); + chafa_canvas_config_unref (config); + chafa_symbol_map_unref (symbol_map); + chafa_term_info_unref (term_info); + g_strfreev (environ); +} + +#endif /* HAVE_CHAFA */ + +static inline cairo_status_t helper_cairo_surface_write_to_ansi_stream (cairo_surface_t *surface, cairo_write_func_t write_func, - void *closure); + void *closure) +{ + unsigned int width = cairo_image_surface_get_width (surface); + unsigned int height = cairo_image_surface_get_height (surface); + if (cairo_image_surface_get_format (surface) != CAIRO_FORMAT_RGB24) { + cairo_surface_t *new_surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height); + cairo_t *cr = cairo_create (new_surface); + if (cairo_image_surface_get_format (surface) == CAIRO_FORMAT_A8) { + cairo_set_source_rgb (cr, 0., 0., 0.); + cairo_paint (cr); + cairo_set_source_rgb (cr, 1., 1., 1.); + cairo_mask_surface (cr, surface, 0, 0); + } else { + cairo_set_source_rgb (cr, 1., 1., 1.); + cairo_paint (cr); + cairo_set_source_surface (cr, surface, 0, 0); + cairo_paint (cr); + } + cairo_destroy (cr); + surface = new_surface; + } else + cairo_surface_reference (surface); + + unsigned int stride = cairo_image_surface_get_stride (surface); + const uint32_t *data = (uint32_t *) (void *) cairo_image_surface_get_data (surface); + + /* We don't have rows to spare on the terminal window... + * Find the tight image top/bottom and only print in between. */ + + /* Use corner color as background color. */ + uint32_t bg_color = data ? * (uint32_t *) data : 0; + + /* Drop first row while empty */ + while (height) + { + unsigned int i; + for (i = 0; i < width; i++) + if (data[i] != bg_color) + break; + if (i < width) + break; + data += stride / 4; + height--; + } + + /* Drop last row while empty */ + unsigned int orig_height = height; + while (height) + { + const uint32_t *row = data + (height - 1) * stride / 4; + unsigned int i; + for (i = 0; i < width; i++) + if (row[i] != bg_color) + break; + if (i < width) + break; + height--; + } + if (height < orig_height) + height++; /* Add one last blank row for padding. */ + + if (width && height) + { +#ifdef HAVE_CHAFA + if (true) + chafa_print_image_rgb24 (data, width, height, stride); + else +#endif + ansi_print_image_rgb24 (data, width, height, stride / 4); + } + + cairo_surface_destroy (surface); + return CAIRO_STATUS_SUCCESS; +} #endif diff --git a/util/meson.build b/util/meson.build index d53712c02..5a520323e 100644 --- a/util/meson.build +++ b/util/meson.build @@ -2,7 +2,6 @@ hb_view_sources = [ 'hb-view.cc', 'options.cc', 'helper-cairo.cc', - 'helper-cairo-ansi.cc', ] hb_shape_sources = [ From c329ce1000ee202700582396b0a47918d815e22e Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Fri, 6 Aug 2021 19:13:21 -0600 Subject: [PATCH 18/33] [util] Also hide ppem settings in hb-ot-shape-closure Leaving ptem, as that can in theory change shape closure if we apply it to optical-size axis (we currently don't). --- util/options.cc | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/util/options.cc b/util/options.cc index 88082051a..31a98b14a 100644 --- a/util/options.cc +++ b/util/options.cc @@ -573,12 +573,15 @@ font_options_t::add_options (option_parser_t *parser) parser->free_later (font_size_text); } + unsigned font_size_flags = DEFAULT_FONT_SIZE == FONT_SIZE_NONE ? G_OPTION_FLAG_HIDDEN : 0; GOptionEntry entries[] = { - {"font-size", 0, DEFAULT_FONT_SIZE ? 0 : G_OPTION_FLAG_HIDDEN, + {"font-size", 0, font_size_flags, G_OPTION_ARG_CALLBACK, (gpointer) &parse_font_size, font_size_text, "1/2 integers or 'upem'"}, - {"font-ppem", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_font_ppem, "Set x,y pixels per EM (default: 0; disabled)", "1/2 integers"}, - {"font-ptem", 0, 0, G_OPTION_ARG_DOUBLE, &this->ptem, "Set font point-size (default: 0; disabled)", "point-size"}, + {"font-ppem", 0, font_size_flags, + G_OPTION_ARG_CALLBACK, (gpointer) &parse_font_ppem, "Set x,y pixels per EM (default: 0; disabled)", "1/2 integers"}, + {"font-ptem", 0, 0, + G_OPTION_ARG_DOUBLE, &this->ptem, "Set font point-size (default: 0; disabled)", "point-size"}, {"font-funcs", 0, 0, G_OPTION_ARG_STRING, &this->font_funcs, text, "impl"}, {"ft-load-flags", 0, 0, G_OPTION_ARG_INT, &this->ft_load_flags, "Set FreeType load-flags (default: 2)", "integer"}, {nullptr} From c5337c43e932f23c18380995fa1ec74e2d8eaeb7 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Fri, 6 Aug 2021 19:19:50 -0600 Subject: [PATCH 19/33] [util] Fold helper-cairo.cc into helper-cairo.hh --- util/Makefile.sources | 1 - util/helper-cairo.cc | 668 ------------------------------------------ util/helper-cairo.hh | 636 +++++++++++++++++++++++++++++++++++++++- util/meson.build | 1 - 4 files changed, 624 insertions(+), 682 deletions(-) delete mode 100644 util/helper-cairo.cc diff --git a/util/Makefile.sources b/util/Makefile.sources index e128a523f..82936092f 100644 --- a/util/Makefile.sources +++ b/util/Makefile.sources @@ -5,7 +5,6 @@ HB_VIEW_sources = \ main-font-text.hh \ shape-consumer.hh \ ansi-print.hh \ - helper-cairo.cc \ helper-cairo.hh \ helper-cairo-ansi.hh \ view-cairo.hh \ diff --git a/util/helper-cairo.cc b/util/helper-cairo.cc deleted file mode 100644 index d65051546..000000000 --- a/util/helper-cairo.cc +++ /dev/null @@ -1,668 +0,0 @@ -/* - * Copyright © 2011 Google, Inc. - * - * This is part of HarfBuzz, a text shaping library. - * - * Permission is hereby granted, without written agreement and without - * license or royalty fees, to use, copy, modify, and distribute this - * software and its documentation for any purpose, provided that the - * above copyright notice and the following two paragraphs appear in - * all copies of this software. - * - * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR - * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES - * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN - * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * - * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, - * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS - * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO - * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - * - * Google Author(s): Behdad Esfahbod - */ - -#include "helper-cairo.hh" - -#include -#include -#include FT_MULTIPLE_MASTERS_H - -#include "helper-cairo-ansi.hh" -#ifdef CAIRO_HAS_SVG_SURFACE -# include -#endif -#ifdef CAIRO_HAS_PDF_SURFACE -# include -#endif -#ifdef CAIRO_HAS_PS_SURFACE -# include -# if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1,6,0) -# define HAS_EPS 1 - -static cairo_surface_t * -_cairo_eps_surface_create_for_stream (cairo_write_func_t write_func, - void *closure, - double width, - double height) -{ - cairo_surface_t *surface; - - surface = cairo_ps_surface_create_for_stream (write_func, closure, width, height); - cairo_ps_surface_set_eps (surface, true); - - return surface; -} - -# else -# undef HAS_EPS -# endif -#endif - - -static FT_Library ft_library; - -#ifdef HAVE_ATEXIT -static inline -void free_ft_library () -{ - FT_Done_FreeType (ft_library); -} -#endif - -cairo_scaled_font_t * -helper_cairo_create_scaled_font (const font_options_t *font_opts) -{ - hb_font_t *font = hb_font_reference (font_opts->get_font ()); - - cairo_font_face_t *cairo_face; - /* We cannot use the FT_Face from hb_font_t, as doing so will confuse hb_font_t because - * cairo will reset the face size. As such, create new face... - * TODO Perhaps add API to hb-ft to encapsulate this code. */ - FT_Face ft_face = nullptr;//hb_ft_font_get_face (font); - if (!ft_face) - { - if (!ft_library) - { - FT_Init_FreeType (&ft_library); -#ifdef HAVE_ATEXIT - atexit (free_ft_library); -#endif - } - - unsigned int blob_length; - const char *blob_data = hb_blob_get_data (font_opts->get_blob (), &blob_length); - - if (FT_New_Memory_Face (ft_library, - (const FT_Byte *) blob_data, - blob_length, - font_opts->face_index, - &ft_face)) - fail (false, "FT_New_Memory_Face fail"); - } - if (!ft_face) - { - /* This allows us to get some boxes at least... */ - cairo_face = cairo_toy_font_face_create ("@cairo:sans", - CAIRO_FONT_SLANT_NORMAL, - CAIRO_FONT_WEIGHT_NORMAL); - } - else - { -#ifdef HAVE_FT_SET_VAR_BLEND_COORDINATES - unsigned int num_coords; - const int *coords = hb_font_get_var_coords_normalized (font, &num_coords); - if (num_coords) - { - FT_Fixed *ft_coords = (FT_Fixed *) calloc (num_coords, sizeof (FT_Fixed)); - if (ft_coords) - { - for (unsigned int i = 0; i < num_coords; i++) - ft_coords[i] = coords[i] << 2; - FT_Set_Var_Blend_Coordinates (ft_face, num_coords, ft_coords); - free (ft_coords); - } - } -#endif - - cairo_face = cairo_ft_font_face_create_for_ft_face (ft_face, font_opts->ft_load_flags); - } - cairo_matrix_t ctm, font_matrix; - cairo_font_options_t *font_options; - - cairo_matrix_init_identity (&ctm); - cairo_matrix_init_scale (&font_matrix, - font_opts->font_size_x, - font_opts->font_size_y); - font_options = cairo_font_options_create (); - cairo_font_options_set_hint_style (font_options, CAIRO_HINT_STYLE_NONE); - cairo_font_options_set_hint_metrics (font_options, CAIRO_HINT_METRICS_OFF); - - cairo_scaled_font_t *scaled_font = cairo_scaled_font_create (cairo_face, - &font_matrix, - &ctm, - font_options); - - cairo_font_options_destroy (font_options); - cairo_font_face_destroy (cairo_face); - - static cairo_user_data_key_t key; - if (cairo_scaled_font_set_user_data (scaled_font, - &key, - (void *) font, - (cairo_destroy_func_t) hb_font_destroy)) - hb_font_destroy (font); - - return scaled_font; -} - -bool -helper_cairo_scaled_font_has_color (cairo_scaled_font_t *scaled_font) -{ - bool ret = false; -#ifdef FT_HAS_COLOR - FT_Face ft_face = cairo_ft_scaled_font_lock_face (scaled_font); - if (ft_face) - { - if (FT_HAS_COLOR (ft_face)) - ret = true; - cairo_ft_scaled_font_unlock_face (scaled_font); - } -#endif - return ret; -} - - -enum class image_protocol_t { - NONE = 0, - ITERM2, - KITTY, -}; - -struct finalize_closure_t { - void (*callback)(finalize_closure_t *); - cairo_surface_t *surface; - cairo_write_func_t write_func; - void *closure; - image_protocol_t protocol; -}; -static cairo_user_data_key_t finalize_closure_key; - - -static void -finalize_ansi (finalize_closure_t *closure) -{ - cairo_status_t status; - status = helper_cairo_surface_write_to_ansi_stream (closure->surface, - closure->write_func, - closure->closure); - if (status != CAIRO_STATUS_SUCCESS) - fail (false, "Failed to write output: %s", - cairo_status_to_string (status)); -} - -static cairo_surface_t * -_cairo_ansi_surface_create_for_stream (cairo_write_func_t write_func, - void *closure, - double width, - double height, - cairo_content_t content, - image_protocol_t protocol HB_UNUSED) -{ - cairo_surface_t *surface; - int w = ceil (width); - int h = ceil (height); - - switch (content) { - case CAIRO_CONTENT_ALPHA: - surface = cairo_image_surface_create (CAIRO_FORMAT_A8, w, h); - break; - default: - case CAIRO_CONTENT_COLOR: - surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, w, h); - break; - case CAIRO_CONTENT_COLOR_ALPHA: - surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w, h); - break; - } - cairo_status_t status = cairo_surface_status (surface); - if (status != CAIRO_STATUS_SUCCESS) - fail (false, "Failed to create cairo surface: %s", - cairo_status_to_string (status)); - - finalize_closure_t *ansi_closure = g_new0 (finalize_closure_t, 1); - ansi_closure->callback = finalize_ansi; - ansi_closure->surface = surface; - ansi_closure->write_func = write_func; - ansi_closure->closure = closure; - - if (cairo_surface_set_user_data (surface, - &finalize_closure_key, - (void *) ansi_closure, - (cairo_destroy_func_t) g_free)) - g_free ((void *) closure); - - return surface; -} - - -#ifdef CAIRO_HAS_PNG_FUNCTIONS - -static cairo_status_t -byte_array_write_func (void *closure, - const unsigned char *data, - unsigned int size) -{ - g_byte_array_append ((GByteArray *) closure, data, size); - return CAIRO_STATUS_SUCCESS; -} - -static void -finalize_png (finalize_closure_t *closure) -{ - cairo_status_t status; - GByteArray *bytes = nullptr; - GString *string; - gchar *base64; - size_t base64_len; - - if (closure->protocol == image_protocol_t::NONE) - { - status = cairo_surface_write_to_png_stream (closure->surface, - closure->write_func, - closure->closure); - } - else - { - bytes = g_byte_array_new (); - status = cairo_surface_write_to_png_stream (closure->surface, - byte_array_write_func, - bytes); - } - - if (status != CAIRO_STATUS_SUCCESS) - fail (false, "Failed to write output: %s", - cairo_status_to_string (status)); - - if (closure->protocol == image_protocol_t::NONE) - return; - - base64 = g_base64_encode (bytes->data, bytes->len); - base64_len = strlen (base64); - - string = g_string_new (NULL); - if (closure->protocol == image_protocol_t::ITERM2) - { - /* https://iterm2.com/documentation-images.html */ - g_string_printf (string, "\033]1337;File=inline=1;size=%zu:%s\a\n", - base64_len, base64); - } - else if (closure->protocol == image_protocol_t::KITTY) - { -#define CHUNK_SIZE 4096 - /* https://sw.kovidgoyal.net/kitty/graphics-protocol.html */ - for (size_t pos = 0; pos < base64_len; pos += CHUNK_SIZE) - { - size_t len = base64_len - pos; - - if (pos == 0) - g_string_append (string, "\033_Ga=T,f=100,m="); - else - g_string_append (string, "\033_Gm="); - - if (len > CHUNK_SIZE) - { - g_string_append (string, "1;"); - g_string_append_len (string, base64 + pos, CHUNK_SIZE); - } - else - { - g_string_append (string, "0;"); - g_string_append_len (string, base64 + pos, len); - } - - g_string_append (string, "\033\\"); - } - g_string_append (string, "\n"); -#undef CHUNK_SIZE - } - - closure->write_func (closure->closure, (unsigned char *) string->str, string->len); - - g_byte_array_unref (bytes); - g_free (base64); - g_string_free (string, TRUE); -} - -static cairo_surface_t * -_cairo_png_surface_create_for_stream (cairo_write_func_t write_func, - void *closure, - double width, - double height, - cairo_content_t content, - image_protocol_t protocol) -{ - cairo_surface_t *surface; - int w = ceil (width); - int h = ceil (height); - - switch (content) { - case CAIRO_CONTENT_ALPHA: - surface = cairo_image_surface_create (CAIRO_FORMAT_A8, w, h); - break; - default: - case CAIRO_CONTENT_COLOR: - surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, w, h); - break; - case CAIRO_CONTENT_COLOR_ALPHA: - surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w, h); - break; - } - cairo_status_t status = cairo_surface_status (surface); - if (status != CAIRO_STATUS_SUCCESS) - fail (false, "Failed to create cairo surface: %s", - cairo_status_to_string (status)); - - finalize_closure_t *png_closure = g_new0 (finalize_closure_t, 1); - png_closure->callback = finalize_png; - png_closure->surface = surface; - png_closure->write_func = write_func; - png_closure->closure = closure; - png_closure->protocol = protocol; - - if (cairo_surface_set_user_data (surface, - &finalize_closure_key, - (void *) png_closure, - (cairo_destroy_func_t) g_free)) - g_free ((void *) closure); - - return surface; -} - -#endif - -static cairo_status_t -stdio_write_func (void *closure, - const unsigned char *data, - unsigned int size) -{ - FILE *fp = (FILE *) closure; - - while (size) { - size_t ret = fwrite (data, 1, size, fp); - size -= ret; - data += ret; - if (size && ferror (fp)) - fail (false, "Failed to write output: %s", strerror (errno)); - } - - return CAIRO_STATUS_SUCCESS; -} - -const char *helper_cairo_supported_formats[] = -{ - "ansi", - #ifdef CAIRO_HAS_PNG_FUNCTIONS - "png", - #endif - #ifdef CAIRO_HAS_SVG_SURFACE - "svg", - #endif - #ifdef CAIRO_HAS_PDF_SURFACE - "pdf", - #endif - #ifdef CAIRO_HAS_PS_SURFACE - "ps", - #ifdef HAS_EPS - "eps", - #endif - #endif - nullptr -}; - -cairo_t * -helper_cairo_create_context (double w, double h, - view_options_t *view_opts, - output_options_t *out_opts, - cairo_content_t content) -{ - cairo_surface_t *(*constructor) (cairo_write_func_t write_func, - void *closure, - double width, - double height) = nullptr; - cairo_surface_t *(*constructor2) (cairo_write_func_t write_func, - void *closure, - double width, - double height, - cairo_content_t content, - image_protocol_t protocol) = nullptr; - - image_protocol_t protocol = image_protocol_t::NONE; - const char *extension = out_opts->output_format; - if (!extension) { -#if HAVE_ISATTY - if (isatty (fileno (out_opts->get_file_handle ()))) - { -#ifdef CAIRO_HAS_PNG_FUNCTIONS - const char *name; - /* https://gitlab.com/gnachman/iterm2/-/issues/7154 */ - if ((name = getenv ("LC_TERMINAL")) != nullptr && - 0 == g_ascii_strcasecmp (name, "iTerm2")) - { - extension = "png"; - protocol = image_protocol_t::ITERM2; - } - else if ((name = getenv ("TERM")) != nullptr && - 0 == g_ascii_strcasecmp (name, "xterm-kitty")) - { - extension = "png"; - protocol = image_protocol_t::KITTY; - } - else - extension = "ansi"; -#else - extension = "ansi"; -#endif - } - else -#endif - { -#ifdef CAIRO_HAS_PNG_FUNCTIONS - extension = "png"; -#else - extension = "ansi"; -#endif - } - } - if (0) - ; - else if (0 == g_ascii_strcasecmp (extension, "ansi")) - constructor2 = _cairo_ansi_surface_create_for_stream; - #ifdef CAIRO_HAS_PNG_FUNCTIONS - else if (0 == g_ascii_strcasecmp (extension, "png")) - constructor2 = _cairo_png_surface_create_for_stream; - #endif - #ifdef CAIRO_HAS_SVG_SURFACE - else if (0 == g_ascii_strcasecmp (extension, "svg")) - constructor = cairo_svg_surface_create_for_stream; - #endif - #ifdef CAIRO_HAS_PDF_SURFACE - else if (0 == g_ascii_strcasecmp (extension, "pdf")) - constructor = cairo_pdf_surface_create_for_stream; - #endif - #ifdef CAIRO_HAS_PS_SURFACE - else if (0 == g_ascii_strcasecmp (extension, "ps")) - constructor = cairo_ps_surface_create_for_stream; - #ifdef HAS_EPS - else if (0 == g_ascii_strcasecmp (extension, "eps")) - constructor = _cairo_eps_surface_create_for_stream; - #endif - #endif - - - unsigned int fr, fg, fb, fa, br, bg, bb, ba; - const char *color; - br = bg = bb = 0; ba = 255; - color = view_opts->back ? view_opts->back : DEFAULT_BACK; - sscanf (color + (*color=='#'), "%2x%2x%2x%2x", &br, &bg, &bb, &ba); - fr = fg = fb = 0; fa = 255; - color = view_opts->fore ? view_opts->fore : DEFAULT_FORE; - sscanf (color + (*color=='#'), "%2x%2x%2x%2x", &fr, &fg, &fb, &fa); - - if (content == CAIRO_CONTENT_ALPHA) - { - if (view_opts->annotate || - br != bg || bg != bb || - fr != fg || fg != fb) - content = CAIRO_CONTENT_COLOR; - } - if (ba != 255) - content = CAIRO_CONTENT_COLOR_ALPHA; - - cairo_surface_t *surface; - FILE *f = out_opts->get_file_handle (); - if (constructor) - surface = constructor (stdio_write_func, f, w, h); - else if (constructor2) - surface = constructor2 (stdio_write_func, f, w, h, content, protocol); - else - fail (false, "Unknown output format `%s'; supported formats are: %s%s", - extension, - g_strjoinv ("/", const_cast (helper_cairo_supported_formats)), - out_opts->explicit_output_format ? "" : - "\nTry setting format using --output-format"); - - cairo_t *cr = cairo_create (surface); - content = cairo_surface_get_content (surface); - - switch (content) { - case CAIRO_CONTENT_ALPHA: - cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); - cairo_set_source_rgba (cr, 1., 1., 1., br / 255.); - cairo_paint (cr); - cairo_set_source_rgba (cr, 1., 1., 1., - (fr / 255.) * (fa / 255.) + (br / 255) * (1 - (fa / 255.))); - break; - default: - case CAIRO_CONTENT_COLOR: - case CAIRO_CONTENT_COLOR_ALPHA: - cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); - cairo_set_source_rgba (cr, br / 255., bg / 255., bb / 255., ba / 255.); - cairo_paint (cr); - cairo_set_operator (cr, CAIRO_OPERATOR_OVER); - cairo_set_source_rgba (cr, fr / 255., fg / 255., fb / 255., fa / 255.); - break; - } - - cairo_surface_destroy (surface); - return cr; -} - -void -helper_cairo_destroy_context (cairo_t *cr) -{ - finalize_closure_t *closure = (finalize_closure_t *) - cairo_surface_get_user_data (cairo_get_target (cr), - &finalize_closure_key); - if (closure) - closure->callback (closure); - - cairo_status_t status = cairo_status (cr); - if (status != CAIRO_STATUS_SUCCESS) - fail (false, "Failed: %s", - cairo_status_to_string (status)); - cairo_destroy (cr); -} - - -void -helper_cairo_line_from_buffer (helper_cairo_line_t *l, - hb_buffer_t *buffer, - const char *text, - unsigned int text_len, - int scale_bits, - hb_bool_t utf8_clusters) -{ - memset (l, 0, sizeof (*l)); - - l->num_glyphs = hb_buffer_get_length (buffer); - hb_glyph_info_t *hb_glyph = hb_buffer_get_glyph_infos (buffer, nullptr); - hb_glyph_position_t *hb_position = hb_buffer_get_glyph_positions (buffer, nullptr); - l->glyphs = cairo_glyph_allocate (l->num_glyphs + 1); - - if (text) { - l->utf8 = g_strndup (text, text_len); - l->utf8_len = text_len; - l->num_clusters = l->num_glyphs ? 1 : 0; - for (unsigned int i = 1; i < l->num_glyphs; i++) - if (hb_glyph[i].cluster != hb_glyph[i-1].cluster) - l->num_clusters++; - l->clusters = cairo_text_cluster_allocate (l->num_clusters); - } - - if ((l->num_glyphs && !l->glyphs) || - (l->utf8_len && !l->utf8) || - (l->num_clusters && !l->clusters)) - { - l->finish (); - return; - } - - hb_position_t x = 0, y = 0; - int i; - for (i = 0; i < (int) l->num_glyphs; i++) - { - l->glyphs[i].index = hb_glyph[i].codepoint; - l->glyphs[i].x = scalbn ((double) hb_position->x_offset + x, scale_bits); - l->glyphs[i].y = scalbn ((double) -hb_position->y_offset + y, scale_bits); - x += hb_position->x_advance; - y += -hb_position->y_advance; - - hb_position++; - } - l->glyphs[i].index = -1; - l->glyphs[i].x = scalbn ((double) x, scale_bits); - l->glyphs[i].y = scalbn ((double) y, scale_bits); - - if (l->num_clusters) { - memset ((void *) l->clusters, 0, l->num_clusters * sizeof (l->clusters[0])); - hb_bool_t backward = HB_DIRECTION_IS_BACKWARD (hb_buffer_get_direction (buffer)); - l->cluster_flags = backward ? CAIRO_TEXT_CLUSTER_FLAG_BACKWARD : (cairo_text_cluster_flags_t) 0; - unsigned int cluster = 0; - const char *start = l->utf8, *end; - l->clusters[cluster].num_glyphs++; - if (backward) { - for (i = l->num_glyphs - 2; i >= 0; i--) { - if (hb_glyph[i].cluster != hb_glyph[i+1].cluster) { - g_assert (hb_glyph[i].cluster > hb_glyph[i+1].cluster); - if (utf8_clusters) - end = start + hb_glyph[i].cluster - hb_glyph[i+1].cluster; - else - end = g_utf8_offset_to_pointer (start, hb_glyph[i].cluster - hb_glyph[i+1].cluster); - l->clusters[cluster].num_bytes = end - start; - start = end; - cluster++; - } - l->clusters[cluster].num_glyphs++; - } - l->clusters[cluster].num_bytes = l->utf8 + text_len - start; - } else { - for (i = 1; i < (int) l->num_glyphs; i++) { - if (hb_glyph[i].cluster != hb_glyph[i-1].cluster) { - g_assert (hb_glyph[i].cluster > hb_glyph[i-1].cluster); - if (utf8_clusters) - end = start + hb_glyph[i].cluster - hb_glyph[i-1].cluster; - else - end = g_utf8_offset_to_pointer (start, hb_glyph[i].cluster - hb_glyph[i-1].cluster); - l->clusters[cluster].num_bytes = end - start; - start = end; - cluster++; - } - l->clusters[cluster].num_glyphs++; - } - l->clusters[cluster].num_bytes = l->utf8 + text_len - start; - } - } -} diff --git a/util/helper-cairo.hh b/util/helper-cairo.hh index 5bfbf7b34..9a1cd4c5a 100644 --- a/util/helper-cairo.hh +++ b/util/helper-cairo.hh @@ -30,25 +30,555 @@ #include "hb.hh" #include "options.hh" -#include +#include +#include +#include FT_MULTIPLE_MASTERS_H + +#include "helper-cairo-ansi.hh" +#ifdef CAIRO_HAS_SVG_SURFACE +# include +#endif +#ifdef CAIRO_HAS_PDF_SURFACE +# include +#endif +#ifdef CAIRO_HAS_PS_SURFACE +# include +# if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1,6,0) +# define HAS_EPS 1 + +static cairo_surface_t * +_cairo_eps_surface_create_for_stream (cairo_write_func_t write_func, + void *closure, + double width, + double height) +{ + cairo_surface_t *surface; + + surface = cairo_ps_surface_create_for_stream (write_func, closure, width, height); + cairo_ps_surface_set_eps (surface, true); + + return surface; +} + +# else +# undef HAS_EPS +# endif +#endif -cairo_scaled_font_t * -helper_cairo_create_scaled_font (const font_options_t *font_opts); +static FT_Library ft_library; -bool -helper_cairo_scaled_font_has_color (cairo_scaled_font_t *scaled_font); +#ifdef HAVE_ATEXIT +static inline +void free_ft_library () +{ + FT_Done_FreeType (ft_library); +} +#endif -extern const char *helper_cairo_supported_formats[]; +static inline cairo_scaled_font_t * +helper_cairo_create_scaled_font (const font_options_t *font_opts) +{ + hb_font_t *font = hb_font_reference (font_opts->get_font ()); -cairo_t * + cairo_font_face_t *cairo_face; + /* We cannot use the FT_Face from hb_font_t, as doing so will confuse hb_font_t because + * cairo will reset the face size. As such, create new face... + * TODO Perhaps add API to hb-ft to encapsulate this code. */ + FT_Face ft_face = nullptr;//hb_ft_font_get_face (font); + if (!ft_face) + { + if (!ft_library) + { + FT_Init_FreeType (&ft_library); +#ifdef HAVE_ATEXIT + atexit (free_ft_library); +#endif + } + + unsigned int blob_length; + const char *blob_data = hb_blob_get_data (font_opts->get_blob (), &blob_length); + + if (FT_New_Memory_Face (ft_library, + (const FT_Byte *) blob_data, + blob_length, + font_opts->face_index, + &ft_face)) + fail (false, "FT_New_Memory_Face fail"); + } + if (!ft_face) + { + /* This allows us to get some boxes at least... */ + cairo_face = cairo_toy_font_face_create ("@cairo:sans", + CAIRO_FONT_SLANT_NORMAL, + CAIRO_FONT_WEIGHT_NORMAL); + } + else + { +#ifdef HAVE_FT_SET_VAR_BLEND_COORDINATES + unsigned int num_coords; + const int *coords = hb_font_get_var_coords_normalized (font, &num_coords); + if (num_coords) + { + FT_Fixed *ft_coords = (FT_Fixed *) calloc (num_coords, sizeof (FT_Fixed)); + if (ft_coords) + { + for (unsigned int i = 0; i < num_coords; i++) + ft_coords[i] = coords[i] << 2; + FT_Set_Var_Blend_Coordinates (ft_face, num_coords, ft_coords); + free (ft_coords); + } + } +#endif + + cairo_face = cairo_ft_font_face_create_for_ft_face (ft_face, font_opts->ft_load_flags); + } + cairo_matrix_t ctm, font_matrix; + cairo_font_options_t *font_options; + + cairo_matrix_init_identity (&ctm); + cairo_matrix_init_scale (&font_matrix, + font_opts->font_size_x, + font_opts->font_size_y); + font_options = cairo_font_options_create (); + cairo_font_options_set_hint_style (font_options, CAIRO_HINT_STYLE_NONE); + cairo_font_options_set_hint_metrics (font_options, CAIRO_HINT_METRICS_OFF); + + cairo_scaled_font_t *scaled_font = cairo_scaled_font_create (cairo_face, + &font_matrix, + &ctm, + font_options); + + cairo_font_options_destroy (font_options); + cairo_font_face_destroy (cairo_face); + + static cairo_user_data_key_t key; + if (cairo_scaled_font_set_user_data (scaled_font, + &key, + (void *) font, + (cairo_destroy_func_t) hb_font_destroy)) + hb_font_destroy (font); + + return scaled_font; +} + +static inline bool +helper_cairo_scaled_font_has_color (cairo_scaled_font_t *scaled_font) +{ + bool ret = false; +#ifdef FT_HAS_COLOR + FT_Face ft_face = cairo_ft_scaled_font_lock_face (scaled_font); + if (ft_face) + { + if (FT_HAS_COLOR (ft_face)) + ret = true; + cairo_ft_scaled_font_unlock_face (scaled_font); + } +#endif + return ret; +} + + +enum class image_protocol_t { + NONE = 0, + ITERM2, + KITTY, +}; + +struct finalize_closure_t { + void (*callback)(finalize_closure_t *); + cairo_surface_t *surface; + cairo_write_func_t write_func; + void *closure; + image_protocol_t protocol; +}; +static cairo_user_data_key_t finalize_closure_key; + + +static void +finalize_ansi (finalize_closure_t *closure) +{ + cairo_status_t status; + status = helper_cairo_surface_write_to_ansi_stream (closure->surface, + closure->write_func, + closure->closure); + if (status != CAIRO_STATUS_SUCCESS) + fail (false, "Failed to write output: %s", + cairo_status_to_string (status)); +} + +static cairo_surface_t * +_cairo_ansi_surface_create_for_stream (cairo_write_func_t write_func, + void *closure, + double width, + double height, + cairo_content_t content, + image_protocol_t protocol HB_UNUSED) +{ + cairo_surface_t *surface; + int w = ceil (width); + int h = ceil (height); + + switch (content) { + case CAIRO_CONTENT_ALPHA: + surface = cairo_image_surface_create (CAIRO_FORMAT_A8, w, h); + break; + default: + case CAIRO_CONTENT_COLOR: + surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, w, h); + break; + case CAIRO_CONTENT_COLOR_ALPHA: + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w, h); + break; + } + cairo_status_t status = cairo_surface_status (surface); + if (status != CAIRO_STATUS_SUCCESS) + fail (false, "Failed to create cairo surface: %s", + cairo_status_to_string (status)); + + finalize_closure_t *ansi_closure = g_new0 (finalize_closure_t, 1); + ansi_closure->callback = finalize_ansi; + ansi_closure->surface = surface; + ansi_closure->write_func = write_func; + ansi_closure->closure = closure; + + if (cairo_surface_set_user_data (surface, + &finalize_closure_key, + (void *) ansi_closure, + (cairo_destroy_func_t) g_free)) + g_free ((void *) closure); + + return surface; +} + + +#ifdef CAIRO_HAS_PNG_FUNCTIONS + +static cairo_status_t +byte_array_write_func (void *closure, + const unsigned char *data, + unsigned int size) +{ + g_byte_array_append ((GByteArray *) closure, data, size); + return CAIRO_STATUS_SUCCESS; +} + +static void +finalize_png (finalize_closure_t *closure) +{ + cairo_status_t status; + GByteArray *bytes = nullptr; + GString *string; + gchar *base64; + size_t base64_len; + + if (closure->protocol == image_protocol_t::NONE) + { + status = cairo_surface_write_to_png_stream (closure->surface, + closure->write_func, + closure->closure); + } + else + { + bytes = g_byte_array_new (); + status = cairo_surface_write_to_png_stream (closure->surface, + byte_array_write_func, + bytes); + } + + if (status != CAIRO_STATUS_SUCCESS) + fail (false, "Failed to write output: %s", + cairo_status_to_string (status)); + + if (closure->protocol == image_protocol_t::NONE) + return; + + base64 = g_base64_encode (bytes->data, bytes->len); + base64_len = strlen (base64); + + string = g_string_new (NULL); + if (closure->protocol == image_protocol_t::ITERM2) + { + /* https://iterm2.com/documentation-images.html */ + g_string_printf (string, "\033]1337;File=inline=1;size=%zu:%s\a\n", + base64_len, base64); + } + else if (closure->protocol == image_protocol_t::KITTY) + { +#define CHUNK_SIZE 4096 + /* https://sw.kovidgoyal.net/kitty/graphics-protocol.html */ + for (size_t pos = 0; pos < base64_len; pos += CHUNK_SIZE) + { + size_t len = base64_len - pos; + + if (pos == 0) + g_string_append (string, "\033_Ga=T,f=100,m="); + else + g_string_append (string, "\033_Gm="); + + if (len > CHUNK_SIZE) + { + g_string_append (string, "1;"); + g_string_append_len (string, base64 + pos, CHUNK_SIZE); + } + else + { + g_string_append (string, "0;"); + g_string_append_len (string, base64 + pos, len); + } + + g_string_append (string, "\033\\"); + } + g_string_append (string, "\n"); +#undef CHUNK_SIZE + } + + closure->write_func (closure->closure, (unsigned char *) string->str, string->len); + + g_byte_array_unref (bytes); + g_free (base64); + g_string_free (string, TRUE); +} + +static cairo_surface_t * +_cairo_png_surface_create_for_stream (cairo_write_func_t write_func, + void *closure, + double width, + double height, + cairo_content_t content, + image_protocol_t protocol) +{ + cairo_surface_t *surface; + int w = ceil (width); + int h = ceil (height); + + switch (content) { + case CAIRO_CONTENT_ALPHA: + surface = cairo_image_surface_create (CAIRO_FORMAT_A8, w, h); + break; + default: + case CAIRO_CONTENT_COLOR: + surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, w, h); + break; + case CAIRO_CONTENT_COLOR_ALPHA: + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w, h); + break; + } + cairo_status_t status = cairo_surface_status (surface); + if (status != CAIRO_STATUS_SUCCESS) + fail (false, "Failed to create cairo surface: %s", + cairo_status_to_string (status)); + + finalize_closure_t *png_closure = g_new0 (finalize_closure_t, 1); + png_closure->callback = finalize_png; + png_closure->surface = surface; + png_closure->write_func = write_func; + png_closure->closure = closure; + png_closure->protocol = protocol; + + if (cairo_surface_set_user_data (surface, + &finalize_closure_key, + (void *) png_closure, + (cairo_destroy_func_t) g_free)) + g_free ((void *) closure); + + return surface; +} + +#endif + +static cairo_status_t +stdio_write_func (void *closure, + const unsigned char *data, + unsigned int size) +{ + FILE *fp = (FILE *) closure; + + while (size) { + size_t ret = fwrite (data, 1, size, fp); + size -= ret; + data += ret; + if (size && ferror (fp)) + fail (false, "Failed to write output: %s", strerror (errno)); + } + + return CAIRO_STATUS_SUCCESS; +} + +static const char *helper_cairo_supported_formats[] = +{ + "ansi", + #ifdef CAIRO_HAS_PNG_FUNCTIONS + "png", + #endif + #ifdef CAIRO_HAS_SVG_SURFACE + "svg", + #endif + #ifdef CAIRO_HAS_PDF_SURFACE + "pdf", + #endif + #ifdef CAIRO_HAS_PS_SURFACE + "ps", + #ifdef HAS_EPS + "eps", + #endif + #endif + nullptr +}; + +static inline cairo_t * helper_cairo_create_context (double w, double h, view_options_t *view_opts, output_options_t *out_opts, - cairo_content_t content); + cairo_content_t content) +{ + cairo_surface_t *(*constructor) (cairo_write_func_t write_func, + void *closure, + double width, + double height) = nullptr; + cairo_surface_t *(*constructor2) (cairo_write_func_t write_func, + void *closure, + double width, + double height, + cairo_content_t content, + image_protocol_t protocol) = nullptr; -void -helper_cairo_destroy_context (cairo_t *cr); + image_protocol_t protocol = image_protocol_t::NONE; + const char *extension = out_opts->output_format; + if (!extension) { +#if HAVE_ISATTY + if (isatty (fileno (out_opts->get_file_handle ()))) + { +#ifdef CAIRO_HAS_PNG_FUNCTIONS + const char *name; + /* https://gitlab.com/gnachman/iterm2/-/issues/7154 */ + if ((name = getenv ("LC_TERMINAL")) != nullptr && + 0 == g_ascii_strcasecmp (name, "iTerm2")) + { + extension = "png"; + protocol = image_protocol_t::ITERM2; + } + else if ((name = getenv ("TERM")) != nullptr && + 0 == g_ascii_strcasecmp (name, "xterm-kitty")) + { + extension = "png"; + protocol = image_protocol_t::KITTY; + } + else + extension = "ansi"; +#else + extension = "ansi"; +#endif + } + else +#endif + { +#ifdef CAIRO_HAS_PNG_FUNCTIONS + extension = "png"; +#else + extension = "ansi"; +#endif + } + } + if (0) + ; + else if (0 == g_ascii_strcasecmp (extension, "ansi")) + constructor2 = _cairo_ansi_surface_create_for_stream; + #ifdef CAIRO_HAS_PNG_FUNCTIONS + else if (0 == g_ascii_strcasecmp (extension, "png")) + constructor2 = _cairo_png_surface_create_for_stream; + #endif + #ifdef CAIRO_HAS_SVG_SURFACE + else if (0 == g_ascii_strcasecmp (extension, "svg")) + constructor = cairo_svg_surface_create_for_stream; + #endif + #ifdef CAIRO_HAS_PDF_SURFACE + else if (0 == g_ascii_strcasecmp (extension, "pdf")) + constructor = cairo_pdf_surface_create_for_stream; + #endif + #ifdef CAIRO_HAS_PS_SURFACE + else if (0 == g_ascii_strcasecmp (extension, "ps")) + constructor = cairo_ps_surface_create_for_stream; + #ifdef HAS_EPS + else if (0 == g_ascii_strcasecmp (extension, "eps")) + constructor = _cairo_eps_surface_create_for_stream; + #endif + #endif + + + unsigned int fr, fg, fb, fa, br, bg, bb, ba; + const char *color; + br = bg = bb = 0; ba = 255; + color = view_opts->back ? view_opts->back : DEFAULT_BACK; + sscanf (color + (*color=='#'), "%2x%2x%2x%2x", &br, &bg, &bb, &ba); + fr = fg = fb = 0; fa = 255; + color = view_opts->fore ? view_opts->fore : DEFAULT_FORE; + sscanf (color + (*color=='#'), "%2x%2x%2x%2x", &fr, &fg, &fb, &fa); + + if (content == CAIRO_CONTENT_ALPHA) + { + if (view_opts->annotate || + br != bg || bg != bb || + fr != fg || fg != fb) + content = CAIRO_CONTENT_COLOR; + } + if (ba != 255) + content = CAIRO_CONTENT_COLOR_ALPHA; + + cairo_surface_t *surface; + FILE *f = out_opts->get_file_handle (); + if (constructor) + surface = constructor (stdio_write_func, f, w, h); + else if (constructor2) + surface = constructor2 (stdio_write_func, f, w, h, content, protocol); + else + fail (false, "Unknown output format `%s'; supported formats are: %s%s", + extension, + g_strjoinv ("/", const_cast (helper_cairo_supported_formats)), + out_opts->explicit_output_format ? "" : + "\nTry setting format using --output-format"); + + cairo_t *cr = cairo_create (surface); + content = cairo_surface_get_content (surface); + + switch (content) { + case CAIRO_CONTENT_ALPHA: + cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); + cairo_set_source_rgba (cr, 1., 1., 1., br / 255.); + cairo_paint (cr); + cairo_set_source_rgba (cr, 1., 1., 1., + (fr / 255.) * (fa / 255.) + (br / 255) * (1 - (fa / 255.))); + break; + default: + case CAIRO_CONTENT_COLOR: + case CAIRO_CONTENT_COLOR_ALPHA: + cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); + cairo_set_source_rgba (cr, br / 255., bg / 255., bb / 255., ba / 255.); + cairo_paint (cr); + cairo_set_operator (cr, CAIRO_OPERATOR_OVER); + cairo_set_source_rgba (cr, fr / 255., fg / 255., fb / 255., fa / 255.); + break; + } + + cairo_surface_destroy (surface); + return cr; +} + +static inline void +helper_cairo_destroy_context (cairo_t *cr) +{ + finalize_closure_t *closure = (finalize_closure_t *) + cairo_surface_get_user_data (cairo_get_target (cr), + &finalize_closure_key); + if (closure) + closure->callback (closure); + + cairo_status_t status = cairo_status (cr); + if (status != CAIRO_STATUS_SUCCESS) + fail (false, "Failed: %s", + cairo_status_to_string (status)); + cairo_destroy (cr); +} struct helper_cairo_line_t { @@ -75,12 +605,94 @@ struct helper_cairo_line_t { } }; -void +static inline void helper_cairo_line_from_buffer (helper_cairo_line_t *l, hb_buffer_t *buffer, const char *text, unsigned int text_len, int scale_bits, - hb_bool_t utf8_clusters); + hb_bool_t utf8_clusters) +{ + memset (l, 0, sizeof (*l)); + + l->num_glyphs = hb_buffer_get_length (buffer); + hb_glyph_info_t *hb_glyph = hb_buffer_get_glyph_infos (buffer, nullptr); + hb_glyph_position_t *hb_position = hb_buffer_get_glyph_positions (buffer, nullptr); + l->glyphs = cairo_glyph_allocate (l->num_glyphs + 1); + + if (text) { + l->utf8 = g_strndup (text, text_len); + l->utf8_len = text_len; + l->num_clusters = l->num_glyphs ? 1 : 0; + for (unsigned int i = 1; i < l->num_glyphs; i++) + if (hb_glyph[i].cluster != hb_glyph[i-1].cluster) + l->num_clusters++; + l->clusters = cairo_text_cluster_allocate (l->num_clusters); + } + + if ((l->num_glyphs && !l->glyphs) || + (l->utf8_len && !l->utf8) || + (l->num_clusters && !l->clusters)) + { + l->finish (); + return; + } + + hb_position_t x = 0, y = 0; + int i; + for (i = 0; i < (int) l->num_glyphs; i++) + { + l->glyphs[i].index = hb_glyph[i].codepoint; + l->glyphs[i].x = scalbn ((double) hb_position->x_offset + x, scale_bits); + l->glyphs[i].y = scalbn ((double) -hb_position->y_offset + y, scale_bits); + x += hb_position->x_advance; + y += -hb_position->y_advance; + + hb_position++; + } + l->glyphs[i].index = -1; + l->glyphs[i].x = scalbn ((double) x, scale_bits); + l->glyphs[i].y = scalbn ((double) y, scale_bits); + + if (l->num_clusters) { + memset ((void *) l->clusters, 0, l->num_clusters * sizeof (l->clusters[0])); + hb_bool_t backward = HB_DIRECTION_IS_BACKWARD (hb_buffer_get_direction (buffer)); + l->cluster_flags = backward ? CAIRO_TEXT_CLUSTER_FLAG_BACKWARD : (cairo_text_cluster_flags_t) 0; + unsigned int cluster = 0; + const char *start = l->utf8, *end; + l->clusters[cluster].num_glyphs++; + if (backward) { + for (i = l->num_glyphs - 2; i >= 0; i--) { + if (hb_glyph[i].cluster != hb_glyph[i+1].cluster) { + g_assert (hb_glyph[i].cluster > hb_glyph[i+1].cluster); + if (utf8_clusters) + end = start + hb_glyph[i].cluster - hb_glyph[i+1].cluster; + else + end = g_utf8_offset_to_pointer (start, hb_glyph[i].cluster - hb_glyph[i+1].cluster); + l->clusters[cluster].num_bytes = end - start; + start = end; + cluster++; + } + l->clusters[cluster].num_glyphs++; + } + l->clusters[cluster].num_bytes = l->utf8 + text_len - start; + } else { + for (i = 1; i < (int) l->num_glyphs; i++) { + if (hb_glyph[i].cluster != hb_glyph[i-1].cluster) { + g_assert (hb_glyph[i].cluster > hb_glyph[i-1].cluster); + if (utf8_clusters) + end = start + hb_glyph[i].cluster - hb_glyph[i-1].cluster; + else + end = g_utf8_offset_to_pointer (start, hb_glyph[i].cluster - hb_glyph[i-1].cluster); + l->clusters[cluster].num_bytes = end - start; + start = end; + cluster++; + } + l->clusters[cluster].num_glyphs++; + } + l->clusters[cluster].num_bytes = l->utf8 + text_len - start; + } + } +} #endif diff --git a/util/meson.build b/util/meson.build index 5a520323e..cb724f6cb 100644 --- a/util/meson.build +++ b/util/meson.build @@ -1,7 +1,6 @@ hb_view_sources = [ 'hb-view.cc', 'options.cc', - 'helper-cairo.cc', ] hb_shape_sources = [ From 06d661803c79e18bad3e943894a8a7bcba1352e8 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Fri, 6 Aug 2021 19:20:47 -0600 Subject: [PATCH 20/33] [util] Fix compiler error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ../util/options.cc:588:3: error: narrowing conversion of ‘font_size_flags’ from ‘unsigned int’ to ‘gint {aka int}’ inside { } [-Werror=narrowing] --- util/options.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/options.cc b/util/options.cc index 31a98b14a..641376767 100644 --- a/util/options.cc +++ b/util/options.cc @@ -573,7 +573,7 @@ font_options_t::add_options (option_parser_t *parser) parser->free_later (font_size_text); } - unsigned font_size_flags = DEFAULT_FONT_SIZE == FONT_SIZE_NONE ? G_OPTION_FLAG_HIDDEN : 0; + int font_size_flags = DEFAULT_FONT_SIZE == FONT_SIZE_NONE ? G_OPTION_FLAG_HIDDEN : 0; GOptionEntry entries[] = { {"font-size", 0, font_size_flags, From 4e97678ebb77be96765942051d8bb03ab8f74f1c Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Fri, 6 Aug 2021 22:53:44 -0600 Subject: [PATCH 21/33] [util] Use inheritence to embed inherent options of objects --- util/hb-subset.cc | 9 ++++----- util/shape-consumer.hh | 15 +++++++-------- util/view-cairo.hh | 33 ++++++++++++++++----------------- 3 files changed, 27 insertions(+), 30 deletions(-) diff --git a/util/hb-subset.cc b/util/hb-subset.cc index 4519c38b3..3e06d16e2 100644 --- a/util/hb-subset.cc +++ b/util/hb-subset.cc @@ -38,18 +38,18 @@ const unsigned SUBPIXEL_BITS = 0; * Command line interface to the harfbuzz font subsetter. */ -struct subset_consumer_t +struct subset_consumer_t : subset_options_t { void add_options (option_parser_t *parser) { + subset_options_t::add_options (parser); output_options.add_options (parser); - subset_options.add_options (parser); } void init (const face_options_t *face_opts) { face = hb_face_reference (face_opts->get_face ()); - input = hb_subset_input_reference (subset_options.get_input ()); + input = hb_subset_input_reference (get_input ()); } void consume_line (const char *text, @@ -102,7 +102,7 @@ struct subset_consumer_t void finish (const face_options_t *face_opts) { hb_face_t *new_face = nullptr; - for (unsigned i = 0; i < subset_options.num_iterations; i++) + for (unsigned i = 0; i < num_iterations; i++) { hb_face_destroy (new_face); new_face = hb_subset_or_fail (face, input); @@ -126,7 +126,6 @@ struct subset_consumer_t private: output_options_t output_options; - subset_options_t subset_options; hb_face_t *face = nullptr; hb_subset_input_t *input = nullptr; diff --git a/util/shape-consumer.hh b/util/shape-consumer.hh index 6a0d767c0..3c18605cc 100644 --- a/util/shape-consumer.hh +++ b/util/shape-consumer.hh @@ -32,11 +32,11 @@ template -struct shape_consumer_t +struct shape_consumer_t : shape_options_t { void add_options (option_parser_t *parser) { - shaper.add_options (parser); + shape_options_t::add_options (parser); output.add_options (parser); } @@ -55,14 +55,14 @@ struct shape_consumer_t { output.new_line (); - for (unsigned int n = shaper.num_iterations; n; n--) + for (unsigned int n = num_iterations; n; n--) { const char *error = nullptr; - shaper.populate_buffer (buffer, text, text_len, text_before, text_after); + populate_buffer (buffer, text, text_len, text_before, text_after); if (n == 1) - output.consume_text (buffer, text, text_len, shaper.utf8_clusters); - if (!shaper.shape (font, buffer, &error)) + output.consume_text (buffer, text, text_len, utf8_clusters); + if (!shape (font, buffer, &error)) { failed = true; output.error (error); @@ -73,7 +73,7 @@ struct shape_consumer_t } } - output.consume_glyphs (buffer, text, text_len, shaper.utf8_clusters); + output.consume_glyphs (buffer, text, text_len, utf8_clusters); } void finish (const font_options_t *font_opts) { @@ -88,7 +88,6 @@ struct shape_consumer_t bool failed = false; protected: - shape_options_t shaper; output_t output; hb_font_t *font = nullptr; diff --git a/util/view-cairo.hh b/util/view-cairo.hh index 265f31486..2599eb4ae 100644 --- a/util/view-cairo.hh +++ b/util/view-cairo.hh @@ -32,7 +32,7 @@ #include "helper-cairo.hh" -struct view_cairo_t +struct view_cairo_t : view_options_t { ~view_cairo_t () { @@ -41,8 +41,8 @@ struct view_cairo_t void add_options (option_parser_t *parser) { + view_options_t::add_options (parser); output_options.add_options (parser, helper_cairo_supported_formats); - view_options.add_options (parser); } void init (hb_buffer_t *buffer, const font_options_t *font_opts) @@ -87,7 +87,6 @@ struct view_cairo_t void render (const font_options_t *font_opts); output_options_t output_options; - view_options_t view_options; hb_direction_t direction = HB_DIRECTION_INVALID; // Remove this, make segment_properties accessible GArray *lines = nullptr; @@ -106,24 +105,24 @@ view_cairo_t::render (const font_options_t *font_opts) hb_font_t *font = font_opts->get_font(); - view_options_t::font_extents_t extents = view_options.font_extents; - if (!view_options.have_font_extents) + if (!have_font_extents) { hb_font_extents_t hb_extents; hb_font_get_extents_for_direction (font, direction, &hb_extents); - extents.ascent = scalbn ((double) hb_extents.ascender, scale_bits); - extents.descent = -scalbn ((double) hb_extents.descender, scale_bits); - extents.line_gap = scalbn ((double) hb_extents.line_gap, scale_bits); + font_extents.ascent = scalbn ((double) hb_extents.ascender, scale_bits); + font_extents.descent = -scalbn ((double) hb_extents.descender, scale_bits); + font_extents.line_gap = scalbn ((double) hb_extents.line_gap, scale_bits); + have_font_extents = true; } - double ascent = y_sign * extents.ascent; - double descent = y_sign * extents.descent; - double line_gap = y_sign * extents.line_gap + view_options.line_space; + double ascent = y_sign * font_extents.ascent; + double descent = y_sign * font_extents.descent; + double line_gap = y_sign * font_extents.line_gap + line_space; double leading = ascent + descent + line_gap; /* Calculate surface size. */ double w = 0, h = 0; - (vertical ? w : h) = (int) lines->len * leading - (extents.line_gap + view_options.line_space); + (vertical ? w : h) = (int) lines->len * leading - (font_extents.line_gap + line_space); (vertical ? h : w) = 0; for (unsigned int i = 0; i < lines->len; i++) { helper_cairo_line_t &line = g_array_index (lines, helper_cairo_line_t, i); @@ -143,13 +142,13 @@ view_cairo_t::render (const font_options_t *font_opts) content = CAIRO_CONTENT_COLOR; /* Create surface. */ - cairo_t *cr = helper_cairo_create_context (w + view_options.margin.l + view_options.margin.r, - h + view_options.margin.t + view_options.margin.b, - &view_options, &output_options, content); + cairo_t *cr = helper_cairo_create_context (w + margin.l + margin.r, + h + margin.t + margin.b, + static_cast (this), &output_options, content); cairo_set_scaled_font (cr, scaled_font); /* Setup coordinate system. */ - cairo_translate (cr, view_options.margin.l, view_options.margin.t); + cairo_translate (cr, margin.l, margin.t); if (vertical) cairo_translate (cr, w - ascent, /* We currently always stack lines right to left */ @@ -169,7 +168,7 @@ view_cairo_t::render (const font_options_t *font_opts) cairo_translate (cr, -vert * leading, +horiz * leading); - if (view_options.annotate) { + if (annotate) { cairo_save (cr); /* Draw actual glyph origins */ From 370e961faf28291def7f011f694bef1266adda99 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Fri, 6 Aug 2021 22:59:50 -0600 Subject: [PATCH 22/33] [util/hb-subset] Use hb-subset.h public API instead of poking inside Oops. --- util/options-subset.cc | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/util/options-subset.cc b/util/options-subset.cc index dbabe3692..9c719efaa 100644 --- a/util/options-subset.cc +++ b/util/options-subset.cc @@ -26,7 +26,6 @@ #include "options.hh" -#include "hb-subset-input.hh" static gboolean parse_gids (const char *name G_GNUC_UNUSED, @@ -35,7 +34,7 @@ parse_gids (const char *name G_GNUC_UNUSED, GError **error G_GNUC_UNUSED) { subset_options_t *subset_opts = (subset_options_t *) data; - hb_set_t *gids = subset_opts->input->glyphs; + hb_set_t *gids = hb_subset_input_glyph_set (subset_opts->input); char *s = (char *) arg; char *p; @@ -95,7 +94,7 @@ parse_nameids (const char *name, GError **error G_GNUC_UNUSED) { subset_options_t *subset_opts = (subset_options_t *) data; - hb_set_t *name_ids = subset_opts->input->name_ids; + hb_set_t *name_ids = hb_subset_input_nameid_set (subset_opts->input); char last_name_char = name[strlen (name) - 1]; @@ -151,7 +150,7 @@ parse_name_languages (const char *name, GError **error G_GNUC_UNUSED) { subset_options_t *subset_opts = (subset_options_t *) data; - hb_set_t *name_languages = subset_opts->input->name_languages; + hb_set_t *name_languages = hb_subset_input_namelangid_set (subset_opts->input); char last_name_char = name[strlen (name) - 1]; @@ -207,7 +206,7 @@ parse_layout_features (const char *name, GError **error G_GNUC_UNUSED) { subset_options_t *subset_opts = (subset_options_t *) data; - hb_set_t *layout_features = subset_opts->input->layout_features; + hb_set_t *layout_features = hb_subset_input_layout_features_set (subset_opts->input); char last_name_char = name[strlen (name) - 1]; @@ -256,7 +255,7 @@ parse_drop_tables (const char *name, GError **error G_GNUC_UNUSED) { subset_options_t *subset_opts = (subset_options_t *) data; - hb_set_t *drop_tables = subset_opts->input->drop_tables; + hb_set_t *drop_tables = hb_subset_input_drop_tables_set (subset_opts->input); char last_name_char = name[strlen (name) - 1]; From 167f58a2ca1db5649a0724f88e033c28a3bbb844 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Fri, 6 Aug 2021 23:03:30 -0600 Subject: [PATCH 23/33] [util] Move subset options into subset-options.hh --- util/Makefile.sources | 2 +- util/hb-subset.cc | 3 +- util/meson.build | 1 - util/options.hh | 40 ----------------- util/{options-subset.cc => subset-options.hh} | 45 +++++++++++++++++++ 5 files changed, 48 insertions(+), 43 deletions(-) rename util/{options-subset.cc => subset-options.hh} (91%) diff --git a/util/Makefile.sources b/util/Makefile.sources index 82936092f..8a45f69b4 100644 --- a/util/Makefile.sources +++ b/util/Makefile.sources @@ -28,7 +28,7 @@ HB_OT_SHAPE_CLOSURE_sources = \ HB_SUBSET_CLI_sources = \ hb-subset.cc \ options.cc \ - options-subset.cc \ options.hh \ + subset-options.hh \ main-font-text.hh \ $(NULL) diff --git a/util/hb-subset.cc b/util/hb-subset.cc index 3e06d16e2..1469ae343 100644 --- a/util/hb-subset.cc +++ b/util/hb-subset.cc @@ -28,7 +28,7 @@ #include #include "main-font-text.hh" -#include "hb-subset.h" +#include "subset-options.hh" // XXX Remove eventually const unsigned DEFAULT_FONT_SIZE = FONT_SIZE_NONE; @@ -38,6 +38,7 @@ const unsigned SUBPIXEL_BITS = 0; * Command line interface to the harfbuzz font subsetter. */ + struct subset_consumer_t : subset_options_t { void add_options (option_parser_t *parser) diff --git a/util/meson.build b/util/meson.build index cb724f6cb..3773e68a6 100644 --- a/util/meson.build +++ b/util/meson.build @@ -16,7 +16,6 @@ hb_ot_shape_closure_sources = [ hb_subset_cli_sources = [ 'hb-subset.cc', 'options.cc', - 'options-subset.cc', ] util_deps = [freetype_dep, cairo_dep, cairo_ft_dep, glib_dep] diff --git a/util/options.hh b/util/options.hh index 87678246f..e7a48f2f5 100644 --- a/util/options.hh +++ b/util/options.hh @@ -28,7 +28,6 @@ #define OPTIONS_HH #include "hb.hh" -#include "hb-subset.h" #include #include @@ -622,45 +621,6 @@ struct format_options_t hb_bool_t trace = false; }; -struct subset_options_t -{ - subset_options_t () - : input (hb_subset_input_create_or_fail ()) - {} - ~subset_options_t () - { - hb_subset_input_destroy (input); - } - - void add_options (option_parser_t *parser); - - hb_bool_t* bool_for(hb_subset_flags_t flag) - { - for (unsigned i = 0; i < sizeof(int) * 8; i++) - { - if (1u << i == flag) - return &flags[i]; - } - return &flags[sizeof(int) * 8 - 1]; - } - - hb_subset_input_t * get_input () - { - hb_subset_flags_t flags_set = HB_SUBSET_FLAGS_DEFAULT; - for (unsigned i = 0; i < sizeof(int) * 8; i++) - { - if (flags[i]) - flags_set = (hb_subset_flags_t) (flags_set | (1u << i)); - } - hb_subset_input_set_flags (input, flags_set); - return input; - } - - unsigned num_iterations = 1; - hb_subset_input_t *input = nullptr; - hb_bool_t flags[sizeof(int) * 8] = {0}; -}; - /* fallback implementation for scalbn()/scalbnf() for pre-2013 MSVC */ #if defined (_MSC_VER) && (_MSC_VER < 1800) diff --git a/util/options-subset.cc b/util/subset-options.hh similarity index 91% rename from util/options-subset.cc rename to util/subset-options.hh index 9c719efaa..b202f1deb 100644 --- a/util/options-subset.cc +++ b/util/subset-options.hh @@ -24,7 +24,50 @@ * Google Author(s): Garret Rieger */ +#ifndef SUBSET_OPTIONS_HH +#define SUBSET_OPTIONS_HH + #include "options.hh" +#include "hb-subset.h" + +struct subset_options_t +{ + subset_options_t () + : input (hb_subset_input_create_or_fail ()) + {} + ~subset_options_t () + { + hb_subset_input_destroy (input); + } + + void add_options (option_parser_t *parser); + + hb_bool_t* bool_for(hb_subset_flags_t flag) + { + for (unsigned i = 0; i < sizeof(int) * 8; i++) + { + if (1u << i == flag) + return &flags[i]; + } + return &flags[sizeof(int) * 8 - 1]; + } + + hb_subset_input_t * get_input () + { + hb_subset_flags_t flags_set = HB_SUBSET_FLAGS_DEFAULT; + for (unsigned i = 0; i < sizeof(int) * 8; i++) + { + if (flags[i]) + flags_set = (hb_subset_flags_t) (flags_set | (1u << i)); + } + hb_subset_input_set_flags (input, flags_set); + return input; + } + + unsigned num_iterations = 1; + hb_subset_input_t *input = nullptr; + hb_bool_t flags[sizeof(int) * 8] = {0}; +}; static gboolean @@ -323,3 +366,5 @@ subset_options_t::add_options (option_parser_t *parser) "Options subsetting", this); } + +#endif From cfb2d6ad9e34f4b61ea3fedd27ac448a47ac4c09 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Fri, 6 Aug 2021 23:14:30 -0600 Subject: [PATCH 24/33] [util] Move view options into view-options.hh --- util/Makefile.sources | 15 +++--- util/options.cc | 64 ---------------------- util/options.hh | 25 --------- util/view-cairo.hh | 4 +- util/view-options.hh | 123 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 132 insertions(+), 99 deletions(-) create mode 100644 util/view-options.hh diff --git a/util/Makefile.sources b/util/Makefile.sources index 8a45f69b4..fc74e05fd 100644 --- a/util/Makefile.sources +++ b/util/Makefile.sources @@ -1,34 +1,35 @@ HB_VIEW_sources = \ + ansi-print.hh \ hb-view.cc \ + helper-cairo-ansi.hh \ + helper-cairo.hh \ + main-font-text.hh \ options.cc \ options.hh \ - main-font-text.hh \ shape-consumer.hh \ - ansi-print.hh \ - helper-cairo.hh \ - helper-cairo-ansi.hh \ view-cairo.hh \ + view-options.hh \ $(NULL) HB_SHAPE_sources = \ hb-shape.cc \ + main-font-text.hh \ options.cc \ options.hh \ - main-font-text.hh \ shape-consumer.hh \ $(NULL) HB_OT_SHAPE_CLOSURE_sources = \ hb-ot-shape-closure.cc \ + main-font-text.hh \ options.cc \ options.hh \ - main-font-text.hh \ $(NULL) HB_SUBSET_CLI_sources = \ hb-subset.cc \ + main-font-text.hh \ options.cc \ options.hh \ subset-options.hh \ - main-font-text.hh \ $(NULL) diff --git a/util/options.cc b/util/options.cc index 641376767..69013df81 100644 --- a/util/options.cc +++ b/util/options.cc @@ -127,50 +127,6 @@ option_parser_t::parse (int *argc, char ***argv) } -static gboolean -parse_font_extents (const char *name G_GNUC_UNUSED, - const char *arg, - gpointer data, - GError **error G_GNUC_UNUSED) -{ - view_options_t *view_opts = (view_options_t *) data; - view_options_t::font_extents_t &e = view_opts->font_extents; - switch (sscanf (arg, "%lf%*[ ,]%lf%*[ ,]%lf", &e.ascent, &e.descent, &e.line_gap)) { - case 1: HB_FALLTHROUGH; - case 2: HB_FALLTHROUGH; - case 3: - view_opts->have_font_extents = true; - return true; - default: - g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, - "%s argument should be one to three space-separated numbers", - name); - return false; - } -} - -static gboolean -parse_margin (const char *name G_GNUC_UNUSED, - const char *arg, - gpointer data, - GError **error G_GNUC_UNUSED) -{ - view_options_t *view_opts = (view_options_t *) data; - view_options_t::margin_t &m = view_opts->margin; - switch (sscanf (arg, "%lf%*[ ,]%lf%*[ ,]%lf%*[ ,]%lf", &m.t, &m.r, &m.b, &m.l)) { - case 1: m.r = m.t; HB_FALLTHROUGH; - case 2: m.b = m.t; HB_FALLTHROUGH; - case 3: m.l = m.r; HB_FALLTHROUGH; - case 4: return true; - default: - g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, - "%s argument should be one to four space-separated numbers", - name); - return false; - } -} - - static gboolean parse_shapers (const char *name G_GNUC_UNUSED, const char *arg, @@ -384,26 +340,6 @@ parse_unicodes (const char *name G_GNUC_UNUSED, } -void -view_options_t::add_options (option_parser_t *parser) -{ - GOptionEntry entries[] = - { - {"annotate", 0, 0, G_OPTION_ARG_NONE, &this->annotate, "Annotate output rendering", nullptr}, - {"background", 0, 0, G_OPTION_ARG_STRING, &this->back, "Set background color (default: " DEFAULT_BACK ")", "rrggbb/rrggbbaa"}, - {"foreground", 0, 0, G_OPTION_ARG_STRING, &this->fore, "Set foreground color (default: " DEFAULT_FORE ")", "rrggbb/rrggbbaa"}, - {"line-space", 0, 0, G_OPTION_ARG_DOUBLE, &this->line_space, "Set space between lines (default: 0)", "units"}, - {"font-extents", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_font_extents, "Set font ascent/descent/line-gap (default: auto)","one to three numbers"}, - {"margin", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_margin, "Margin around output (default: " G_STRINGIFY(DEFAULT_MARGIN) ")","one to four numbers"}, - {nullptr} - }; - parser->add_group (entries, - "view", - "View options:", - "Options for output rendering", - this); -} - void shape_options_t::add_options (option_parser_t *parser) { diff --git a/util/options.hh b/util/options.hh index e7a48f2f5..0b5c8b9a2 100644 --- a/util/options.hh +++ b/util/options.hh @@ -123,37 +123,12 @@ struct option_parser_t }; -#define DEFAULT_MARGIN 16 -#define DEFAULT_FORE "#000000" -#define DEFAULT_BACK "#FFFFFF" #define FONT_SIZE_UPEM 0x7FFFFFFF #define FONT_SIZE_NONE 0 extern const unsigned DEFAULT_FONT_SIZE; extern const unsigned SUBPIXEL_BITS; -struct view_options_t -{ - ~view_options_t () - { - g_free (fore); - g_free (back); - } - - void add_options (option_parser_t *parser); - - hb_bool_t annotate = false; - char *fore = nullptr; - char *back = nullptr; - double line_space = 0; - bool have_font_extents = false; - struct font_extents_t { - double ascent, descent, line_gap; - } font_extents = {0., 0., 0.}; - struct margin_t { - double t, r, b, l; - } margin = {DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN}; -}; struct shape_options_t diff --git a/util/view-cairo.hh b/util/view-cairo.hh index 2599eb4ae..d1915d0a5 100644 --- a/util/view-cairo.hh +++ b/util/view-cairo.hh @@ -27,11 +27,9 @@ #ifndef VIEW_CAIRO_HH #define VIEW_CAIRO_HH -#include "hb.hh" -#include "options.hh" +#include "view-options.hh" #include "helper-cairo.hh" - struct view_cairo_t : view_options_t { ~view_cairo_t () diff --git a/util/view-options.hh b/util/view-options.hh new file mode 100644 index 000000000..322009a1e --- /dev/null +++ b/util/view-options.hh @@ -0,0 +1,123 @@ +/* + * Copyright © 2011 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef VIEW_OPTIONS_HH +#define VIEW_OPTIONS_HH + +#include "options.hh" + +#define DEFAULT_MARGIN 16 +#define DEFAULT_FORE "#000000" +#define DEFAULT_BACK "#FFFFFF" + +struct view_options_t +{ + ~view_options_t () + { + g_free (fore); + g_free (back); + } + + void add_options (option_parser_t *parser); + + hb_bool_t annotate = false; + char *fore = nullptr; + char *back = nullptr; + double line_space = 0; + bool have_font_extents = false; + struct font_extents_t { + double ascent, descent, line_gap; + } font_extents = {0., 0., 0.}; + struct margin_t { + double t, r, b, l; + } margin = {DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN}; +}; + + +static gboolean +parse_font_extents (const char *name G_GNUC_UNUSED, + const char *arg, + gpointer data, + GError **error G_GNUC_UNUSED) +{ + view_options_t *view_opts = (view_options_t *) data; + view_options_t::font_extents_t &e = view_opts->font_extents; + switch (sscanf (arg, "%lf%*[ ,]%lf%*[ ,]%lf", &e.ascent, &e.descent, &e.line_gap)) { + case 1: HB_FALLTHROUGH; + case 2: HB_FALLTHROUGH; + case 3: + view_opts->have_font_extents = true; + return true; + default: + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + "%s argument should be one to three space-separated numbers", + name); + return false; + } +} + +static gboolean +parse_margin (const char *name G_GNUC_UNUSED, + const char *arg, + gpointer data, + GError **error G_GNUC_UNUSED) +{ + view_options_t *view_opts = (view_options_t *) data; + view_options_t::margin_t &m = view_opts->margin; + switch (sscanf (arg, "%lf%*[ ,]%lf%*[ ,]%lf%*[ ,]%lf", &m.t, &m.r, &m.b, &m.l)) { + case 1: m.r = m.t; HB_FALLTHROUGH; + case 2: m.b = m.t; HB_FALLTHROUGH; + case 3: m.l = m.r; HB_FALLTHROUGH; + case 4: return true; + default: + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + "%s argument should be one to four space-separated numbers", + name); + return false; + } +} + +void +view_options_t::add_options (option_parser_t *parser) +{ + GOptionEntry entries[] = + { + {"annotate", 0, 0, G_OPTION_ARG_NONE, &this->annotate, "Annotate output rendering", nullptr}, + {"background", 0, 0, G_OPTION_ARG_STRING, &this->back, "Set background color (default: " DEFAULT_BACK ")", "rrggbb/rrggbbaa"}, + {"foreground", 0, 0, G_OPTION_ARG_STRING, &this->fore, "Set foreground color (default: " DEFAULT_FORE ")", "rrggbb/rrggbbaa"}, + {"line-space", 0, 0, G_OPTION_ARG_DOUBLE, &this->line_space, "Set space between lines (default: 0)", "units"}, + {"font-extents", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_font_extents, "Set font ascent/descent/line-gap (default: auto)","one to three numbers"}, + {"margin", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_margin, "Margin around output (default: " G_STRINGIFY(DEFAULT_MARGIN) ")","one to four numbers"}, + {nullptr} + }; + parser->add_group (entries, + "view", + "View options:", + "Options for output rendering", + this); +} + +#endif From c3599fded73882d5234a995b7f253baf0b4fa1e4 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Fri, 6 Aug 2021 23:24:28 -0600 Subject: [PATCH 25/33] [util] Move shape options into shape-options.hh --- util/Makefile.sources | 2 + util/hb-ot-shape-closure.cc | 1 + util/main-font-text.hh | 1 - util/options.cc | 179 +------------ util/options.hh | 286 --------------------- util/shape-consumer.hh | 3 +- util/shape-options.hh | 490 ++++++++++++++++++++++++++++++++++++ 7 files changed, 496 insertions(+), 466 deletions(-) create mode 100644 util/shape-options.hh diff --git a/util/Makefile.sources b/util/Makefile.sources index fc74e05fd..0595f0d5e 100644 --- a/util/Makefile.sources +++ b/util/Makefile.sources @@ -7,6 +7,7 @@ HB_VIEW_sources = \ options.cc \ options.hh \ shape-consumer.hh \ + shape-options.hh \ view-cairo.hh \ view-options.hh \ $(NULL) @@ -17,6 +18,7 @@ HB_SHAPE_sources = \ options.cc \ options.hh \ shape-consumer.hh \ + shape-options.hh \ $(NULL) HB_OT_SHAPE_CLOSURE_sources = \ diff --git a/util/hb-ot-shape-closure.cc b/util/hb-ot-shape-closure.cc index a21754940..97d7e9ed0 100644 --- a/util/hb-ot-shape-closure.cc +++ b/util/hb-ot-shape-closure.cc @@ -24,6 +24,7 @@ * Google Author(s): Behdad Esfahbod */ +#include "shape-options.hh" #include "main-font-text.hh" const unsigned DEFAULT_FONT_SIZE = FONT_SIZE_NONE; diff --git a/util/main-font-text.hh b/util/main-font-text.hh index f45f2c034..f3a7feea1 100644 --- a/util/main-font-text.hh +++ b/util/main-font-text.hh @@ -27,7 +27,6 @@ #ifndef HB_MAIN_FONT_TEXT_HH #define HB_MAIN_FONT_TEXT_HH -#include "hb.hh" #include "options.hh" /* main() body for utilities taking font and processing text.*/ diff --git a/util/options.cc b/util/options.cc index 69013df81..3f765a957 100644 --- a/util/options.cc +++ b/util/options.cc @@ -31,8 +31,6 @@ #endif #include -#define DELIMITERS "<+>{},;&#\\xXuUnNiI\n\t\v\f\r " - static struct supported_font_funcs_t { char name[4]; void (*func) (hb_font_t *); @@ -127,100 +125,6 @@ option_parser_t::parse (int *argc, char ***argv) } -static gboolean -parse_shapers (const char *name G_GNUC_UNUSED, - const char *arg, - gpointer data, - GError **error) -{ - shape_options_t *shape_opts = (shape_options_t *) data; - char **shapers = g_strsplit (arg, ",", 0); - - for (char **shaper = shapers; *shaper; shaper++) { - bool found = false; - for (const char **hb_shaper = hb_shape_list_shapers (); *hb_shaper; hb_shaper++) { - if (strcmp (*shaper, *hb_shaper) == 0) { - found = true; - break; - } - } - if (!found) { - g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, - "Unknown or unsupported shaper: %s", *shaper); - g_strfreev (shapers); - return false; - } - } - - g_strfreev (shape_opts->shapers); - shape_opts->shapers = shapers; - return true; -} - -static G_GNUC_NORETURN gboolean -list_shapers (const char *name G_GNUC_UNUSED, - const char *arg G_GNUC_UNUSED, - gpointer data G_GNUC_UNUSED, - GError **error G_GNUC_UNUSED) -{ - for (const char **shaper = hb_shape_list_shapers (); *shaper; shaper++) - g_printf ("%s\n", *shaper); - - exit(0); -} - - -static gboolean -parse_features (const char *name G_GNUC_UNUSED, - const char *arg, - gpointer data, - GError **error G_GNUC_UNUSED) -{ - shape_options_t *shape_opts = (shape_options_t *) data; - char *s = (char *) arg; - size_t l = strlen (s); - char *p; - - shape_opts->num_features = 0; - g_free (shape_opts->features); - shape_opts->features = nullptr; - - /* if the string is quoted, strip the quotes */ - if (s[0] == s[l - 1] && (s[0] == '\"' || s[0] == '\'')) - { - s[l - 1] = '\0'; - s++; - } - - if (!*s) - return true; - - /* count the features first, so we can allocate memory */ - p = s; - do { - shape_opts->num_features++; - p = strchr (p, ','); - if (p) - p++; - } while (p); - - shape_opts->features = (hb_feature_t *) calloc (shape_opts->num_features, sizeof (*shape_opts->features)); - if (!shape_opts->features) - return false; - - /* now do the actual parsing */ - p = s; - shape_opts->num_features = 0; - while (p && *p) { - char *end = strchr (p, ','); - if (hb_feature_from_string (p, end ? end - p : -1, &shape_opts->features[shape_opts->num_features])) - shape_opts->num_features++; - p = end ? end + 1 : nullptr; - } - - return true; -} - static gboolean parse_variations (const char *name G_GNUC_UNUSED, const char *arg, @@ -313,6 +217,8 @@ parse_unicodes (const char *name G_GNUC_UNUSED, while (s && *s) { +#define DELIMITERS "<+>{},;&#\\xXuUnNiI\n\t\v\f\r " + while (*s && strchr (DELIMITERS, *s)) s++; if (!*s) @@ -339,87 +245,6 @@ parse_unicodes (const char *name G_GNUC_UNUSED, return true; } - -void -shape_options_t::add_options (option_parser_t *parser) -{ - GOptionEntry entries[] = - { - {"list-shapers", 0, G_OPTION_FLAG_NO_ARG, - G_OPTION_ARG_CALLBACK, (gpointer) &list_shapers, "List available shapers and quit", nullptr}, - {"shaper", 0, G_OPTION_FLAG_HIDDEN, - G_OPTION_ARG_CALLBACK, (gpointer) &parse_shapers, "Hidden duplicate of --shapers", nullptr}, - {"shapers", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_shapers, "Set comma-separated list of shapers to try","list"}, - {"direction", 0, 0, G_OPTION_ARG_STRING, &this->direction, "Set text direction (default: auto)", "ltr/rtl/ttb/btt"}, - {"language", 0, 0, G_OPTION_ARG_STRING, &this->language, "Set text language (default: $LANG)", "langstr"}, - {"script", 0, 0, G_OPTION_ARG_STRING, &this->script, "Set text script (default: auto)", "ISO-15924 tag"}, - {"bot", 0, 0, G_OPTION_ARG_NONE, &this->bot, "Treat text as beginning-of-paragraph", nullptr}, - {"eot", 0, 0, G_OPTION_ARG_NONE, &this->eot, "Treat text as end-of-paragraph", nullptr}, - {"preserve-default-ignorables",0, 0, G_OPTION_ARG_NONE, &this->preserve_default_ignorables, "Preserve Default-Ignorable characters", nullptr}, - {"remove-default-ignorables",0, 0, G_OPTION_ARG_NONE, &this->remove_default_ignorables, "Remove Default-Ignorable characters", nullptr}, - {"invisible-glyph", 0, 0, G_OPTION_ARG_INT, &this->invisible_glyph, "Glyph value to replace Default-Ignorables with", nullptr}, - {"utf8-clusters", 0, 0, G_OPTION_ARG_NONE, &this->utf8_clusters, "Use UTF8 byte indices, not char indices", nullptr}, - {"cluster-level", 0, 0, G_OPTION_ARG_INT, &this->cluster_level, "Cluster merging level (default: 0)", "0/1/2"}, - {"normalize-glyphs",0, 0, G_OPTION_ARG_NONE, &this->normalize_glyphs, "Rearrange glyph clusters in nominal order", nullptr}, - {"verify", 0, 0, G_OPTION_ARG_NONE, &this->verify, "Perform sanity checks on shaping results", nullptr}, - {"num-iterations", 'n', 0, G_OPTION_ARG_INT, &this->num_iterations, "Run shaper N times (default: 1)", "N"}, - {nullptr} - }; - parser->add_group (entries, - "shape", - "Shape options:", - "Options for the shaping process", - this); - - const gchar *features_help = "Comma-separated list of font features\n" - "\n" - " Features can be enabled or disabled, either globally or limited to\n" - " specific character ranges. The format for specifying feature settings\n" - " follows. All valid CSS font-feature-settings values other than 'normal'\n" - " and the global values are also accepted, though not documented below.\n" - " CSS string escapes are not supported." - "\n" - " The range indices refer to the positions between Unicode characters,\n" - " unless the --utf8-clusters is provided, in which case range indices\n" - " refer to UTF-8 byte indices. The position before the first character\n" - " is always 0.\n" - "\n" - " The format is Python-esque. Here is how it all works:\n" - "\n" - " Syntax: Value: Start: End:\n" - "\n" - " Setting value:\n" - " \"kern\" 1 0 ∞ # Turn feature on\n" - " \"+kern\" 1 0 ∞ # Turn feature on\n" - " \"-kern\" 0 0 ∞ # Turn feature off\n" - " \"kern=0\" 0 0 ∞ # Turn feature off\n" - " \"kern=1\" 1 0 ∞ # Turn feature on\n" - " \"aalt=2\" 2 0 ∞ # Choose 2nd alternate\n" - "\n" - " Setting index:\n" - " \"kern[]\" 1 0 ∞ # Turn feature on\n" - " \"kern[:]\" 1 0 ∞ # Turn feature on\n" - " \"kern[5:]\" 1 5 ∞ # Turn feature on, partial\n" - " \"kern[:5]\" 1 0 5 # Turn feature on, partial\n" - " \"kern[3:5]\" 1 3 5 # Turn feature on, range\n" - " \"kern[3]\" 1 3 3+1 # Turn feature on, single char\n" - "\n" - " Mixing it all:\n" - "\n" - " \"aalt[3:5]=2\" 2 3 5 # Turn 2nd alternate on for range"; - - GOptionEntry entries2[] = - { - {"features", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_features, features_help, "list"}, - {nullptr} - }; - parser->add_group (entries2, - "features", - "Features options:", - "Options for font features used", - this); -} - static gboolean parse_font_size (const char *name G_GNUC_UNUSED, const char *arg, diff --git a/util/options.hh b/util/options.hh index 0b5c8b9a2..5bb5a9545 100644 --- a/util/options.hh +++ b/util/options.hh @@ -129,292 +129,6 @@ struct option_parser_t extern const unsigned DEFAULT_FONT_SIZE; extern const unsigned SUBPIXEL_BITS; - - -struct shape_options_t -{ - ~shape_options_t () - { - g_free (direction); - g_free (language); - g_free (script); - free (features); - g_strfreev (shapers); - } - - void add_options (option_parser_t *parser); - - void setup_buffer (hb_buffer_t *buffer) - { - hb_buffer_set_direction (buffer, hb_direction_from_string (direction, -1)); - hb_buffer_set_script (buffer, hb_script_from_string (script, -1)); - hb_buffer_set_language (buffer, hb_language_from_string (language, -1)); - hb_buffer_set_flags (buffer, (hb_buffer_flags_t) - (HB_BUFFER_FLAG_DEFAULT | - (bot ? HB_BUFFER_FLAG_BOT : 0) | - (eot ? HB_BUFFER_FLAG_EOT : 0) | - (preserve_default_ignorables ? HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES : 0) | - (remove_default_ignorables ? HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES : 0) | - 0)); - hb_buffer_set_invisible_glyph (buffer, invisible_glyph); - hb_buffer_set_cluster_level (buffer, cluster_level); - hb_buffer_guess_segment_properties (buffer); - } - - static void copy_buffer_properties (hb_buffer_t *dst, hb_buffer_t *src) - { - hb_segment_properties_t props; - hb_buffer_get_segment_properties (src, &props); - hb_buffer_set_segment_properties (dst, &props); - hb_buffer_set_flags (dst, hb_buffer_get_flags (src)); - hb_buffer_set_cluster_level (dst, hb_buffer_get_cluster_level (src)); - } - - void populate_buffer (hb_buffer_t *buffer, const char *text, int text_len, - const char *text_before, const char *text_after) - { - hb_buffer_clear_contents (buffer); - if (text_before) { - unsigned int len = strlen (text_before); - hb_buffer_add_utf8 (buffer, text_before, len, len, 0); - } - hb_buffer_add_utf8 (buffer, text, text_len, 0, text_len); - if (text_after) { - hb_buffer_add_utf8 (buffer, text_after, -1, 0, 0); - } - - if (!utf8_clusters) { - /* Reset cluster values to refer to Unicode character index - * instead of UTF-8 index. */ - unsigned int num_glyphs = hb_buffer_get_length (buffer); - hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, nullptr); - for (unsigned int i = 0; i < num_glyphs; i++) - { - info->cluster = i; - info++; - } - } - - setup_buffer (buffer); - } - - hb_bool_t shape (hb_font_t *font, hb_buffer_t *buffer, const char **error=nullptr) - { - hb_buffer_t *text_buffer = nullptr; - if (verify) - { - text_buffer = hb_buffer_create (); - hb_buffer_append (text_buffer, buffer, 0, -1); - } - - if (!hb_shape_full (font, buffer, features, num_features, shapers)) - { - if (error) - *error = "all shapers failed."; - goto fail; - } - - if (normalize_glyphs) - hb_buffer_normalize_glyphs (buffer); - - if (verify && !verify_buffer (buffer, text_buffer, font, error)) - goto fail; - - if (text_buffer) - hb_buffer_destroy (text_buffer); - - return true; - - fail: - if (text_buffer) - hb_buffer_destroy (text_buffer); - - return false; - } - - bool verify_buffer (hb_buffer_t *buffer, - hb_buffer_t *text_buffer, - hb_font_t *font, - const char **error=nullptr) - { - if (!verify_buffer_monotone (buffer, error)) - return false; - if (!verify_buffer_safe_to_break (buffer, text_buffer, font, error)) - return false; - return true; - } - - bool verify_buffer_monotone (hb_buffer_t *buffer, const char **error=nullptr) - { - /* Check that clusters are monotone. */ - if (cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES || - cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS) - { - bool is_forward = HB_DIRECTION_IS_FORWARD (hb_buffer_get_direction (buffer)); - - unsigned int num_glyphs; - hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, &num_glyphs); - - for (unsigned int i = 1; i < num_glyphs; i++) - if (info[i-1].cluster != info[i].cluster && - (info[i-1].cluster < info[i].cluster) != is_forward) - { - if (error) - *error = "clusters are not monotone."; - return false; - } - } - - return true; - } - - bool verify_buffer_safe_to_break (hb_buffer_t *buffer, - hb_buffer_t *text_buffer, - hb_font_t *font, - const char **error=nullptr) - { - if (cluster_level != HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES && - cluster_level != HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS) - { - /* Cannot perform this check without monotone clusters. - * Then again, unsafe-to-break flag is much harder to use without - * monotone clusters. */ - return true; - } - - /* Check that breaking up shaping at safe-to-break is indeed safe. */ - - hb_buffer_t *fragment = hb_buffer_create (); - hb_buffer_t *reconstruction = hb_buffer_create (); - copy_buffer_properties (reconstruction, buffer); - - unsigned int num_glyphs; - hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, &num_glyphs); - - unsigned int num_chars; - hb_glyph_info_t *text = hb_buffer_get_glyph_infos (text_buffer, &num_chars); - - /* Chop text and shape fragments. */ - bool forward = HB_DIRECTION_IS_FORWARD (hb_buffer_get_direction (buffer)); - unsigned int start = 0; - unsigned int text_start = forward ? 0 : num_chars; - unsigned int text_end = text_start; - for (unsigned int end = 1; end < num_glyphs + 1; end++) - { - if (end < num_glyphs && - (info[end].cluster == info[end-1].cluster || - info[end-(forward?0:1)].mask & HB_GLYPH_FLAG_UNSAFE_TO_BREAK)) - continue; - - /* Shape segment corresponding to glyphs start..end. */ - if (end == num_glyphs) - { - if (forward) - text_end = num_chars; - else - text_start = 0; - } - else - { - if (forward) - { - unsigned int cluster = info[end].cluster; - while (text_end < num_chars && text[text_end].cluster < cluster) - text_end++; - } - else - { - unsigned int cluster = info[end - 1].cluster; - while (text_start && text[text_start - 1].cluster >= cluster) - text_start--; - } - } - assert (text_start < text_end); - - if (0) - printf("start %d end %d text start %d end %d\n", start, end, text_start, text_end); - - hb_buffer_clear_contents (fragment); - copy_buffer_properties (fragment, buffer); - - /* TODO: Add pre/post context text. */ - hb_buffer_flags_t flags = hb_buffer_get_flags (fragment); - if (0 < text_start) - flags = (hb_buffer_flags_t) (flags & ~HB_BUFFER_FLAG_BOT); - if (text_end < num_chars) - flags = (hb_buffer_flags_t) (flags & ~HB_BUFFER_FLAG_EOT); - hb_buffer_set_flags (fragment, flags); - - hb_buffer_append (fragment, text_buffer, text_start, text_end); - if (!hb_shape_full (font, fragment, features, num_features, shapers)) - { - if (error) - *error = "all shapers failed while shaping fragment."; - hb_buffer_destroy (reconstruction); - hb_buffer_destroy (fragment); - return false; - } - hb_buffer_append (reconstruction, fragment, 0, -1); - - start = end; - if (forward) - text_start = text_end; - else - text_end = text_start; - } - - bool ret = true; - hb_buffer_diff_flags_t diff = hb_buffer_diff (reconstruction, buffer, (hb_codepoint_t) -1, 0); - if (diff) - { - if (error) - *error = "Safe-to-break test failed."; - ret = false; - - /* Return the reconstructed result instead so it can be inspected. */ - hb_buffer_set_length (buffer, 0); - hb_buffer_append (buffer, reconstruction, 0, -1); - } - - hb_buffer_destroy (reconstruction); - hb_buffer_destroy (fragment); - - return ret; - } - - void shape_closure (const char *text, int text_len, - hb_font_t *font, hb_buffer_t *buffer, - hb_set_t *glyphs) - { - hb_buffer_reset (buffer); - hb_buffer_add_utf8 (buffer, text, text_len, 0, text_len); - setup_buffer (buffer); - hb_ot_shape_glyphs_closure (font, buffer, features, num_features, glyphs); - } - - /* Buffer properties */ - char *direction = nullptr; - char *language = nullptr; - char *script = nullptr; - - /* Buffer flags */ - hb_bool_t bot = false; - hb_bool_t eot = false; - hb_bool_t preserve_default_ignorables = false; - hb_bool_t remove_default_ignorables = false; - - hb_feature_t *features = nullptr; - unsigned int num_features = 0; - char **shapers = nullptr; - hb_bool_t utf8_clusters = false; - hb_codepoint_t invisible_glyph = 0; - hb_buffer_cluster_level_t cluster_level = HB_BUFFER_CLUSTER_LEVEL_DEFAULT; - hb_bool_t normalize_glyphs = false; - hb_bool_t verify = false; - unsigned int num_iterations = 1; -}; - - struct face_options_t { void add_options (option_parser_t *parser); diff --git a/util/shape-consumer.hh b/util/shape-consumer.hh index 3c18605cc..e6a139c5e 100644 --- a/util/shape-consumer.hh +++ b/util/shape-consumer.hh @@ -27,8 +27,7 @@ #ifndef HB_SHAPE_CONSUMER_HH #define HB_SHAPE_CONSUMER_HH -#include "hb.hh" -#include "options.hh" +#include "shape-options.hh" template diff --git a/util/shape-options.hh b/util/shape-options.hh new file mode 100644 index 000000000..f19578bbf --- /dev/null +++ b/util/shape-options.hh @@ -0,0 +1,490 @@ +/* + * Copyright © 2011 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef SHAPE_OPTIONS_HH +#define SHAPE_OPTIONS_HH + +#include "options.hh" + +struct shape_options_t +{ + ~shape_options_t () + { + g_free (direction); + g_free (language); + g_free (script); + free (features); + g_strfreev (shapers); + } + + void add_options (option_parser_t *parser); + + void setup_buffer (hb_buffer_t *buffer) + { + hb_buffer_set_direction (buffer, hb_direction_from_string (direction, -1)); + hb_buffer_set_script (buffer, hb_script_from_string (script, -1)); + hb_buffer_set_language (buffer, hb_language_from_string (language, -1)); + hb_buffer_set_flags (buffer, (hb_buffer_flags_t) + (HB_BUFFER_FLAG_DEFAULT | + (bot ? HB_BUFFER_FLAG_BOT : 0) | + (eot ? HB_BUFFER_FLAG_EOT : 0) | + (preserve_default_ignorables ? HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES : 0) | + (remove_default_ignorables ? HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES : 0) | + 0)); + hb_buffer_set_invisible_glyph (buffer, invisible_glyph); + hb_buffer_set_cluster_level (buffer, cluster_level); + hb_buffer_guess_segment_properties (buffer); + } + + static void copy_buffer_properties (hb_buffer_t *dst, hb_buffer_t *src) + { + hb_segment_properties_t props; + hb_buffer_get_segment_properties (src, &props); + hb_buffer_set_segment_properties (dst, &props); + hb_buffer_set_flags (dst, hb_buffer_get_flags (src)); + hb_buffer_set_cluster_level (dst, hb_buffer_get_cluster_level (src)); + } + + void populate_buffer (hb_buffer_t *buffer, const char *text, int text_len, + const char *text_before, const char *text_after) + { + hb_buffer_clear_contents (buffer); + if (text_before) { + unsigned int len = strlen (text_before); + hb_buffer_add_utf8 (buffer, text_before, len, len, 0); + } + hb_buffer_add_utf8 (buffer, text, text_len, 0, text_len); + if (text_after) { + hb_buffer_add_utf8 (buffer, text_after, -1, 0, 0); + } + + if (!utf8_clusters) { + /* Reset cluster values to refer to Unicode character index + * instead of UTF-8 index. */ + unsigned int num_glyphs = hb_buffer_get_length (buffer); + hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, nullptr); + for (unsigned int i = 0; i < num_glyphs; i++) + { + info->cluster = i; + info++; + } + } + + setup_buffer (buffer); + } + + hb_bool_t shape (hb_font_t *font, hb_buffer_t *buffer, const char **error=nullptr) + { + hb_buffer_t *text_buffer = nullptr; + if (verify) + { + text_buffer = hb_buffer_create (); + hb_buffer_append (text_buffer, buffer, 0, -1); + } + + if (!hb_shape_full (font, buffer, features, num_features, shapers)) + { + if (error) + *error = "all shapers failed."; + goto fail; + } + + if (normalize_glyphs) + hb_buffer_normalize_glyphs (buffer); + + if (verify && !verify_buffer (buffer, text_buffer, font, error)) + goto fail; + + if (text_buffer) + hb_buffer_destroy (text_buffer); + + return true; + + fail: + if (text_buffer) + hb_buffer_destroy (text_buffer); + + return false; + } + + bool verify_buffer (hb_buffer_t *buffer, + hb_buffer_t *text_buffer, + hb_font_t *font, + const char **error=nullptr) + { + if (!verify_buffer_monotone (buffer, error)) + return false; + if (!verify_buffer_safe_to_break (buffer, text_buffer, font, error)) + return false; + return true; + } + + bool verify_buffer_monotone (hb_buffer_t *buffer, const char **error=nullptr) + { + /* Check that clusters are monotone. */ + if (cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES || + cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS) + { + bool is_forward = HB_DIRECTION_IS_FORWARD (hb_buffer_get_direction (buffer)); + + unsigned int num_glyphs; + hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, &num_glyphs); + + for (unsigned int i = 1; i < num_glyphs; i++) + if (info[i-1].cluster != info[i].cluster && + (info[i-1].cluster < info[i].cluster) != is_forward) + { + if (error) + *error = "clusters are not monotone."; + return false; + } + } + + return true; + } + + bool verify_buffer_safe_to_break (hb_buffer_t *buffer, + hb_buffer_t *text_buffer, + hb_font_t *font, + const char **error=nullptr) + { + if (cluster_level != HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES && + cluster_level != HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS) + { + /* Cannot perform this check without monotone clusters. + * Then again, unsafe-to-break flag is much harder to use without + * monotone clusters. */ + return true; + } + + /* Check that breaking up shaping at safe-to-break is indeed safe. */ + + hb_buffer_t *fragment = hb_buffer_create (); + hb_buffer_t *reconstruction = hb_buffer_create (); + copy_buffer_properties (reconstruction, buffer); + + unsigned int num_glyphs; + hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, &num_glyphs); + + unsigned int num_chars; + hb_glyph_info_t *text = hb_buffer_get_glyph_infos (text_buffer, &num_chars); + + /* Chop text and shape fragments. */ + bool forward = HB_DIRECTION_IS_FORWARD (hb_buffer_get_direction (buffer)); + unsigned int start = 0; + unsigned int text_start = forward ? 0 : num_chars; + unsigned int text_end = text_start; + for (unsigned int end = 1; end < num_glyphs + 1; end++) + { + if (end < num_glyphs && + (info[end].cluster == info[end-1].cluster || + info[end-(forward?0:1)].mask & HB_GLYPH_FLAG_UNSAFE_TO_BREAK)) + continue; + + /* Shape segment corresponding to glyphs start..end. */ + if (end == num_glyphs) + { + if (forward) + text_end = num_chars; + else + text_start = 0; + } + else + { + if (forward) + { + unsigned int cluster = info[end].cluster; + while (text_end < num_chars && text[text_end].cluster < cluster) + text_end++; + } + else + { + unsigned int cluster = info[end - 1].cluster; + while (text_start && text[text_start - 1].cluster >= cluster) + text_start--; + } + } + assert (text_start < text_end); + + if (0) + printf("start %d end %d text start %d end %d\n", start, end, text_start, text_end); + + hb_buffer_clear_contents (fragment); + copy_buffer_properties (fragment, buffer); + + /* TODO: Add pre/post context text. */ + hb_buffer_flags_t flags = hb_buffer_get_flags (fragment); + if (0 < text_start) + flags = (hb_buffer_flags_t) (flags & ~HB_BUFFER_FLAG_BOT); + if (text_end < num_chars) + flags = (hb_buffer_flags_t) (flags & ~HB_BUFFER_FLAG_EOT); + hb_buffer_set_flags (fragment, flags); + + hb_buffer_append (fragment, text_buffer, text_start, text_end); + if (!hb_shape_full (font, fragment, features, num_features, shapers)) + { + if (error) + *error = "all shapers failed while shaping fragment."; + hb_buffer_destroy (reconstruction); + hb_buffer_destroy (fragment); + return false; + } + hb_buffer_append (reconstruction, fragment, 0, -1); + + start = end; + if (forward) + text_start = text_end; + else + text_end = text_start; + } + + bool ret = true; + hb_buffer_diff_flags_t diff = hb_buffer_diff (reconstruction, buffer, (hb_codepoint_t) -1, 0); + if (diff) + { + if (error) + *error = "Safe-to-break test failed."; + ret = false; + + /* Return the reconstructed result instead so it can be inspected. */ + hb_buffer_set_length (buffer, 0); + hb_buffer_append (buffer, reconstruction, 0, -1); + } + + hb_buffer_destroy (reconstruction); + hb_buffer_destroy (fragment); + + return ret; + } + + void shape_closure (const char *text, int text_len, + hb_font_t *font, hb_buffer_t *buffer, + hb_set_t *glyphs) + { + hb_buffer_reset (buffer); + hb_buffer_add_utf8 (buffer, text, text_len, 0, text_len); + setup_buffer (buffer); + hb_ot_shape_glyphs_closure (font, buffer, features, num_features, glyphs); + } + + /* Buffer properties */ + char *direction = nullptr; + char *language = nullptr; + char *script = nullptr; + + /* Buffer flags */ + hb_bool_t bot = false; + hb_bool_t eot = false; + hb_bool_t preserve_default_ignorables = false; + hb_bool_t remove_default_ignorables = false; + + hb_feature_t *features = nullptr; + unsigned int num_features = 0; + char **shapers = nullptr; + hb_bool_t utf8_clusters = false; + hb_codepoint_t invisible_glyph = 0; + hb_buffer_cluster_level_t cluster_level = HB_BUFFER_CLUSTER_LEVEL_DEFAULT; + hb_bool_t normalize_glyphs = false; + hb_bool_t verify = false; + unsigned int num_iterations = 1; +}; + + +static gboolean +parse_shapers (const char *name G_GNUC_UNUSED, + const char *arg, + gpointer data, + GError **error) +{ + shape_options_t *shape_opts = (shape_options_t *) data; + char **shapers = g_strsplit (arg, ",", 0); + + for (char **shaper = shapers; *shaper; shaper++) { + bool found = false; + for (const char **hb_shaper = hb_shape_list_shapers (); *hb_shaper; hb_shaper++) { + if (strcmp (*shaper, *hb_shaper) == 0) { + found = true; + break; + } + } + if (!found) { + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + "Unknown or unsupported shaper: %s", *shaper); + g_strfreev (shapers); + return false; + } + } + + g_strfreev (shape_opts->shapers); + shape_opts->shapers = shapers; + return true; +} + +static G_GNUC_NORETURN gboolean +list_shapers (const char *name G_GNUC_UNUSED, + const char *arg G_GNUC_UNUSED, + gpointer data G_GNUC_UNUSED, + GError **error G_GNUC_UNUSED) +{ + for (const char **shaper = hb_shape_list_shapers (); *shaper; shaper++) + g_printf ("%s\n", *shaper); + + exit(0); +} + + +static gboolean +parse_features (const char *name G_GNUC_UNUSED, + const char *arg, + gpointer data, + GError **error G_GNUC_UNUSED) +{ + shape_options_t *shape_opts = (shape_options_t *) data; + char *s = (char *) arg; + size_t l = strlen (s); + char *p; + + shape_opts->num_features = 0; + g_free (shape_opts->features); + shape_opts->features = nullptr; + + /* if the string is quoted, strip the quotes */ + if (s[0] == s[l - 1] && (s[0] == '\"' || s[0] == '\'')) + { + s[l - 1] = '\0'; + s++; + } + + if (!*s) + return true; + + /* count the features first, so we can allocate memory */ + p = s; + do { + shape_opts->num_features++; + p = strchr (p, ','); + if (p) + p++; + } while (p); + + shape_opts->features = (hb_feature_t *) calloc (shape_opts->num_features, sizeof (*shape_opts->features)); + if (!shape_opts->features) + return false; + + /* now do the actual parsing */ + p = s; + shape_opts->num_features = 0; + while (p && *p) { + char *end = strchr (p, ','); + if (hb_feature_from_string (p, end ? end - p : -1, &shape_opts->features[shape_opts->num_features])) + shape_opts->num_features++; + p = end ? end + 1 : nullptr; + } + + return true; +} + +void +shape_options_t::add_options (option_parser_t *parser) +{ + GOptionEntry entries[] = + { + {"list-shapers", 0, G_OPTION_FLAG_NO_ARG, + G_OPTION_ARG_CALLBACK, (gpointer) &list_shapers, "List available shapers and quit", nullptr}, + {"shaper", 0, G_OPTION_FLAG_HIDDEN, + G_OPTION_ARG_CALLBACK, (gpointer) &parse_shapers, "Hidden duplicate of --shapers", nullptr}, + {"shapers", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_shapers, "Set comma-separated list of shapers to try","list"}, + {"direction", 0, 0, G_OPTION_ARG_STRING, &this->direction, "Set text direction (default: auto)", "ltr/rtl/ttb/btt"}, + {"language", 0, 0, G_OPTION_ARG_STRING, &this->language, "Set text language (default: $LANG)", "langstr"}, + {"script", 0, 0, G_OPTION_ARG_STRING, &this->script, "Set text script (default: auto)", "ISO-15924 tag"}, + {"bot", 0, 0, G_OPTION_ARG_NONE, &this->bot, "Treat text as beginning-of-paragraph", nullptr}, + {"eot", 0, 0, G_OPTION_ARG_NONE, &this->eot, "Treat text as end-of-paragraph", nullptr}, + {"preserve-default-ignorables",0, 0, G_OPTION_ARG_NONE, &this->preserve_default_ignorables, "Preserve Default-Ignorable characters", nullptr}, + {"remove-default-ignorables",0, 0, G_OPTION_ARG_NONE, &this->remove_default_ignorables, "Remove Default-Ignorable characters", nullptr}, + {"invisible-glyph", 0, 0, G_OPTION_ARG_INT, &this->invisible_glyph, "Glyph value to replace Default-Ignorables with", nullptr}, + {"utf8-clusters", 0, 0, G_OPTION_ARG_NONE, &this->utf8_clusters, "Use UTF8 byte indices, not char indices", nullptr}, + {"cluster-level", 0, 0, G_OPTION_ARG_INT, &this->cluster_level, "Cluster merging level (default: 0)", "0/1/2"}, + {"normalize-glyphs",0, 0, G_OPTION_ARG_NONE, &this->normalize_glyphs, "Rearrange glyph clusters in nominal order", nullptr}, + {"verify", 0, 0, G_OPTION_ARG_NONE, &this->verify, "Perform sanity checks on shaping results", nullptr}, + {"num-iterations", 'n', 0, G_OPTION_ARG_INT, &this->num_iterations, "Run shaper N times (default: 1)", "N"}, + {nullptr} + }; + parser->add_group (entries, + "shape", + "Shape options:", + "Options for the shaping process", + this); + + const gchar *features_help = "Comma-separated list of font features\n" + "\n" + " Features can be enabled or disabled, either globally or limited to\n" + " specific character ranges. The format for specifying feature settings\n" + " follows. All valid CSS font-feature-settings values other than 'normal'\n" + " and the global values are also accepted, though not documented below.\n" + " CSS string escapes are not supported." + "\n" + " The range indices refer to the positions between Unicode characters,\n" + " unless the --utf8-clusters is provided, in which case range indices\n" + " refer to UTF-8 byte indices. The position before the first character\n" + " is always 0.\n" + "\n" + " The format is Python-esque. Here is how it all works:\n" + "\n" + " Syntax: Value: Start: End:\n" + "\n" + " Setting value:\n" + " \"kern\" 1 0 ∞ # Turn feature on\n" + " \"+kern\" 1 0 ∞ # Turn feature on\n" + " \"-kern\" 0 0 ∞ # Turn feature off\n" + " \"kern=0\" 0 0 ∞ # Turn feature off\n" + " \"kern=1\" 1 0 ∞ # Turn feature on\n" + " \"aalt=2\" 2 0 ∞ # Choose 2nd alternate\n" + "\n" + " Setting index:\n" + " \"kern[]\" 1 0 ∞ # Turn feature on\n" + " \"kern[:]\" 1 0 ∞ # Turn feature on\n" + " \"kern[5:]\" 1 5 ∞ # Turn feature on, partial\n" + " \"kern[:5]\" 1 0 5 # Turn feature on, partial\n" + " \"kern[3:5]\" 1 3 5 # Turn feature on, range\n" + " \"kern[3]\" 1 3 3+1 # Turn feature on, single char\n" + "\n" + " Mixing it all:\n" + "\n" + " \"aalt[3:5]=2\" 2 3 5 # Turn 2nd alternate on for range"; + + GOptionEntry entries2[] = + { + {"features", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_features, features_help, "list"}, + {nullptr} + }; + parser->add_group (entries2, + "features", + "Features options:", + "Options for font features used", + this); +} + +#endif From 798bb89c969288704c1cc534192eac9e6339d66a Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Fri, 6 Aug 2021 23:29:29 -0600 Subject: [PATCH 26/33] [util] Move shape format options into shape-format.hh --- util/Makefile.sources | 1 + util/hb-shape.cc | 5 +- util/hb-subset.cc | 2 +- util/hb-view.cc | 2 +- util/options.cc | 135 -------------------------- util/options.hh | 42 --------- util/shape-format.hh | 214 ++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 220 insertions(+), 181 deletions(-) create mode 100644 util/shape-format.hh diff --git a/util/Makefile.sources b/util/Makefile.sources index 0595f0d5e..ee8414595 100644 --- a/util/Makefile.sources +++ b/util/Makefile.sources @@ -18,6 +18,7 @@ HB_SHAPE_sources = \ options.cc \ options.hh \ shape-consumer.hh \ + shape-format.hh \ shape-options.hh \ $(NULL) diff --git a/util/hb-shape.cc b/util/hb-shape.cc index 91d4489d2..79b0c3520 100644 --- a/util/hb-shape.cc +++ b/util/hb-shape.cc @@ -25,8 +25,9 @@ * Google Author(s): Behdad Esfahbod */ -#include "main-font-text.hh" #include "shape-consumer.hh" +#include "shape-format.hh" +#include "main-font-text.hh" const unsigned DEFAULT_FONT_SIZE = FONT_SIZE_UPEM; const unsigned SUBPIXEL_BITS = 0; @@ -146,7 +147,7 @@ struct output_buffer_t protected: output_options_t options; - format_options_t format; + shape_format_options_t format; GString *gs = nullptr; unsigned int line_no = 0; diff --git a/util/hb-subset.cc b/util/hb-subset.cc index 1469ae343..4c63251c6 100644 --- a/util/hb-subset.cc +++ b/util/hb-subset.cc @@ -27,8 +27,8 @@ #include -#include "main-font-text.hh" #include "subset-options.hh" +#include "main-font-text.hh" // XXX Remove eventually const unsigned DEFAULT_FONT_SIZE = FONT_SIZE_NONE; diff --git a/util/hb-view.cc b/util/hb-view.cc index 8289b6d1b..471ab4c6a 100644 --- a/util/hb-view.cc +++ b/util/hb-view.cc @@ -25,9 +25,9 @@ * Google Author(s): Behdad Esfahbod */ -#include "main-font-text.hh" #include "shape-consumer.hh" #include "view-cairo.hh" +#include "main-font-text.hh" const unsigned DEFAULT_FONT_SIZE = 256; const unsigned SUBPIXEL_BITS = 6; diff --git a/util/options.cc b/util/options.cc index 3f765a957..90d5752c2 100644 --- a/util/options.cc +++ b/util/options.cc @@ -639,138 +639,3 @@ output_options_t::get_file_handle () return fp; } -static gboolean -parse_verbose (const char *name G_GNUC_UNUSED, - const char *arg G_GNUC_UNUSED, - gpointer data G_GNUC_UNUSED, - GError **error G_GNUC_UNUSED) -{ - format_options_t *format_opts = (format_options_t *) data; - format_opts->show_text = format_opts->show_unicode = format_opts->show_line_num = true; - return true; -} - -static gboolean -parse_ned (const char *name G_GNUC_UNUSED, - const char *arg G_GNUC_UNUSED, - gpointer data G_GNUC_UNUSED, - GError **error G_GNUC_UNUSED) -{ - format_options_t *format_opts = (format_options_t *) data; - format_opts->show_clusters = format_opts->show_advances = false; - return true; -} - -void -format_options_t::add_options (option_parser_t *parser) -{ - GOptionEntry entries[] = - { - {"show-text", 0, 0, G_OPTION_ARG_NONE, &this->show_text, "Prefix each line of output with its corresponding input text", nullptr}, - {"show-unicode", 0, 0, G_OPTION_ARG_NONE, &this->show_unicode, "Prefix each line of output with its corresponding input codepoint(s)", nullptr}, - {"show-line-num", 0, 0, G_OPTION_ARG_NONE, &this->show_line_num, "Prefix each line of output with its corresponding input line number", nullptr}, - {"verbose", 'v', G_OPTION_FLAG_NO_ARG, - G_OPTION_ARG_CALLBACK, (gpointer) &parse_verbose, "Prefix each line of output with all of the above", nullptr}, - {"no-glyph-names", 0, G_OPTION_FLAG_REVERSE, - G_OPTION_ARG_NONE, &this->show_glyph_names, "Output glyph indices instead of names", nullptr}, - {"no-positions", 0, G_OPTION_FLAG_REVERSE, - G_OPTION_ARG_NONE, &this->show_positions, "Do not output glyph positions", nullptr}, - {"no-advances", 0, G_OPTION_FLAG_REVERSE, - G_OPTION_ARG_NONE, &this->show_advances, "Do not output glyph advances", nullptr}, - {"no-clusters", 0, G_OPTION_FLAG_REVERSE, - G_OPTION_ARG_NONE, &this->show_clusters, "Do not output cluster indices", nullptr}, - {"show-extents", 0, 0, G_OPTION_ARG_NONE, &this->show_extents, "Output glyph extents", nullptr}, - {"show-flags", 0, 0, G_OPTION_ARG_NONE, &this->show_flags, "Output glyph flags", nullptr}, - {"ned", 'v', G_OPTION_FLAG_NO_ARG, - G_OPTION_ARG_CALLBACK, (gpointer) &parse_ned, "No Extra Data; Do not output clusters or advances", nullptr}, - {"trace", 'V', 0, G_OPTION_ARG_NONE, &this->trace, "Output interim shaping results", nullptr}, - {nullptr} - }; - parser->add_group (entries, - "output-syntax", - "Output syntax:\n" - " text: [=@,+,|...]\n" - " json: [{\"g\": , \"ax\": , \"ay\": , \"dx\": , \"dy\": , \"cl\": }, ...]\n" - "\nOutput syntax options:", - "Options for the syntax of the output", - this); -} - -void -format_options_t::serialize (hb_buffer_t *buffer, - hb_font_t *font, - hb_buffer_serialize_format_t output_format, - hb_buffer_serialize_flags_t flags, - GString *gs) -{ - unsigned int num_glyphs = hb_buffer_get_length (buffer); - unsigned int start = 0; - - while (start < num_glyphs) - { - char buf[32768]; - unsigned int consumed; - start += hb_buffer_serialize (buffer, start, num_glyphs, - buf, sizeof (buf), &consumed, - font, output_format, flags); - if (!consumed) - break; - g_string_append (gs, buf); - } -} - -void -format_options_t::serialize_line_no (unsigned int line_no, - GString *gs) -{ - if (show_line_num) - g_string_append_printf (gs, "%d: ", line_no); -} -void -format_options_t::serialize_buffer_of_text (hb_buffer_t *buffer, - unsigned int line_no, - const char *text, - unsigned int text_len, - hb_font_t *font, - GString *gs) -{ - if (show_text) - { - serialize_line_no (line_no, gs); - g_string_append_c (gs, '('); - g_string_append_len (gs, text, text_len); - g_string_append_c (gs, ')'); - g_string_append_c (gs, '\n'); - } - - if (show_unicode) - { - serialize_line_no (line_no, gs); - serialize (buffer, font, HB_BUFFER_SERIALIZE_FORMAT_TEXT, HB_BUFFER_SERIALIZE_FLAG_DEFAULT, gs); - g_string_append_c (gs, '\n'); - } -} -void -format_options_t::serialize_message (unsigned int line_no, - const char *type, - const char *msg, - GString *gs) -{ - serialize_line_no (line_no, gs); - g_string_append_printf (gs, "%s: %s", type, msg); - g_string_append_c (gs, '\n'); -} -void -format_options_t::serialize_buffer_of_glyphs (hb_buffer_t *buffer, - unsigned int line_no, - const char *text, - unsigned int text_len, - hb_font_t *font, - hb_buffer_serialize_format_t output_format, - hb_buffer_serialize_flags_t format_flags, - GString *gs) -{ - serialize_line_no (line_no, gs); - serialize (buffer, font, output_format, format_flags, gs); - g_string_append_c (gs, '\n'); -} diff --git a/util/options.hh b/util/options.hh index 5bb5a9545..df8fb5be0 100644 --- a/util/options.hh +++ b/util/options.hh @@ -267,48 +267,6 @@ struct output_options_t mutable FILE *fp = nullptr; }; -struct format_options_t -{ - void add_options (option_parser_t *parser); - - void serialize (hb_buffer_t *buffer, - hb_font_t *font, - hb_buffer_serialize_format_t format, - hb_buffer_serialize_flags_t flags, - GString *gs); - void serialize_line_no (unsigned int line_no, - GString *gs); - void serialize_buffer_of_text (hb_buffer_t *buffer, - unsigned int line_no, - const char *text, - unsigned int text_len, - hb_font_t *font, - GString *gs); - void serialize_message (unsigned int line_no, - const char *type, - const char *msg, - GString *gs); - void serialize_buffer_of_glyphs (hb_buffer_t *buffer, - unsigned int line_no, - const char *text, - unsigned int text_len, - hb_font_t *font, - hb_buffer_serialize_format_t output_format, - hb_buffer_serialize_flags_t format_flags, - GString *gs); - - - hb_bool_t show_glyph_names = true; - hb_bool_t show_positions = true; - hb_bool_t show_advances = true; - hb_bool_t show_clusters = true; - hb_bool_t show_text = false; - hb_bool_t show_unicode = false; - hb_bool_t show_line_num = false; - hb_bool_t show_extents = false; - hb_bool_t show_flags = false; - hb_bool_t trace = false; -}; /* fallback implementation for scalbn()/scalbnf() for pre-2013 MSVC */ #if defined (_MSC_VER) && (_MSC_VER < 1800) diff --git a/util/shape-format.hh b/util/shape-format.hh new file mode 100644 index 000000000..18b0b96e1 --- /dev/null +++ b/util/shape-format.hh @@ -0,0 +1,214 @@ +/* + * Copyright © 2011 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef SHAPE_FORMAT_OPTIONS_HH +#define SHAPE_FORMAT_OPTIONS_HH + +#include "options.hh" + + +struct shape_format_options_t +{ + void add_options (option_parser_t *parser); + + void serialize (hb_buffer_t *buffer, + hb_font_t *font, + hb_buffer_serialize_format_t format, + hb_buffer_serialize_flags_t flags, + GString *gs); + void serialize_line_no (unsigned int line_no, + GString *gs); + void serialize_buffer_of_text (hb_buffer_t *buffer, + unsigned int line_no, + const char *text, + unsigned int text_len, + hb_font_t *font, + GString *gs); + void serialize_message (unsigned int line_no, + const char *type, + const char *msg, + GString *gs); + void serialize_buffer_of_glyphs (hb_buffer_t *buffer, + unsigned int line_no, + const char *text, + unsigned int text_len, + hb_font_t *font, + hb_buffer_serialize_format_t output_format, + hb_buffer_serialize_flags_t format_flags, + GString *gs); + + + hb_bool_t show_glyph_names = true; + hb_bool_t show_positions = true; + hb_bool_t show_advances = true; + hb_bool_t show_clusters = true; + hb_bool_t show_text = false; + hb_bool_t show_unicode = false; + hb_bool_t show_line_num = false; + hb_bool_t show_extents = false; + hb_bool_t show_flags = false; + hb_bool_t trace = false; +}; + + +static gboolean +parse_verbose (const char *name G_GNUC_UNUSED, + const char *arg G_GNUC_UNUSED, + gpointer data G_GNUC_UNUSED, + GError **error G_GNUC_UNUSED) +{ + shape_format_options_t *format_opts = (shape_format_options_t *) data; + format_opts->show_text = format_opts->show_unicode = format_opts->show_line_num = true; + return true; +} + +static gboolean +parse_ned (const char *name G_GNUC_UNUSED, + const char *arg G_GNUC_UNUSED, + gpointer data G_GNUC_UNUSED, + GError **error G_GNUC_UNUSED) +{ + shape_format_options_t *format_opts = (shape_format_options_t *) data; + format_opts->show_clusters = format_opts->show_advances = false; + return true; +} + +inline void +shape_format_options_t::serialize (hb_buffer_t *buffer, + hb_font_t *font, + hb_buffer_serialize_format_t output_format, + hb_buffer_serialize_flags_t flags, + GString *gs) +{ + unsigned int num_glyphs = hb_buffer_get_length (buffer); + unsigned int start = 0; + + while (start < num_glyphs) + { + char buf[32768]; + unsigned int consumed; + start += hb_buffer_serialize (buffer, start, num_glyphs, + buf, sizeof (buf), &consumed, + font, output_format, flags); + if (!consumed) + break; + g_string_append (gs, buf); + } +} + +inline void +shape_format_options_t::serialize_line_no (unsigned int line_no, + GString *gs) +{ + if (show_line_num) + g_string_append_printf (gs, "%d: ", line_no); +} +inline void +shape_format_options_t::serialize_buffer_of_text (hb_buffer_t *buffer, + unsigned int line_no, + const char *text, + unsigned int text_len, + hb_font_t *font, + GString *gs) +{ + if (show_text) + { + serialize_line_no (line_no, gs); + g_string_append_c (gs, '('); + g_string_append_len (gs, text, text_len); + g_string_append_c (gs, ')'); + g_string_append_c (gs, '\n'); + } + + if (show_unicode) + { + serialize_line_no (line_no, gs); + serialize (buffer, font, HB_BUFFER_SERIALIZE_FORMAT_TEXT, HB_BUFFER_SERIALIZE_FLAG_DEFAULT, gs); + g_string_append_c (gs, '\n'); + } +} +inline void +shape_format_options_t::serialize_message (unsigned int line_no, + const char *type, + const char *msg, + GString *gs) +{ + serialize_line_no (line_no, gs); + g_string_append_printf (gs, "%s: %s", type, msg); + g_string_append_c (gs, '\n'); +} +inline void +shape_format_options_t::serialize_buffer_of_glyphs (hb_buffer_t *buffer, + unsigned int line_no, + const char *text, + unsigned int text_len, + hb_font_t *font, + hb_buffer_serialize_format_t output_format, + hb_buffer_serialize_flags_t format_flags, + GString *gs) +{ + serialize_line_no (line_no, gs); + serialize (buffer, font, output_format, format_flags, gs); + g_string_append_c (gs, '\n'); +} + + +void +shape_format_options_t::add_options (option_parser_t *parser) +{ + GOptionEntry entries[] = + { + {"show-text", 0, 0, G_OPTION_ARG_NONE, &this->show_text, "Prefix each line of output with its corresponding input text", nullptr}, + {"show-unicode", 0, 0, G_OPTION_ARG_NONE, &this->show_unicode, "Prefix each line of output with its corresponding input codepoint(s)", nullptr}, + {"show-line-num", 0, 0, G_OPTION_ARG_NONE, &this->show_line_num, "Prefix each line of output with its corresponding input line number", nullptr}, + {"verbose", 'v', G_OPTION_FLAG_NO_ARG, + G_OPTION_ARG_CALLBACK, (gpointer) &parse_verbose, "Prefix each line of output with all of the above", nullptr}, + {"no-glyph-names", 0, G_OPTION_FLAG_REVERSE, + G_OPTION_ARG_NONE, &this->show_glyph_names, "Output glyph indices instead of names", nullptr}, + {"no-positions", 0, G_OPTION_FLAG_REVERSE, + G_OPTION_ARG_NONE, &this->show_positions, "Do not output glyph positions", nullptr}, + {"no-advances", 0, G_OPTION_FLAG_REVERSE, + G_OPTION_ARG_NONE, &this->show_advances, "Do not output glyph advances", nullptr}, + {"no-clusters", 0, G_OPTION_FLAG_REVERSE, + G_OPTION_ARG_NONE, &this->show_clusters, "Do not output cluster indices", nullptr}, + {"show-extents", 0, 0, G_OPTION_ARG_NONE, &this->show_extents, "Output glyph extents", nullptr}, + {"show-flags", 0, 0, G_OPTION_ARG_NONE, &this->show_flags, "Output glyph flags", nullptr}, + {"ned", 'v', G_OPTION_FLAG_NO_ARG, + G_OPTION_ARG_CALLBACK, (gpointer) &parse_ned, "No Extra Data; Do not output clusters or advances", nullptr}, + {"trace", 'V', 0, G_OPTION_ARG_NONE, &this->trace, "Output interim shaping results", nullptr}, + {nullptr} + }; + parser->add_group (entries, + "output-syntax", + "Output syntax:\n" + " text: [=@,+,|...]\n" + " json: [{\"g\": , \"ax\": , \"ay\": , \"dx\": , \"dy\": , \"cl\": }, ...]\n" + "\nOutput syntax options:", + "Options for the syntax of the output", + this); +} + +#endif From 6edd50ae7fa550c909fa32339537f6105002a07d Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Fri, 6 Aug 2021 23:36:45 -0600 Subject: [PATCH 27/33] [util] Move option_parser_t code into options.hh --- util/options.cc | 83 ------------------------------------------------ util/options.hh | 84 ++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 83 insertions(+), 84 deletions(-) diff --git a/util/options.cc b/util/options.cc index 90d5752c2..03f16f222 100644 --- a/util/options.cc +++ b/util/options.cc @@ -43,88 +43,6 @@ static struct supported_font_funcs_t { }; -void -fail (hb_bool_t suggest_help, const char *format, ...) -{ - const char *msg; - - va_list vap; - va_start (vap, format); - msg = g_strdup_vprintf (format, vap); - va_end (vap); - const char *prgname = g_get_prgname (); - g_printerr ("%s: %s\n", prgname, msg); - if (suggest_help) - g_printerr ("Try `%s --help' for more information.\n", prgname); - - exit (1); -} - - -static gchar * -shapers_to_string () -{ - GString *shapers = g_string_new (nullptr); - const char **shaper_list = hb_shape_list_shapers (); - - for (; *shaper_list; shaper_list++) { - g_string_append (shapers, *shaper_list); - g_string_append_c (shapers, ','); - } - g_string_truncate (shapers, MAX (0, (gint)shapers->len - 1)); - - return g_string_free (shapers, false); -} - -static G_GNUC_NORETURN gboolean -show_version (const char *name G_GNUC_UNUSED, - const char *arg G_GNUC_UNUSED, - gpointer data G_GNUC_UNUSED, - GError **error G_GNUC_UNUSED) -{ - g_printf ("%s (%s) %s\n", g_get_prgname (), PACKAGE_NAME, PACKAGE_VERSION); - - char *shapers = shapers_to_string (); - g_printf ("Available shapers: %s\n", shapers); - g_free (shapers); - if (strcmp (HB_VERSION_STRING, hb_version_string ())) - g_printf ("Linked HarfBuzz library has a different version: %s\n", hb_version_string ()); - - exit(0); -} - - -void -option_parser_t::add_main_options () -{ - GOptionEntry entries[] = - { - {"version", 0, G_OPTION_FLAG_NO_ARG, - G_OPTION_ARG_CALLBACK, (gpointer) &show_version, "Show version numbers", nullptr}, - {nullptr} - }; - g_option_context_add_main_entries (context, entries, nullptr); -} - -void -option_parser_t::parse (int *argc, char ***argv) -{ - setlocale (LC_ALL, ""); - - GError *parse_error = nullptr; - if (!g_option_context_parse (context, argc, argv, &parse_error)) - { - if (parse_error) - { - fail (true, "%s", parse_error->message); - //g_error_free (parse_error); - } - else - fail (true, "Option parse error"); - } -} - - static gboolean parse_variations (const char *name G_GNUC_UNUSED, const char *arg, @@ -638,4 +556,3 @@ output_options_t::get_file_handle () return fp; } - diff --git a/util/options.hh b/util/options.hh index df8fb5be0..d35193e75 100644 --- a/util/options.hh +++ b/util/options.hh @@ -50,7 +50,25 @@ #include #include -void fail (hb_bool_t suggest_help, const char *format, ...) G_GNUC_NORETURN G_GNUC_PRINTF (2, 3); + +static inline void fail (hb_bool_t suggest_help, const char *format, ...) G_GNUC_NORETURN G_GNUC_PRINTF (2, 3); + +static inline void +fail (hb_bool_t suggest_help, const char *format, ...) +{ + const char *msg; + + va_list vap; + va_start (vap, format); + msg = g_strdup_vprintf (format, vap); + va_end (vap); + const char *prgname = g_get_prgname (); + g_printerr ("%s: %s\n", prgname, msg); + if (suggest_help) + g_printerr ("Try `%s --help' for more information.\n", prgname); + + exit (1); +} struct option_parser_t @@ -123,6 +141,70 @@ struct option_parser_t }; +static inline gchar * +shapers_to_string () +{ + GString *shapers = g_string_new (nullptr); + const char **shaper_list = hb_shape_list_shapers (); + + for (; *shaper_list; shaper_list++) { + g_string_append (shapers, *shaper_list); + g_string_append_c (shapers, ','); + } + g_string_truncate (shapers, MAX (0, (gint)shapers->len - 1)); + + return g_string_free (shapers, false); +} + +static G_GNUC_NORETURN gboolean +show_version (const char *name G_GNUC_UNUSED, + const char *arg G_GNUC_UNUSED, + gpointer data G_GNUC_UNUSED, + GError **error G_GNUC_UNUSED) +{ + g_printf ("%s (%s) %s\n", g_get_prgname (), PACKAGE_NAME, PACKAGE_VERSION); + + char *shapers = shapers_to_string (); + g_printf ("Available shapers: %s\n", shapers); + g_free (shapers); + if (strcmp (HB_VERSION_STRING, hb_version_string ())) + g_printf ("Linked HarfBuzz library has a different version: %s\n", hb_version_string ()); + + exit(0); +} + +inline void +option_parser_t::add_main_options () +{ + GOptionEntry entries[] = + { + {"version", 0, G_OPTION_FLAG_NO_ARG, + G_OPTION_ARG_CALLBACK, (gpointer) &show_version, "Show version numbers", nullptr}, + {nullptr} + }; + g_option_context_add_main_entries (context, entries, nullptr); +} + +inline void +option_parser_t::parse (int *argc, char ***argv) +{ + setlocale (LC_ALL, ""); + + GError *parse_error = nullptr; + if (!g_option_context_parse (context, argc, argv, &parse_error)) + { + if (parse_error) + { + fail (true, "%s", parse_error->message); + //g_error_free (parse_error); + } + else + fail (true, "Option parse error"); + } +} + +// XXXXXXXXXXXX + #define FONT_SIZE_UPEM 0x7FFFFFFF #define FONT_SIZE_NONE 0 From 58c223357b340ceb64af337b481fd4786152881e Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Fri, 6 Aug 2021 23:45:59 -0600 Subject: [PATCH 28/33] [util] Move face/font options into face/font-options.hh --- util/Makefile.sources | 7 + util/face-options.hh | 135 +++++++++++++++ util/font-options.hh | 306 ++++++++++++++++++++++++++++++++++ util/hb-ot-shape-closure.cc | 3 +- util/hb-shape.cc | 2 +- util/hb-subset.cc | 7 +- util/hb-view.cc | 2 +- util/main-font-text.hh | 2 +- util/options.cc | 318 +----------------------------------- util/options.hh | 63 ------- util/shape-consumer.hh | 1 + 11 files changed, 457 insertions(+), 389 deletions(-) create mode 100644 util/face-options.hh create mode 100644 util/font-options.hh diff --git a/util/Makefile.sources b/util/Makefile.sources index ee8414595..200d3f673 100644 --- a/util/Makefile.sources +++ b/util/Makefile.sources @@ -1,5 +1,7 @@ HB_VIEW_sources = \ ansi-print.hh \ + face-options.hh \ + font-options.hh \ hb-view.cc \ helper-cairo-ansi.hh \ helper-cairo.hh \ @@ -13,6 +15,8 @@ HB_VIEW_sources = \ $(NULL) HB_SHAPE_sources = \ + face-options.hh \ + font-options.hh \ hb-shape.cc \ main-font-text.hh \ options.cc \ @@ -23,6 +27,8 @@ HB_SHAPE_sources = \ $(NULL) HB_OT_SHAPE_CLOSURE_sources = \ + face-options.hh \ + font-options.hh \ hb-ot-shape-closure.cc \ main-font-text.hh \ options.cc \ @@ -30,6 +36,7 @@ HB_OT_SHAPE_CLOSURE_sources = \ $(NULL) HB_SUBSET_CLI_sources = \ + face-options.hh \ hb-subset.cc \ main-font-text.hh \ options.cc \ diff --git a/util/face-options.hh b/util/face-options.hh new file mode 100644 index 000000000..12cfd3023 --- /dev/null +++ b/util/face-options.hh @@ -0,0 +1,135 @@ +/* + * Copyright © 2011 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef FACE_OPTIONS_HH +#define FACE_OPTIONS_HH + +#include "options.hh" + +struct face_options_t +{ + void add_options (option_parser_t *parser); + + hb_blob_t *get_blob () const; + hb_face_t *get_face () const; + + static struct cache_t + { + ~cache_t () + { + free ((void *) font_path); + hb_blob_destroy (blob); + hb_face_destroy (face); + } + + const char *font_path = nullptr; + hb_blob_t *blob = nullptr; + unsigned face_index = (unsigned) -1; + hb_face_t *face = nullptr; + } cache; + + char *font_file = nullptr; + unsigned face_index = 0; + private: + mutable hb_face_t *face = nullptr; +}; + + +face_options_t::cache_t face_options_t::cache {}; + +hb_blob_t * +face_options_t::get_blob () const +{ + // XXX This does the job for now; will move to post_parse. + return cache.blob; +} + +hb_face_t * +face_options_t::get_face () const +{ + if (face) + return face; + + if (!font_file) + fail (true, "No font file set"); + + const char *font_path = font_file; + + if (0 == strcmp (font_path, "-")) + { +#if defined(_WIN32) || defined(__CYGWIN__) + setmode (fileno (stdin), O_BINARY); + font_path = "STDIN"; +#else + font_path = "/dev/stdin"; +#endif + } + + if (!cache.font_path || 0 != strcmp (cache.font_path, font_path)) + { + hb_blob_destroy (cache.blob); + cache.blob = hb_blob_create_from_file_or_fail (font_path); + + free ((char *) cache.font_path); + cache.font_path = strdup (font_path); + + if (!cache.blob) + fail (false, "%s: Failed reading file", font_path); + + hb_face_destroy (cache.face); + cache.face = nullptr; + cache.face_index = (unsigned) -1; + } + + if (cache.face_index != face_index) + { + hb_face_destroy (cache.face); + cache.face = hb_face_create (cache.blob, face_index); + cache.face_index = face_index; + } + + face = cache.face; + + return face; +} + +void +face_options_t::add_options (option_parser_t *parser) +{ + GOptionEntry entries[] = + { + {"font-file", 0, 0, G_OPTION_ARG_STRING, &this->font_file, "Set font file-name", "filename"}, + {"face-index", 0, 0, G_OPTION_ARG_INT, &this->face_index, "Set face index (default: 0)", "index"}, + {nullptr} + }; + parser->add_group (entries, + "face", + "Font-face options:", + "Options for the font face", + this); +} + +#endif diff --git a/util/font-options.hh b/util/font-options.hh new file mode 100644 index 000000000..108dbd824 --- /dev/null +++ b/util/font-options.hh @@ -0,0 +1,306 @@ +/* + * Copyright © 2011 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef FONT_OPTIONS_HH +#define FONT_OPTIONS_HH + +#include "face-options.hh" + +#ifdef HAVE_FREETYPE +#include +#endif +#include + +#define FONT_SIZE_UPEM 0x7FFFFFFF +#define FONT_SIZE_NONE 0 + +extern const unsigned DEFAULT_FONT_SIZE; +extern const unsigned SUBPIXEL_BITS; + +struct font_options_t : face_options_t +{ + ~font_options_t () + { + g_free (font_file); + free (variations); + g_free (font_funcs); + hb_font_destroy (font); + } + + void add_options (option_parser_t *parser); + + hb_font_t *get_font () const; + + hb_variation_t *variations = nullptr; + unsigned int num_variations = 0; + int x_ppem = 0; + int y_ppem = 0; + double ptem = 0.; + unsigned int subpixel_bits = SUBPIXEL_BITS; + mutable double font_size_x = DEFAULT_FONT_SIZE; + mutable double font_size_y = DEFAULT_FONT_SIZE; + char *font_funcs = nullptr; + int ft_load_flags = 2; + + private: + mutable hb_font_t *font = nullptr; +}; + + +static struct supported_font_funcs_t { + char name[4]; + void (*func) (hb_font_t *); +} supported_font_funcs[] = +{ +#ifdef HAVE_FREETYPE + {"ft", hb_ft_font_set_funcs}, +#endif + {"ot", hb_ot_font_set_funcs}, +}; + +hb_font_t * +font_options_t::get_font () const +{ + if (font) + return font; + + auto *face = get_face (); + + font = hb_font_create (face); + + if (font_size_x == FONT_SIZE_UPEM) + font_size_x = hb_face_get_upem (face); + if (font_size_y == FONT_SIZE_UPEM) + font_size_y = hb_face_get_upem (face); + + hb_font_set_ppem (font, x_ppem, y_ppem); + hb_font_set_ptem (font, ptem); + + int scale_x = (int) scalbnf (font_size_x, subpixel_bits); + int scale_y = (int) scalbnf (font_size_y, subpixel_bits); + hb_font_set_scale (font, scale_x, scale_y); + + hb_font_set_variations (font, variations, num_variations); + + void (*set_font_funcs) (hb_font_t *) = nullptr; + if (!font_funcs) + { + set_font_funcs = supported_font_funcs[0].func; + } + else + { + for (unsigned int i = 0; i < ARRAY_LENGTH (supported_font_funcs); i++) + if (0 == g_ascii_strcasecmp (font_funcs, supported_font_funcs[i].name)) + { + set_font_funcs = supported_font_funcs[i].func; + break; + } + if (!set_font_funcs) + { + GString *s = g_string_new (nullptr); + for (unsigned int i = 0; i < ARRAY_LENGTH (supported_font_funcs); i++) + { + if (i) + g_string_append_c (s, '/'); + g_string_append (s, supported_font_funcs[i].name); + } + char *p = g_string_free (s, FALSE); + fail (false, "Unknown font function implementation `%s'; supported values are: %s; default is %s", + font_funcs, + p, + supported_font_funcs[0].name); + //free (p); + } + } + set_font_funcs (font); +#ifdef HAVE_FREETYPE + hb_ft_font_set_load_flags (font, ft_load_flags); +#endif + + return font; +} + + +static gboolean +parse_variations (const char *name G_GNUC_UNUSED, + const char *arg, + gpointer data, + GError **error G_GNUC_UNUSED) +{ + font_options_t *font_opts = (font_options_t *) data; + char *s = (char *) arg; + char *p; + + font_opts->num_variations = 0; + g_free (font_opts->variations); + font_opts->variations = nullptr; + + if (!*s) + return true; + + /* count the variations first, so we can allocate memory */ + p = s; + do { + font_opts->num_variations++; + p = strchr (p, ','); + if (p) + p++; + } while (p); + + font_opts->variations = (hb_variation_t *) calloc (font_opts->num_variations, sizeof (*font_opts->variations)); + if (!font_opts->variations) + return false; + + /* now do the actual parsing */ + p = s; + font_opts->num_variations = 0; + while (p && *p) { + char *end = strchr (p, ','); + if (hb_variation_from_string (p, end ? end - p : -1, &font_opts->variations[font_opts->num_variations])) + font_opts->num_variations++; + p = end ? end + 1 : nullptr; + } + + return true; +} + +static gboolean +parse_font_size (const char *name G_GNUC_UNUSED, + const char *arg, + gpointer data, + GError **error G_GNUC_UNUSED) +{ + font_options_t *font_opts = (font_options_t *) data; + if (0 == strcmp (arg, "upem")) + { + font_opts->font_size_y = font_opts->font_size_x = FONT_SIZE_UPEM; + return true; + } + switch (sscanf (arg, "%lf%*[ ,]%lf", &font_opts->font_size_x, &font_opts->font_size_y)) { + case 1: font_opts->font_size_y = font_opts->font_size_x; HB_FALLTHROUGH; + case 2: return true; + default: + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + "%s argument should be one or two space-separated numbers", + name); + return false; + } +} + +static gboolean +parse_font_ppem (const char *name G_GNUC_UNUSED, + const char *arg, + gpointer data, + GError **error G_GNUC_UNUSED) +{ + font_options_t *font_opts = (font_options_t *) data; + switch (sscanf (arg, "%d%*[ ,]%d", &font_opts->x_ppem, &font_opts->y_ppem)) { + case 1: font_opts->y_ppem = font_opts->x_ppem; HB_FALLTHROUGH; + case 2: return true; + default: + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + "%s argument should be one or two space-separated numbers", + name); + return false; + } +} + +void +font_options_t::add_options (option_parser_t *parser) +{ + face_options_t::add_options (parser); + + char *text = nullptr; + + { + static_assert ((ARRAY_LENGTH_CONST (supported_font_funcs) > 0), + "No supported font-funcs found."); + GString *s = g_string_new (nullptr); + g_string_printf (s, "Set font functions implementation to use (default: %s)\n\n Supported font function implementations are: %s", + supported_font_funcs[0].name, + supported_font_funcs[0].name); + for (unsigned int i = 1; i < ARRAY_LENGTH (supported_font_funcs); i++) + { + g_string_append_c (s, '/'); + g_string_append (s, supported_font_funcs[i].name); + } + text = g_string_free (s, FALSE); + parser->free_later (text); + } + + char *font_size_text; + if (DEFAULT_FONT_SIZE == FONT_SIZE_UPEM) + font_size_text = (char *) "Font size (default: upem)"; + else + { + font_size_text = g_strdup_printf ("Font size (default: %d)", DEFAULT_FONT_SIZE); + parser->free_later (font_size_text); + } + + int font_size_flags = DEFAULT_FONT_SIZE == FONT_SIZE_NONE ? G_OPTION_FLAG_HIDDEN : 0; + GOptionEntry entries[] = + { + {"font-size", 0, font_size_flags, + G_OPTION_ARG_CALLBACK, (gpointer) &parse_font_size, font_size_text, "1/2 integers or 'upem'"}, + {"font-ppem", 0, font_size_flags, + G_OPTION_ARG_CALLBACK, (gpointer) &parse_font_ppem, "Set x,y pixels per EM (default: 0; disabled)", "1/2 integers"}, + {"font-ptem", 0, 0, + G_OPTION_ARG_DOUBLE, &this->ptem, "Set font point-size (default: 0; disabled)", "point-size"}, + {"font-funcs", 0, 0, G_OPTION_ARG_STRING, &this->font_funcs, text, "impl"}, + {"ft-load-flags", 0, 0, G_OPTION_ARG_INT, &this->ft_load_flags, "Set FreeType load-flags (default: 2)", "integer"}, + {nullptr} + }; + parser->add_group (entries, + "font", + "Font-instance options:", + "Options for the font instance", + this); + + const gchar *variations_help = "Comma-separated list of font variations\n" + "\n" + " Variations are set globally. The format for specifying variation settings\n" + " follows. All valid CSS font-variation-settings values other than 'normal'\n" + " and 'inherited' are also accepted, though, not documented below.\n" + "\n" + " The format is a tag, optionally followed by an equals sign, followed by a\n" + " number. For example:\n" + "\n" + " \"wght=500\"\n" + " \"slnt=-7.5\"\n"; + + GOptionEntry entries2[] = + { + {"variations", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_variations, variations_help, "list"}, + {nullptr} + }; + parser->add_group (entries2, + "variations", + "Variations options:", + "Options for font variations used", + this); +} + +#endif diff --git a/util/hb-ot-shape-closure.cc b/util/hb-ot-shape-closure.cc index 97d7e9ed0..c31ef60be 100644 --- a/util/hb-ot-shape-closure.cc +++ b/util/hb-ot-shape-closure.cc @@ -25,6 +25,7 @@ */ #include "shape-options.hh" +#include "font-options.hh" #include "main-font-text.hh" const unsigned DEFAULT_FONT_SIZE = FONT_SIZE_NONE; @@ -112,6 +113,6 @@ struct shape_closure_consumer_t int main (int argc, char **argv) { - main_font_text_t driver; + main_font_text_t driver; return driver.main (argc, argv); } diff --git a/util/hb-shape.cc b/util/hb-shape.cc index 79b0c3520..b9fbc7740 100644 --- a/util/hb-shape.cc +++ b/util/hb-shape.cc @@ -159,7 +159,7 @@ struct output_buffer_t int main (int argc, char **argv) { - using driver_t = main_font_text_t>; + using driver_t = main_font_text_t, font_options_t, text_options_t>; if (argc == 2 && !strcmp (argv[1], "--batch")) { diff --git a/util/hb-subset.cc b/util/hb-subset.cc index 4c63251c6..b48b73277 100644 --- a/util/hb-subset.cc +++ b/util/hb-subset.cc @@ -28,12 +28,9 @@ #include #include "subset-options.hh" +#include "face-options.hh" #include "main-font-text.hh" -// XXX Remove eventually -const unsigned DEFAULT_FONT_SIZE = FONT_SIZE_NONE; -const unsigned SUBPIXEL_BITS = 0; - /* * Command line interface to the harfbuzz font subsetter. */ @@ -135,7 +132,7 @@ struct subset_consumer_t : subset_options_t int main (int argc, char **argv) { - using driver_t = main_font_text_t; + using driver_t = main_font_text_t; if (argc == 2 && !strcmp (argv[1], "--batch")) { diff --git a/util/hb-view.cc b/util/hb-view.cc index 471ab4c6a..6e3398e71 100644 --- a/util/hb-view.cc +++ b/util/hb-view.cc @@ -35,6 +35,6 @@ const unsigned SUBPIXEL_BITS = 6; int main (int argc, char **argv) { - main_font_text_t> driver; + main_font_text_t, font_options_t, text_options_t> driver; return driver.main (argc, argv); } diff --git a/util/main-font-text.hh b/util/main-font-text.hh index f3a7feea1..55b676651 100644 --- a/util/main-font-text.hh +++ b/util/main-font-text.hh @@ -46,7 +46,7 @@ locale_to_utf8 (char *s) return t; } -template +template struct main_font_text_t { int diff --git a/util/options.cc b/util/options.cc index 03f16f222..00573cb51 100644 --- a/util/options.cc +++ b/util/options.cc @@ -25,67 +25,6 @@ */ #include "options.hh" - -#ifdef HAVE_FREETYPE -#include -#endif -#include - -static struct supported_font_funcs_t { - char name[4]; - void (*func) (hb_font_t *); -} supported_font_funcs[] = -{ -#ifdef HAVE_FREETYPE - {"ft", hb_ft_font_set_funcs}, -#endif - {"ot", hb_ot_font_set_funcs}, -}; - - -static gboolean -parse_variations (const char *name G_GNUC_UNUSED, - const char *arg, - gpointer data, - GError **error G_GNUC_UNUSED) -{ - font_options_t *font_opts = (font_options_t *) data; - char *s = (char *) arg; - char *p; - - font_opts->num_variations = 0; - g_free (font_opts->variations); - font_opts->variations = nullptr; - - if (!*s) - return true; - - /* count the variations first, so we can allocate memory */ - p = s; - do { - font_opts->num_variations++; - p = strchr (p, ','); - if (p) - p++; - } while (p); - - font_opts->variations = (hb_variation_t *) calloc (font_opts->num_variations, sizeof (*font_opts->variations)); - if (!font_opts->variations) - return false; - - /* now do the actual parsing */ - p = s; - font_opts->num_variations = 0; - while (p && *p) { - char *end = strchr (p, ','); - if (hb_variation_from_string (p, end ? end - p : -1, &font_opts->variations[font_opts->num_variations])) - font_opts->num_variations++; - p = end ? end + 1 : nullptr; - } - - return true; -} - static gboolean parse_text (const char *name G_GNUC_UNUSED, const char *arg, @@ -163,138 +102,6 @@ parse_unicodes (const char *name G_GNUC_UNUSED, return true; } -static gboolean -parse_font_size (const char *name G_GNUC_UNUSED, - const char *arg, - gpointer data, - GError **error G_GNUC_UNUSED) -{ - font_options_t *font_opts = (font_options_t *) data; - if (0 == strcmp (arg, "upem")) - { - font_opts->font_size_y = font_opts->font_size_x = FONT_SIZE_UPEM; - return true; - } - switch (sscanf (arg, "%lf%*[ ,]%lf", &font_opts->font_size_x, &font_opts->font_size_y)) { - case 1: font_opts->font_size_y = font_opts->font_size_x; HB_FALLTHROUGH; - case 2: return true; - default: - g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, - "%s argument should be one or two space-separated numbers", - name); - return false; - } -} - -static gboolean -parse_font_ppem (const char *name G_GNUC_UNUSED, - const char *arg, - gpointer data, - GError **error G_GNUC_UNUSED) -{ - font_options_t *font_opts = (font_options_t *) data; - switch (sscanf (arg, "%d%*[ ,]%d", &font_opts->x_ppem, &font_opts->y_ppem)) { - case 1: font_opts->y_ppem = font_opts->x_ppem; HB_FALLTHROUGH; - case 2: return true; - default: - g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, - "%s argument should be one or two space-separated numbers", - name); - return false; - } -} - -void -face_options_t::add_options (option_parser_t *parser) -{ - GOptionEntry entries[] = - { - {"font-file", 0, 0, G_OPTION_ARG_STRING, &this->font_file, "Set font file-name", "filename"}, - {"face-index", 0, 0, G_OPTION_ARG_INT, &this->face_index, "Set face index (default: 0)", "index"}, - {nullptr} - }; - parser->add_group (entries, - "face", - "Font-face options:", - "Options for the font face", - this); -} - -void -font_options_t::add_options (option_parser_t *parser) -{ - face_options_t::add_options (parser); - - char *text = nullptr; - - { - static_assert ((ARRAY_LENGTH_CONST (supported_font_funcs) > 0), - "No supported font-funcs found."); - GString *s = g_string_new (nullptr); - g_string_printf (s, "Set font functions implementation to use (default: %s)\n\n Supported font function implementations are: %s", - supported_font_funcs[0].name, - supported_font_funcs[0].name); - for (unsigned int i = 1; i < ARRAY_LENGTH (supported_font_funcs); i++) - { - g_string_append_c (s, '/'); - g_string_append (s, supported_font_funcs[i].name); - } - text = g_string_free (s, FALSE); - parser->free_later (text); - } - - char *font_size_text; - if (DEFAULT_FONT_SIZE == FONT_SIZE_UPEM) - font_size_text = (char *) "Font size (default: upem)"; - else - { - font_size_text = g_strdup_printf ("Font size (default: %d)", DEFAULT_FONT_SIZE); - parser->free_later (font_size_text); - } - - int font_size_flags = DEFAULT_FONT_SIZE == FONT_SIZE_NONE ? G_OPTION_FLAG_HIDDEN : 0; - GOptionEntry entries[] = - { - {"font-size", 0, font_size_flags, - G_OPTION_ARG_CALLBACK, (gpointer) &parse_font_size, font_size_text, "1/2 integers or 'upem'"}, - {"font-ppem", 0, font_size_flags, - G_OPTION_ARG_CALLBACK, (gpointer) &parse_font_ppem, "Set x,y pixels per EM (default: 0; disabled)", "1/2 integers"}, - {"font-ptem", 0, 0, - G_OPTION_ARG_DOUBLE, &this->ptem, "Set font point-size (default: 0; disabled)", "point-size"}, - {"font-funcs", 0, 0, G_OPTION_ARG_STRING, &this->font_funcs, text, "impl"}, - {"ft-load-flags", 0, 0, G_OPTION_ARG_INT, &this->ft_load_flags, "Set FreeType load-flags (default: 2)", "integer"}, - {nullptr} - }; - parser->add_group (entries, - "font", - "Font-instance options:", - "Options for the font instance", - this); - - const gchar *variations_help = "Comma-separated list of font variations\n" - "\n" - " Variations are set globally. The format for specifying variation settings\n" - " follows. All valid CSS font-variation-settings values other than 'normal'\n" - " and 'inherited' are also accepted, though, not documented below.\n" - "\n" - " The format is a tag, optionally followed by an equals sign, followed by a\n" - " number. For example:\n" - "\n" - " \"wght=500\"\n" - " \"slnt=-7.5\"\n"; - - GOptionEntry entries2[] = - { - {"variations", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_variations, variations_help, "list"}, - {nullptr} - }; - parser->add_group (entries2, - "variations", - "Variations options:", - "Options for font variations used", - this); -} - void text_options_t::add_options (option_parser_t *parser) { @@ -321,7 +128,7 @@ output_options_t::add_options (option_parser_t *parser, const char *text; if (!supported_formats) - text = "Set output serialization format"; + text = "Set output format"; else { char *items = g_strjoinv ("/", const_cast (supported_formats)); @@ -343,129 +150,6 @@ output_options_t::add_options (option_parser_t *parser, this); } - -face_options_t::cache_t face_options_t::cache {}; - -hb_blob_t * -face_options_t::get_blob () const -{ - // XXX This does the job for now; will move to post_parse. - return cache.blob; -} - -hb_face_t * -face_options_t::get_face () const -{ - if (face) - return face; - - if (!font_file) - fail (true, "No font file set"); - - const char *font_path = font_file; - - if (0 == strcmp (font_path, "-")) - { -#if defined(_WIN32) || defined(__CYGWIN__) - setmode (fileno (stdin), O_BINARY); - font_path = "STDIN"; -#else - font_path = "/dev/stdin"; -#endif - } - - if (!cache.font_path || 0 != strcmp (cache.font_path, font_path)) - { - hb_blob_destroy (cache.blob); - cache.blob = hb_blob_create_from_file_or_fail (font_path); - - free ((char *) cache.font_path); - cache.font_path = strdup (font_path); - - if (!cache.blob) - fail (false, "%s: Failed reading file", font_path); - - hb_face_destroy (cache.face); - cache.face = nullptr; - cache.face_index = (unsigned) -1; - } - - if (cache.face_index != face_index) - { - hb_face_destroy (cache.face); - cache.face = hb_face_create (cache.blob, face_index); - cache.face_index = face_index; - } - - face = cache.face; - - return face; -} - - -hb_font_t * -font_options_t::get_font () const -{ - if (font) - return font; - - auto *face = get_face (); - - font = hb_font_create (face); - - if (font_size_x == FONT_SIZE_UPEM) - font_size_x = hb_face_get_upem (face); - if (font_size_y == FONT_SIZE_UPEM) - font_size_y = hb_face_get_upem (face); - - hb_font_set_ppem (font, x_ppem, y_ppem); - hb_font_set_ptem (font, ptem); - - int scale_x = (int) scalbnf (font_size_x, subpixel_bits); - int scale_y = (int) scalbnf (font_size_y, subpixel_bits); - hb_font_set_scale (font, scale_x, scale_y); - - hb_font_set_variations (font, variations, num_variations); - - void (*set_font_funcs) (hb_font_t *) = nullptr; - if (!font_funcs) - { - set_font_funcs = supported_font_funcs[0].func; - } - else - { - for (unsigned int i = 0; i < ARRAY_LENGTH (supported_font_funcs); i++) - if (0 == g_ascii_strcasecmp (font_funcs, supported_font_funcs[i].name)) - { - set_font_funcs = supported_font_funcs[i].func; - break; - } - if (!set_font_funcs) - { - GString *s = g_string_new (nullptr); - for (unsigned int i = 0; i < ARRAY_LENGTH (supported_font_funcs); i++) - { - if (i) - g_string_append_c (s, '/'); - g_string_append (s, supported_font_funcs[i].name); - } - char *p = g_string_free (s, FALSE); - fail (false, "Unknown font function implementation `%s'; supported values are: %s; default is %s", - font_funcs, - p, - supported_font_funcs[0].name); - //free (p); - } - } - set_font_funcs (font); -#ifdef HAVE_FREETYPE - hb_ft_font_set_load_flags (font, ft_load_flags); -#endif - - return font; -} - - const char * text_options_t::get_line (unsigned int *len, int eol) { diff --git a/util/options.hh b/util/options.hh index d35193e75..bec2e5746 100644 --- a/util/options.hh +++ b/util/options.hh @@ -205,69 +205,6 @@ option_parser_t::parse (int *argc, char ***argv) // XXXXXXXXXXXX -#define FONT_SIZE_UPEM 0x7FFFFFFF -#define FONT_SIZE_NONE 0 - -extern const unsigned DEFAULT_FONT_SIZE; -extern const unsigned SUBPIXEL_BITS; - -struct face_options_t -{ - void add_options (option_parser_t *parser); - - hb_blob_t *get_blob () const; - hb_face_t *get_face () const; - - static struct cache_t - { - ~cache_t () - { - free ((void *) font_path); - hb_blob_destroy (blob); - hb_face_destroy (face); - } - - const char *font_path = nullptr; - hb_blob_t *blob = nullptr; - unsigned face_index = (unsigned) -1; - hb_face_t *face = nullptr; - } cache; - - char *font_file = nullptr; - unsigned face_index = 0; - private: - mutable hb_face_t *face = nullptr; -}; - -struct font_options_t : face_options_t -{ - ~font_options_t () - { - g_free (font_file); - free (variations); - g_free (font_funcs); - hb_font_destroy (font); - } - - void add_options (option_parser_t *parser); - - hb_font_t *get_font () const; - - hb_variation_t *variations = nullptr; - unsigned int num_variations = 0; - int x_ppem = 0; - int y_ppem = 0; - double ptem = 0.; - unsigned int subpixel_bits = SUBPIXEL_BITS; - mutable double font_size_x = DEFAULT_FONT_SIZE; - mutable double font_size_y = DEFAULT_FONT_SIZE; - char *font_funcs = nullptr; - int ft_load_flags = 2; - - private: - mutable hb_font_t *font = nullptr; -}; - struct text_options_t { diff --git a/util/shape-consumer.hh b/util/shape-consumer.hh index e6a139c5e..760756432 100644 --- a/util/shape-consumer.hh +++ b/util/shape-consumer.hh @@ -27,6 +27,7 @@ #ifndef HB_SHAPE_CONSUMER_HH #define HB_SHAPE_CONSUMER_HH +#include "font-options.hh" #include "shape-options.hh" From 5545eea7e575318d53aa42acbb09cf39e4b82134 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Sat, 7 Aug 2021 11:04:46 -0600 Subject: [PATCH 29/33] [util] Remove main_font_text_t class, use bare function --- util/hb-ot-shape-closure.cc | 3 +- util/hb-shape.cc | 8 ++-- util/hb-subset.cc | 8 ++-- util/hb-view.cc | 3 +- util/main-font-text.hh | 82 +++++++++++++++---------------------- util/options.hh | 15 +++++++ 6 files changed, 55 insertions(+), 64 deletions(-) diff --git a/util/hb-ot-shape-closure.cc b/util/hb-ot-shape-closure.cc index c31ef60be..b9cab9815 100644 --- a/util/hb-ot-shape-closure.cc +++ b/util/hb-ot-shape-closure.cc @@ -113,6 +113,5 @@ struct shape_closure_consumer_t int main (int argc, char **argv) { - main_font_text_t driver; - return driver.main (argc, argv); + return main_font_text (argc, argv); } diff --git a/util/hb-shape.cc b/util/hb-shape.cc index b9fbc7740..732923b13 100644 --- a/util/hb-shape.cc +++ b/util/hb-shape.cc @@ -159,7 +159,7 @@ struct output_buffer_t int main (int argc, char **argv) { - using driver_t = main_font_text_t, font_options_t, text_options_t>; + auto main_func = main_font_text, font_options_t, text_options_t>; if (argc == 2 && !strcmp (argv[1], "--batch")) { @@ -185,13 +185,11 @@ main (int argc, char **argv) start_offset = argc == 2 && p[0] != '\0' && p[0] != ':' && p[1] == ':' && (p[2] == '\\' || p[2] == '/') ? 2 : 0; } - driver_t driver; - ret |= driver.main (argc, args, EOF); + ret |= main_func (argc, args, EOF); fflush (stdout); } return ret; } - driver_t driver; - return driver.main (argc, argv); + return main_func (argc, argv, '\n'); } diff --git a/util/hb-subset.cc b/util/hb-subset.cc index b48b73277..c20cade42 100644 --- a/util/hb-subset.cc +++ b/util/hb-subset.cc @@ -132,7 +132,7 @@ struct subset_consumer_t : subset_options_t int main (int argc, char **argv) { - using driver_t = main_font_text_t; + auto main_func = main_font_text; if (argc == 2 && !strcmp (argv[1], "--batch")) { @@ -156,8 +156,7 @@ main (int argc, char **argv) args[argc++] = p = e; } - driver_t driver; - int result = driver.main (argc, args, EOF); + int result = main_func (argc, args, EOF); fprintf (stdout, result == 0 ? "success\n" : "failure\n"); fflush (stdout); ret |= result; @@ -165,6 +164,5 @@ main (int argc, char **argv) return ret; } - driver_t driver; - return driver.main (argc, argv); + return main_func (argc, argv, '\n'); } diff --git a/util/hb-view.cc b/util/hb-view.cc index 6e3398e71..ec148ecf0 100644 --- a/util/hb-view.cc +++ b/util/hb-view.cc @@ -35,6 +35,5 @@ const unsigned SUBPIXEL_BITS = 6; int main (int argc, char **argv) { - main_font_text_t, font_options_t, text_options_t> driver; - return driver.main (argc, argv); + return main_font_text, font_options_t, text_options_t> (argc, argv); } diff --git a/util/main-font-text.hh b/util/main-font-text.hh index 55b676651..a3e8d42bc 100644 --- a/util/main-font-text.hh +++ b/util/main-font-text.hh @@ -31,59 +31,41 @@ /* main() body for utilities taking font and processing text.*/ -static char * -locale_to_utf8 (char *s) +template +int +main_font_text (int argc, char **argv, int eol = '\n') { - char *t; - GError *error = nullptr; - t = g_locale_to_utf8 (s, -1, nullptr, nullptr, &error); - if (!t) - { - fail (true, "Failed converting text to UTF-8"); - } + font_options_t font_opts; + text_options_t input; + consumer_t consumer; - return t; + option_parser_t options ("[FONT-FILE] [TEXT]"); + font_opts.add_options (&options); + input.add_options (&options); + consumer.add_options (&options); + options.parse (&argc, &argv); + + argc--, argv++; + if (argc && !font_opts.font_file) font_opts.font_file = locale_to_utf8 (argv[0]), argc--, argv++; + if (argc && !input.text && !input.text_file) input.text = locale_to_utf8 (argv[0]), argc--, argv++; + if (argc) + fail (true, "Too many arguments on the command line"); + if (!font_opts.font_file) + options.usage (); + if (!input.text && !input.text_file) + input.text_file = g_strdup ("-"); + + consumer.init (&font_opts); + + unsigned int text_len; + const char *text; + while ((text = input.get_line (&text_len, eol))) + consumer.consume_line (text, text_len, input.text_before, input.text_after); + + consumer.finish (&font_opts); + + return consumer.failed ? 1 : 0; } -template -struct main_font_text_t -{ - int - main (int argc, char **argv, int eol = '\n') - { - - font_options_t font_opts; - text_options_t input; - consumer_t consumer; - - option_parser_t options ("[FONT-FILE] [TEXT]"); - font_opts.add_options (&options); - input.add_options (&options); - consumer.add_options (&options); - options.parse (&argc, &argv); - - argc--, argv++; - if (argc && !font_opts.font_file) font_opts.font_file = locale_to_utf8 (argv[0]), argc--, argv++; - if (argc && !input.text && !input.text_file) input.text = locale_to_utf8 (argv[0]), argc--, argv++; - if (argc) - fail (true, "Too many arguments on the command line"); - if (!font_opts.font_file) - options.usage (); - if (!input.text && !input.text_file) - input.text_file = g_strdup ("-"); - - consumer.init (&font_opts); - - unsigned int text_len; - const char *text; - while ((text = input.get_line (&text_len, eol))) - consumer.consume_line (text, text_len, input.text_before, input.text_after); - - consumer.finish (&font_opts); - - return consumer.failed ? 1 : 0; - } -}; - #endif diff --git a/util/options.hh b/util/options.hh index bec2e5746..ee9041845 100644 --- a/util/options.hh +++ b/util/options.hh @@ -70,6 +70,21 @@ fail (hb_bool_t suggest_help, const char *format, ...) exit (1); } +static inline char * +locale_to_utf8 (char *s) +{ + char *t; + GError *error = nullptr; + + t = g_locale_to_utf8 (s, -1, nullptr, nullptr, &error); + if (!t) + { + fail (true, "Failed converting text to UTF-8"); + } + + return t; +} + struct option_parser_t { From 9db0fd4821ab721bbf558e999044384726ba650e Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Sat, 7 Aug 2021 12:22:17 -0600 Subject: [PATCH 30/33] [util] Don't show --output-format if no supported formats provided --- util/options.cc | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/util/options.cc b/util/options.cc index 00573cb51..5cbd4a1b6 100644 --- a/util/options.cc +++ b/util/options.cc @@ -125,11 +125,9 @@ void output_options_t::add_options (option_parser_t *parser, const char **supported_formats) { - const char *text; + const char *text = nullptr; - if (!supported_formats) - text = "Set output format"; - else + if (supported_formats) { char *items = g_strjoinv ("/", const_cast (supported_formats)); text = g_strdup_printf ("Set output format\n\n Supported output formats are: %s", items); @@ -140,7 +138,8 @@ output_options_t::add_options (option_parser_t *parser, GOptionEntry entries[] = { {"output-file", 'o', 0, G_OPTION_ARG_STRING, &this->output_file, "Set output file-name (default: stdout)","filename"}, - {"output-format", 'O', 0, G_OPTION_ARG_STRING, &this->output_format, text, "format"}, + {"output-format", 'O', supported_formats ? 0 : G_OPTION_FLAG_HIDDEN, + G_OPTION_ARG_STRING, &this->output_format, text, "format"}, {nullptr} }; parser->add_group (entries, From af9d2495dc68c4a64bab87a7cc0a60927d81fa53 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Sat, 7 Aug 2021 12:29:52 -0600 Subject: [PATCH 31/33] [util] Use inheritance for output_options_t --- util/hb-shape.cc | 41 ++++++++++++++++++++--------------------- util/hb-subset.cc | 9 +++------ util/view-cairo.hh | 12 ++++++------ 3 files changed, 29 insertions(+), 33 deletions(-) diff --git a/util/hb-shape.cc b/util/hb-shape.cc index 732923b13..776afcdb4 100644 --- a/util/hb-shape.cc +++ b/util/hb-shape.cc @@ -32,38 +32,38 @@ const unsigned DEFAULT_FONT_SIZE = FONT_SIZE_UPEM; const unsigned SUBPIXEL_BITS = 0; -struct output_buffer_t +struct output_buffer_t : output_options_t { void add_options (option_parser_t *parser) { - options.add_options (parser, hb_buffer_serialize_list_formats ()); + output_options_t::add_options (parser, hb_buffer_serialize_list_formats ()); format.add_options (parser); } void init (hb_buffer_t *buffer, const font_options_t *font_opts) { - options.get_file_handle (); + get_file_handle (); gs = g_string_new (nullptr); line_no = 0; font = hb_font_reference (font_opts->get_font ()); - if (!options.output_format) - output_format = HB_BUFFER_SERIALIZE_FORMAT_TEXT; + if (!output_format) + serialize_format = HB_BUFFER_SERIALIZE_FORMAT_TEXT; else - output_format = hb_buffer_serialize_format_from_string (options.output_format, -1); + serialize_format = hb_buffer_serialize_format_from_string (output_format, -1); /* An empty "output_format" parameter basically skips output generating. * Useful for benchmarking. */ - if ((!options.output_format || *options.output_format) && - !hb_buffer_serialize_format_to_string (output_format)) + if ((!output_format || *output_format) && + !hb_buffer_serialize_format_to_string (serialize_format)) { - if (options.explicit_output_format) + if (explicit_output_format) fail (false, "Unknown output format `%s'; supported formats are: %s", - options.output_format, + output_format, g_strjoinv ("/", const_cast (hb_buffer_serialize_list_formats ()))); else /* Just default to TEXT if not explicitly requested and the * file extension is not recognized. */ - output_format = HB_BUFFER_SERIALIZE_FORMAT_TEXT; + serialize_format = HB_BUFFER_SERIALIZE_FORMAT_TEXT; } unsigned int flags = HB_BUFFER_SERIALIZE_FLAG_DEFAULT; @@ -79,7 +79,7 @@ struct output_buffer_t flags |= HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS; if (format.show_flags) flags |= HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS; - format_flags = (hb_buffer_serialize_flags_t) flags; + serialize_flags = (hb_buffer_serialize_flags_t) flags; if (format.trace) hb_buffer_set_message_func (buffer, message_func, this, nullptr); @@ -92,13 +92,13 @@ struct output_buffer_t { g_string_set_size (gs, 0); format.serialize_buffer_of_text (buffer, line_no, text, text_len, font, gs); - fprintf (options.fp, "%s", gs->str); + fprintf (fp, "%s", gs->str); } void error (const char *message) { g_string_set_size (gs, 0); format.serialize_message (line_no, "error", message, gs); - fprintf (options.fp, "%s", gs->str); + fprintf (fp, "%s", gs->str); } void consume_glyphs (hb_buffer_t *buffer, const char *text, @@ -107,8 +107,8 @@ struct output_buffer_t { g_string_set_size (gs, 0); format.serialize_buffer_of_glyphs (buffer, line_no, text, text_len, font, - output_format, format_flags, gs); - fprintf (options.fp, "%s", gs->str); + serialize_format, serialize_flags, gs); + fprintf (fp, "%s", gs->str); } void finish (hb_buffer_t *buffer, const font_options_t *font_opts) { @@ -138,22 +138,21 @@ struct output_buffer_t g_string_set_size (gs, 0); format.serialize_line_no (line_no, gs); g_string_append_printf (gs, "trace: %s buffer: ", message); - format.serialize (buffer, font, output_format, format_flags, gs); + format.serialize (buffer, font, serialize_format, serialize_flags, gs); g_string_append_c (gs, '\n'); - fprintf (options.fp, "%s", gs->str); + fprintf (fp, "%s", gs->str); } protected: - output_options_t options; shape_format_options_t format; GString *gs = nullptr; unsigned int line_no = 0; hb_font_t *font = nullptr; - hb_buffer_serialize_format_t output_format = HB_BUFFER_SERIALIZE_FORMAT_INVALID; - hb_buffer_serialize_flags_t format_flags = HB_BUFFER_SERIALIZE_FLAG_DEFAULT; + hb_buffer_serialize_format_t serialize_format = HB_BUFFER_SERIALIZE_FORMAT_INVALID; + hb_buffer_serialize_flags_t serialize_flags = HB_BUFFER_SERIALIZE_FLAG_DEFAULT; }; int diff --git a/util/hb-subset.cc b/util/hb-subset.cc index c20cade42..704070757 100644 --- a/util/hb-subset.cc +++ b/util/hb-subset.cc @@ -36,12 +36,12 @@ */ -struct subset_consumer_t : subset_options_t +struct subset_consumer_t : subset_options_t, output_options_t { void add_options (option_parser_t *parser) { subset_options_t::add_options (parser); - output_options.add_options (parser); + output_options_t::add_options (parser); } void init (const face_options_t *face_opts) @@ -110,7 +110,7 @@ struct subset_consumer_t : subset_options_t if (!failed) { hb_blob_t *result = hb_face_reference_blob (new_face); - write_file (output_options.output_file, result); + write_file (output_file, result); hb_blob_destroy (result); } @@ -122,9 +122,6 @@ struct subset_consumer_t : subset_options_t public: bool failed = false; - private: - output_options_t output_options; - hb_face_t *face = nullptr; hb_subset_input_t *input = nullptr; }; diff --git a/util/view-cairo.hh b/util/view-cairo.hh index d1915d0a5..47fbf35f4 100644 --- a/util/view-cairo.hh +++ b/util/view-cairo.hh @@ -30,7 +30,7 @@ #include "view-options.hh" #include "helper-cairo.hh" -struct view_cairo_t : view_options_t +struct view_cairo_t : view_options_t, output_options_t { ~view_cairo_t () { @@ -39,8 +39,8 @@ struct view_cairo_t : view_options_t void add_options (option_parser_t *parser) { - view_options_t::add_options (parser); - output_options.add_options (parser, helper_cairo_supported_formats); + view_options_t::add_options (parser); + output_options_t::add_options (parser, helper_cairo_supported_formats); } void init (hb_buffer_t *buffer, const font_options_t *font_opts) @@ -84,8 +84,6 @@ struct view_cairo_t : view_options_t void render (const font_options_t *font_opts); - output_options_t output_options; - hb_direction_t direction = HB_DIRECTION_INVALID; // Remove this, make segment_properties accessible GArray *lines = nullptr; int scale_bits = 0; @@ -142,7 +140,9 @@ view_cairo_t::render (const font_options_t *font_opts) /* Create surface. */ cairo_t *cr = helper_cairo_create_context (w + margin.l + margin.r, h + margin.t + margin.b, - static_cast (this), &output_options, content); + static_cast (this), + static_cast (this), + content); cairo_set_scaled_font (cr, scaled_font); /* Setup coordinate system. */ From 93bc62e9b24404d2af4042c4a9aa450af79be8dd Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Sat, 7 Aug 2021 13:13:58 -0600 Subject: [PATCH 32/33] [util] Move output options into output-options.hh --- util/Makefile.sources | 21 ++++--- util/hb-shape.cc | 1 + util/hb-subset.cc | 1 + util/helper-cairo.hh | 4 +- util/options.cc | 50 ----------------- util/options.hh | 40 -------------- util/output-options.hh | 123 +++++++++++++++++++++++++++++++++++++++++ util/view-cairo.hh | 1 + 8 files changed, 140 insertions(+), 101 deletions(-) create mode 100644 util/output-options.hh diff --git a/util/Makefile.sources b/util/Makefile.sources index 200d3f673..56c455661 100644 --- a/util/Makefile.sources +++ b/util/Makefile.sources @@ -8,6 +8,7 @@ HB_VIEW_sources = \ main-font-text.hh \ options.cc \ options.hh \ + output-options.hh \ shape-consumer.hh \ shape-options.hh \ view-cairo.hh \ @@ -21,11 +22,22 @@ HB_SHAPE_sources = \ main-font-text.hh \ options.cc \ options.hh \ + output-options.hh \ shape-consumer.hh \ shape-format.hh \ shape-options.hh \ $(NULL) +HB_SUBSET_CLI_sources = \ + face-options.hh \ + hb-subset.cc \ + main-font-text.hh \ + options.cc \ + options.hh \ + output-options.hh \ + subset-options.hh \ + $(NULL) + HB_OT_SHAPE_CLOSURE_sources = \ face-options.hh \ font-options.hh \ @@ -34,12 +46,3 @@ HB_OT_SHAPE_CLOSURE_sources = \ options.cc \ options.hh \ $(NULL) - -HB_SUBSET_CLI_sources = \ - face-options.hh \ - hb-subset.cc \ - main-font-text.hh \ - options.cc \ - options.hh \ - subset-options.hh \ - $(NULL) diff --git a/util/hb-shape.cc b/util/hb-shape.cc index 776afcdb4..29f99176b 100644 --- a/util/hb-shape.cc +++ b/util/hb-shape.cc @@ -27,6 +27,7 @@ #include "shape-consumer.hh" #include "shape-format.hh" +#include "output-options.hh" #include "main-font-text.hh" const unsigned DEFAULT_FONT_SIZE = FONT_SIZE_UPEM; diff --git a/util/hb-subset.cc b/util/hb-subset.cc index 704070757..b5f3f7f0a 100644 --- a/util/hb-subset.cc +++ b/util/hb-subset.cc @@ -28,6 +28,7 @@ #include #include "subset-options.hh" +#include "output-options.hh" #include "face-options.hh" #include "main-font-text.hh" diff --git a/util/helper-cairo.hh b/util/helper-cairo.hh index 9a1cd4c5a..e219e646b 100644 --- a/util/helper-cairo.hh +++ b/util/helper-cairo.hh @@ -27,8 +27,8 @@ #ifndef HELPER_CAIRO_HH #define HELPER_CAIRO_HH -#include "hb.hh" -#include "options.hh" +#include "view-options.hh" +#include "output-options.hh" #include #include diff --git a/util/options.cc b/util/options.cc index 5cbd4a1b6..66ddaa8e6 100644 --- a/util/options.cc +++ b/util/options.cc @@ -121,34 +121,6 @@ text_options_t::add_options (option_parser_t *parser) this); } -void -output_options_t::add_options (option_parser_t *parser, - const char **supported_formats) -{ - const char *text = nullptr; - - if (supported_formats) - { - char *items = g_strjoinv ("/", const_cast (supported_formats)); - text = g_strdup_printf ("Set output format\n\n Supported output formats are: %s", items); - g_free (items); - parser->free_later ((char *) text); - } - - GOptionEntry entries[] = - { - {"output-file", 'o', 0, G_OPTION_ARG_STRING, &this->output_file, "Set output file-name (default: stdout)","filename"}, - {"output-format", 'O', supported_formats ? 0 : G_OPTION_FLAG_HIDDEN, - G_OPTION_ARG_STRING, &this->output_format, text, "format"}, - {nullptr} - }; - parser->add_group (entries, - "output", - "Output destination & format options:", - "Options for the destination & form of the output", - this); -} - const char * text_options_t::get_line (unsigned int *len, int eol) { @@ -217,25 +189,3 @@ text_options_t::get_line (unsigned int *len, int eol) *len = gs->len; return !*len && feof (fp) ? nullptr : gs->str; } - - -FILE * -output_options_t::get_file_handle () -{ - if (fp) - return fp; - - if (output_file) - fp = fopen (output_file, "wb"); - else { -#if defined(_WIN32) || defined(__CYGWIN__) - setmode (fileno (stdout), O_BINARY); -#endif - fp = stdout; - } - if (!fp) - fail (false, "Cannot open output file `%s': %s", - g_filename_display_name (output_file), strerror (errno)); - - return fp; -} diff --git a/util/options.hh b/util/options.hh index ee9041845..246a7c6df 100644 --- a/util/options.hh +++ b/util/options.hh @@ -261,46 +261,6 @@ struct text_options_t unsigned int line_len = UINT_MAX; }; -struct output_options_t -{ - ~output_options_t () - { - g_free (output_file); - g_free (output_format); - if (fp && fp != stdout) - fclose (fp); - } - - void add_options (option_parser_t *parser, - const char **supported_formats = nullptr); - - void post_parse (GError **error G_GNUC_UNUSED) - { - if (output_format) - explicit_output_format = true; - - if (output_file && !output_format) { - output_format = strrchr (output_file, '.'); - if (output_format) - { - output_format++; /* skip the dot */ - output_format = g_strdup (output_format); - } - } - - if (output_file && 0 == strcmp (output_file, "-")) - output_file = nullptr; /* STDOUT */ - } - - FILE *get_file_handle (); - - char *output_file = nullptr; - char *output_format = nullptr; - bool explicit_output_format = false; - - mutable FILE *fp = nullptr; -}; - /* fallback implementation for scalbn()/scalbnf() for pre-2013 MSVC */ #if defined (_MSC_VER) && (_MSC_VER < 1800) diff --git a/util/output-options.hh b/util/output-options.hh new file mode 100644 index 000000000..f3fda61e3 --- /dev/null +++ b/util/output-options.hh @@ -0,0 +1,123 @@ +/* + * Copyright © 2011 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef OUTPUT_OPTIONS_HH +#define OUTPUT_OPTIONS_HH + +#include "options.hh" + +struct output_options_t +{ + ~output_options_t () + { + g_free (output_file); + g_free (output_format); + if (fp && fp != stdout) + fclose (fp); + } + + void add_options (option_parser_t *parser, + const char **supported_formats = nullptr); + + void post_parse (GError **error G_GNUC_UNUSED) + { + if (output_format) + explicit_output_format = true; + + if (output_file && !output_format) { + output_format = strrchr (output_file, '.'); + if (output_format) + { + output_format++; /* skip the dot */ + output_format = g_strdup (output_format); + } + } + + if (output_file && 0 == strcmp (output_file, "-")) + output_file = nullptr; /* STDOUT */ + } + + FILE *get_file_handle (); + + char *output_file = nullptr; + char *output_format = nullptr; + bool explicit_output_format = false; + + mutable FILE *fp = nullptr; +}; + + +FILE * +output_options_t::get_file_handle () +{ + if (fp) + return fp; + + if (output_file) + fp = fopen (output_file, "wb"); + else { +#if defined(_WIN32) || defined(__CYGWIN__) + setmode (fileno (stdout), O_BINARY); +#endif + fp = stdout; + } + if (!fp) + fail (false, "Cannot open output file `%s': %s", + g_filename_display_name (output_file), strerror (errno)); + + return fp; +} + + +void +output_options_t::add_options (option_parser_t *parser, + const char **supported_formats) +{ + const char *text = nullptr; + + if (supported_formats) + { + char *items = g_strjoinv ("/", const_cast (supported_formats)); + text = g_strdup_printf ("Set output format\n\n Supported output formats are: %s", items); + g_free (items); + parser->free_later ((char *) text); + } + + GOptionEntry entries[] = + { + {"output-file", 'o', 0, G_OPTION_ARG_STRING, &this->output_file, "Set output file-name (default: stdout)","filename"}, + {"output-format", 'O', supported_formats ? 0 : G_OPTION_FLAG_HIDDEN, + G_OPTION_ARG_STRING, &this->output_format, text, "format"}, + {nullptr} + }; + parser->add_group (entries, + "output", + "Output destination & format options:", + "Options for the destination & form of the output", + this); +} + +#endif diff --git a/util/view-cairo.hh b/util/view-cairo.hh index 47fbf35f4..bfe4e2a0c 100644 --- a/util/view-cairo.hh +++ b/util/view-cairo.hh @@ -28,6 +28,7 @@ #define VIEW_CAIRO_HH #include "view-options.hh" +#include "output-options.hh" #include "helper-cairo.hh" struct view_cairo_t : view_options_t, output_options_t From 7d2e9164ab6681bfd9fa0946a1150905a1aba283 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Sat, 7 Aug 2021 13:38:19 -0600 Subject: [PATCH 33/33] [util] Move text options into text_options_t Remove now empty options.cc. --- util/Makefile.sources | 8 +- util/hb-ot-shape-closure.cc | 1 + util/hb-shape.cc | 4 +- util/hb-subset.cc | 1 + util/hb-view.cc | 2 + util/meson.build | 4 - util/options.hh | 43 ---------- util/{options.cc => text-options.hh} | 118 ++++++++++++++++----------- 8 files changed, 80 insertions(+), 101 deletions(-) rename util/{options.cc => text-options.hh} (74%) diff --git a/util/Makefile.sources b/util/Makefile.sources index 56c455661..470fd3c64 100644 --- a/util/Makefile.sources +++ b/util/Makefile.sources @@ -6,11 +6,11 @@ HB_VIEW_sources = \ helper-cairo-ansi.hh \ helper-cairo.hh \ main-font-text.hh \ - options.cc \ options.hh \ output-options.hh \ shape-consumer.hh \ shape-options.hh \ + text-options.hh \ view-cairo.hh \ view-options.hh \ $(NULL) @@ -20,22 +20,22 @@ HB_SHAPE_sources = \ font-options.hh \ hb-shape.cc \ main-font-text.hh \ - options.cc \ options.hh \ output-options.hh \ shape-consumer.hh \ shape-format.hh \ shape-options.hh \ + text-options.hh \ $(NULL) HB_SUBSET_CLI_sources = \ face-options.hh \ hb-subset.cc \ main-font-text.hh \ - options.cc \ options.hh \ output-options.hh \ subset-options.hh \ + text-options.hh \ $(NULL) HB_OT_SHAPE_CLOSURE_sources = \ @@ -43,6 +43,6 @@ HB_OT_SHAPE_CLOSURE_sources = \ font-options.hh \ hb-ot-shape-closure.cc \ main-font-text.hh \ - options.cc \ options.hh \ + text-options.hh \ $(NULL) diff --git a/util/hb-ot-shape-closure.cc b/util/hb-ot-shape-closure.cc index b9cab9815..41d18b549 100644 --- a/util/hb-ot-shape-closure.cc +++ b/util/hb-ot-shape-closure.cc @@ -26,6 +26,7 @@ #include "shape-options.hh" #include "font-options.hh" +#include "text-options.hh" #include "main-font-text.hh" const unsigned DEFAULT_FONT_SIZE = FONT_SIZE_NONE; diff --git a/util/hb-shape.cc b/util/hb-shape.cc index 29f99176b..d9c727f55 100644 --- a/util/hb-shape.cc +++ b/util/hb-shape.cc @@ -25,9 +25,11 @@ * Google Author(s): Behdad Esfahbod */ +#include "output-options.hh" +#include "font-options.hh" +#include "text-options.hh" #include "shape-consumer.hh" #include "shape-format.hh" -#include "output-options.hh" #include "main-font-text.hh" const unsigned DEFAULT_FONT_SIZE = FONT_SIZE_UPEM; diff --git a/util/hb-subset.cc b/util/hb-subset.cc index b5f3f7f0a..da808f901 100644 --- a/util/hb-subset.cc +++ b/util/hb-subset.cc @@ -30,6 +30,7 @@ #include "subset-options.hh" #include "output-options.hh" #include "face-options.hh" +#include "text-options.hh" #include "main-font-text.hh" /* diff --git a/util/hb-view.cc b/util/hb-view.cc index ec148ecf0..5d2dfe52a 100644 --- a/util/hb-view.cc +++ b/util/hb-view.cc @@ -27,6 +27,8 @@ #include "shape-consumer.hh" #include "view-cairo.hh" +#include "font-options.hh" +#include "text-options.hh" #include "main-font-text.hh" const unsigned DEFAULT_FONT_SIZE = 256; diff --git a/util/meson.build b/util/meson.build index 3773e68a6..fdab620fc 100644 --- a/util/meson.build +++ b/util/meson.build @@ -1,21 +1,17 @@ hb_view_sources = [ 'hb-view.cc', - 'options.cc', ] hb_shape_sources = [ 'hb-shape.cc', - 'options.cc', ] hb_ot_shape_closure_sources = [ 'hb-ot-shape-closure.cc', - 'options.cc', ] hb_subset_cli_sources = [ 'hb-subset.cc', - 'options.cc', ] util_deps = [freetype_dep, cairo_dep, cairo_ft_dep, glib_dep] diff --git a/util/options.hh b/util/options.hh index 246a7c6df..ff542f4c5 100644 --- a/util/options.hh +++ b/util/options.hh @@ -218,49 +218,6 @@ option_parser_t::parse (int *argc, char ***argv) } } -// XXXXXXXXXXXX - - -struct text_options_t -{ - ~text_options_t () - { - g_free (text_before); - g_free (text_after); - g_free (text); - g_free (text_file); - if (gs) - g_string_free (gs, true); - if (fp && fp != stdin) - fclose (fp); - } - - void add_options (option_parser_t *parser); - - void post_parse (GError **error G_GNUC_UNUSED) - { - if (text && text_file) - g_set_error (error, - G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, - "Only one of text and text-file can be set"); - } - - const char *get_line (unsigned int *len, int eol = '\n'); - - char *text_before = nullptr; - char *text_after = nullptr; - - int text_len = -1; - char *text = nullptr; - char *text_file = nullptr; - - private: - FILE *fp = nullptr; - GString *gs = nullptr; - char *line = nullptr; - unsigned int line_len = UINT_MAX; -}; - /* fallback implementation for scalbn()/scalbnf() for pre-2013 MSVC */ #if defined (_MSC_VER) && (_MSC_VER < 1800) diff --git a/util/options.cc b/util/text-options.hh similarity index 74% rename from util/options.cc rename to util/text-options.hh index 66ddaa8e6..ad72d7384 100644 --- a/util/options.cc +++ b/util/text-options.hh @@ -1,30 +1,45 @@ -/* - * Copyright © 2011,2012 Google, Inc. - * - * This is part of HarfBuzz, a text shaping library. - * - * Permission is hereby granted, without written agreement and without - * license or royalty fees, to use, copy, modify, and distribute this - * software and its documentation for any purpose, provided that the - * above copyright notice and the following two paragraphs appear in - * all copies of this software. - * - * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR - * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES - * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN - * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * - * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, - * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS - * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO - * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - * - * Google Author(s): Behdad Esfahbod - */ -#include "options.hh" +struct text_options_t +{ + ~text_options_t () + { + g_free (text_before); + g_free (text_after); + g_free (text); + g_free (text_file); + if (gs) + g_string_free (gs, true); + if (fp && fp != stdin) + fclose (fp); + } + + void add_options (option_parser_t *parser); + + void post_parse (GError **error G_GNUC_UNUSED) + { + if (text && text_file) + g_set_error (error, + G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + "Only one of text and text-file can be set"); + } + + const char *get_line (unsigned int *len, int eol = '\n'); + + char *text_before = nullptr; + char *text_after = nullptr; + + int text_len = -1; + char *text = nullptr; + char *text_file = nullptr; + + private: + FILE *fp = nullptr; + GString *gs = nullptr; + char *line = nullptr; + unsigned int line_len = UINT_MAX; +}; + + static gboolean parse_text (const char *name G_GNUC_UNUSED, const char *arg, @@ -102,29 +117,11 @@ parse_unicodes (const char *name G_GNUC_UNUSED, return true; } -void -text_options_t::add_options (option_parser_t *parser) -{ - GOptionEntry entries[] = - { - {"text", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_text, "Set input text", "string"}, - {"text-file", 0, 0, G_OPTION_ARG_STRING, &this->text_file, "Set input text file-name\n\n If no text is provided, standard input is used for input.\n", "filename"}, - {"unicodes", 'u', 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_unicodes, "Set input Unicode codepoints", "list of hex numbers"}, - {"text-before", 0, 0, G_OPTION_ARG_STRING, &this->text_before, "Set text context before each line", "string"}, - {"text-after", 0, 0, G_OPTION_ARG_STRING, &this->text_after, "Set text context after each line", "string"}, - {nullptr} - }; - parser->add_group (entries, - "text", - "Text options:", - "Options for the input text", - this); -} - const char * text_options_t::get_line (unsigned int *len, int eol) { - if (text) { + if (text) + { if (!line) { line = text; @@ -141,11 +138,14 @@ text_options_t::get_line (unsigned int *len, int eol) const char *ret = line; const char *p = (const char *) memchr (line, eol, line_len); unsigned int ret_len; - if (!p) { + if (!p) + { ret_len = line_len; line += ret_len; line_len = 0; - } else { + } + else + { ret_len = p - ret; line += ret_len + 1; line_len -= ret_len + 1; @@ -155,7 +155,8 @@ text_options_t::get_line (unsigned int *len, int eol) return ret; } - if (!fp) { + if (!fp) + { if (!text_file) fail (true, "At least one of text or text-file must be set"); @@ -189,3 +190,22 @@ text_options_t::get_line (unsigned int *len, int eol) *len = gs->len; return !*len && feof (fp) ? nullptr : gs->str; } + +void +text_options_t::add_options (option_parser_t *parser) +{ + GOptionEntry entries[] = + { + {"text", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_text, "Set input text", "string"}, + {"text-file", 0, 0, G_OPTION_ARG_STRING, &this->text_file, "Set input text file-name\n\n If no text is provided, standard input is used for input.\n", "filename"}, + {"unicodes", 'u', 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_unicodes, "Set input Unicode codepoints", "list of hex numbers"}, + {"text-before", 0, 0, G_OPTION_ARG_STRING, &this->text_before, "Set text context before each line", "string"}, + {"text-after", 0, 0, G_OPTION_ARG_STRING, &this->text_after, "Set text context after each line", "string"}, + {nullptr} + }; + parser->add_group (entries, + "text", + "Text options:", + "Options for the input text", + this); +}