diff --git a/docs/harfbuzz-sections.txt b/docs/harfbuzz-sections.txt index 5b2e4b963..0eaae7264 100644 --- a/docs/harfbuzz-sections.txt +++ b/docs/harfbuzz-sections.txt @@ -346,6 +346,7 @@ hb_font_get_scale hb_font_get_user_data hb_font_get_variation_glyph hb_font_get_variation_glyph_func_t +hb_font_get_var_coords_design hb_font_get_var_coords_normalized hb_font_glyph_from_string hb_font_glyph_to_string diff --git a/src/hb-font.cc b/src/hb-font.cc index e89ad697e..0702c421c 100644 --- a/src/hb-font.cc +++ b/src/hb-font.cc @@ -33,6 +33,9 @@ #include "hb-ot.h" +#include "hb-ot-var-avar-table.hh" +#include "hb-ot-var-fvar-table.hh" + /** * SECTION:hb-font @@ -1332,6 +1335,7 @@ DEFINE_NULL_INSTANCE (hb_font_t) = 0, /* num_coords */ nullptr, /* coords */ + nullptr, /* design_coords */ const_cast (&_hb_Null_hb_font_funcs_t), @@ -1383,6 +1387,20 @@ hb_font_create (hb_face_t *face) return font; } +static void +_hb_font_adopt_var_coords (hb_font_t *font, + int *coords, /* 2.14 normalized */ + float *design_coords, + unsigned int coords_length) +{ + free (font->coords); + free (font->design_coords); + + font->coords = coords; + font->design_coords = design_coords; + font->num_coords = coords_length; +} + /** * hb_font_create_sub_font: * @parent: parent font. @@ -1413,15 +1431,22 @@ hb_font_create_sub_font (hb_font_t *parent) font->y_ppem = parent->y_ppem; font->ptem = parent->ptem; - font->num_coords = parent->num_coords; - if (font->num_coords) + unsigned int num_coords = parent->num_coords; + if (num_coords) { - unsigned int size = parent->num_coords * sizeof (parent->coords[0]); - font->coords = (int *) malloc (size); - if (unlikely (!font->coords)) - font->num_coords = 0; + int *coords = (int *) calloc (num_coords, sizeof (parent->coords[0])); + float *design_coords = (float *) calloc (num_coords, sizeof (parent->design_coords[0])); + if (likely (coords && design_coords)) + { + memcpy (coords, parent->coords, num_coords * sizeof (parent->coords[0])); + memcpy (design_coords, parent->design_coords, num_coords * sizeof (parent->design_coords[0])); + _hb_font_adopt_var_coords (font, coords, design_coords, num_coords); + } else - memcpy (font->coords, parent->coords, size); + { + free (coords); + free (design_coords); + } } return font; @@ -1481,6 +1506,7 @@ hb_font_destroy (hb_font_t *font) hb_font_funcs_destroy (font->klass); free (font->coords); + free (font->design_coords); free (font); } @@ -1842,17 +1868,6 @@ hb_font_get_ptem (hb_font_t *font) * Variations */ -static void -_hb_font_adopt_var_coords_normalized (hb_font_t *font, - int *coords, /* 2.14 normalized */ - unsigned int coords_length) -{ - free (font->coords); - - font->coords = coords; - font->num_coords = coords_length; -} - /** * hb_font_set_variations: * @@ -1875,13 +1890,27 @@ hb_font_set_variations (hb_font_t *font, unsigned int coords_length = hb_ot_var_get_axis_count (font->face); int *normalized = coords_length ? (int *) calloc (coords_length, sizeof (int)) : nullptr; - if (unlikely (coords_length && !normalized)) + float *design_coords = coords_length ? (float *) calloc (coords_length, sizeof (float)) : nullptr; + + if (unlikely (coords_length && !(normalized && design_coords))) + { + free (normalized); + free (design_coords); return; + } + + for (unsigned int i = 0; i < variations_length; i++) + { + hb_ot_var_axis_info_t info; + if (hb_ot_var_find_axis_info (font->face, variations[i].tag, &info) && + info.axis_index < coords_length) + design_coords[info.axis_index] = variations[i].value; + } hb_ot_var_normalize_variations (font->face, variations, variations_length, normalized, coords_length); - _hb_font_adopt_var_coords_normalized (font, normalized, coords_length); + _hb_font_adopt_var_coords (font, normalized, design_coords, coords_length); } /** @@ -1898,11 +1927,20 @@ hb_font_set_var_coords_design (hb_font_t *font, return; int *normalized = coords_length ? (int *) calloc (coords_length, sizeof (int)) : nullptr; - if (unlikely (coords_length && !normalized)) + float *design_coords = coords_length ? (float *) calloc (coords_length, sizeof (float)) : nullptr; + + if (unlikely (coords_length && !(normalized && design_coords))) + { + free (normalized); + free (design_coords); return; + } + + if (coords_length) + memcpy (design_coords, coords, font->num_coords * sizeof (font->design_coords[0])); hb_ot_var_normalize_coords (font->face, coords_length, coords, normalized); - _hb_font_adopt_var_coords_normalized (font, normalized, coords_length); + _hb_font_adopt_var_coords (font, normalized, design_coords, coords_length); } /** @@ -1946,13 +1984,30 @@ hb_font_set_var_coords_normalized (hb_font_t *font, return; int *copy = coords_length ? (int *) calloc (coords_length, sizeof (coords[0])) : nullptr; - if (unlikely (coords_length && !copy)) + int *unmapped = coords_length ? (int *) calloc (coords_length, sizeof (coords[0])) : nullptr; + float *design_coords = coords_length ? (float *) calloc (coords_length, sizeof (design_coords[0])) : nullptr; + + if (unlikely (coords_length && !(copy && unmapped && design_coords))) + { + free (copy); + free (unmapped); + free (design_coords); return; + } if (coords_length) + { memcpy (copy, coords, coords_length * sizeof (coords[0])); + memcpy (unmapped, coords, coords_length * sizeof (coords[0])); + } + + /* Best effort design coords simulation */ + font->face->table.avar->unmap_coords (unmapped, coords_length); + for (unsigned int i = 0; i < coords_length; ++i) + design_coords[i] = font->face->table.fvar->unnormalize_axis_value (i, unmapped[i]); + free (unmapped); - _hb_font_adopt_var_coords_normalized (font, copy, coords_length); + _hb_font_adopt_var_coords (font, copy, design_coords, coords_length); } /** @@ -1972,6 +2027,24 @@ hb_font_get_var_coords_normalized (hb_font_t *font, return font->coords; } + +/** + * hb_font_get_var_coords_design: + * + * Return value is valid as long as variation coordinates of the font + * are not modified. + * + * Since: REPLACEME + */ +const float * +hb_font_get_var_coords_design (hb_font_t *font, + unsigned int *length) +{ + if (length) + *length = font->num_coords; + + return font->design_coords; +} #endif #ifndef HB_DISABLE_DEPRECATED diff --git a/src/hb-font.h b/src/hb-font.h index ca4627f3e..695a51c40 100644 --- a/src/hb-font.h +++ b/src/hb-font.h @@ -705,6 +705,10 @@ hb_font_set_var_coords_design (hb_font_t *font, const float *coords, unsigned int coords_length); +HB_EXTERN const float * +hb_font_get_var_coords_design (hb_font_t *font, + unsigned int *length); + HB_EXTERN void hb_font_set_var_coords_normalized (hb_font_t *font, const int *coords, /* 2.14 normalized */ diff --git a/src/hb-font.hh b/src/hb-font.hh index b1e8e6440..4a5c85300 100644 --- a/src/hb-font.hh +++ b/src/hb-font.hh @@ -120,6 +120,7 @@ struct hb_font_t /* Font variation coordinates. */ unsigned int num_coords; int *coords; + float *design_coords; hb_font_funcs_t *klass; void *user_data; diff --git a/test/api/Makefile.am b/test/api/Makefile.am index 6f0bba9df..f5de297c5 100644 --- a/test/api/Makefile.am +++ b/test/api/Makefile.am @@ -61,6 +61,7 @@ TEST_PROGS = \ test-subset-colr \ test-subset-cbdt \ test-unicode \ + test-var-coords \ test-version \ test-subset-nameids \ $(NULL) diff --git a/test/api/test-var-coords.c b/test/api/test-var-coords.c new file mode 100644 index 000000000..ff75c1575 --- /dev/null +++ b/test/api/test-var-coords.c @@ -0,0 +1,76 @@ +/* + * Copyright © 2019 Ebrahim Byagowi + * + * 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. + */ + +#include "hb-test.h" + +#include + +/* Unit tests for hb_font_[gs]et_var_coords_ */ + +#ifndef G_APPROX_VALUE +#define G_APPROX_VALUE(a, b, epsilon) \ + (((a) > (b) ? (a) - (b) : (b) - (a)) < (epsilon)) +#endif + +#define EPSILON 0.05f + +static void +test_get_var_coords (void) +{ + hb_face_t *face = hb_test_open_font_file ("fonts/TestCFF2VF.otf"); + hb_font_t *font = hb_font_create (face); + + /* Normalized coords as input */ + int normalized_coords[] = {100, 0}; + hb_font_set_var_coords_normalized (font, normalized_coords, 2); + g_assert_cmpint ((int) hb_font_get_var_coords_design (font, NULL)[0], ==, 403); + g_assert_cmpint ((int) hb_font_get_var_coords_normalized (font, NULL)[0], ==, 100); + + /* Design coords as input */ + float design_coords[] = {206.f, 0}; + hb_font_set_var_coords_design (font, design_coords, 2); + g_assert_cmpint ((int) hb_font_get_var_coords_normalized (font, NULL)[0], ==, -16117); + g_assert_cmpint ((int) hb_font_get_var_coords_design (font, NULL)[0], ==, 206); + + for (float weight = 200; weight < 901; ++weight) + { + int normalized; + hb_ot_var_normalize_coords (face, 1, &weight, &normalized); + hb_font_set_var_coords_normalized (font, &normalized, 1); + float converted_back = hb_font_get_var_coords_design (font, NULL)[0]; + // fprintf (stderr, "%f: %d => %f\n", weight, normalized, converted_back); + g_assert_true (G_APPROX_VALUE (converted_back, weight, EPSILON)); + } + + hb_font_destroy (font); + hb_face_destroy (face); +} + +int +main (int argc, char **argv) +{ + hb_test_init (&argc, &argv); + hb_test_add (test_get_var_coords); + return hb_test_run (); +}