blob: 50dd104d35c4cd91eba28dce1f404187e49cfcf1 [file]
/*
* Copyright © 2023 Red Hat, 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.
*
* Author(s): Matthias Clasen
*/
#include "hb-test.h"
static void
test_glyph_names_post (void)
{
hb_face_t *face;
hb_font_t *font;
hb_bool_t ret;
char name [64];
face = hb_test_open_font_file ("fonts/adwaita.ttf");
font = hb_font_create (face);
ret = hb_font_get_glyph_name (font, 0, name, 64);
g_assert_true (ret);
g_assert_cmpstr (name, ==, ".notdef");
ret = hb_font_get_glyph_name (font, 1, name, 64);
g_assert_true (ret);
g_assert_cmpstr (name, ==, ".space");
ret = hb_font_get_glyph_name (font, 2, name, 64);
g_assert_true (ret);
g_assert_cmpstr (name, ==, "icon0");
ret = hb_font_get_glyph_name (font, 3, name, 64);
g_assert_true (ret);
g_assert_cmpstr (name, ==, "icon0.0");
ret = hb_font_get_glyph_name (font, 4, name, 64);
g_assert_true (ret);
g_assert_cmpstr (name, ==, "icon0.1");
ret = hb_font_get_glyph_name (font, 5, name, 64);
g_assert_true (ret);
g_assert_cmpstr (name, ==, "icon0.2");
/* beyond last glyph */
ret = hb_font_get_glyph_name (font, 100, name, 64);
g_assert_false (ret);
hb_font_destroy (font);
hb_face_destroy (face);
}
static void
test_glyph_names_cff (void)
{
hb_face_t *face;
hb_font_t *font;
hb_bool_t ret;
char name [64];
face = hb_test_open_font_file ("fonts/SourceSansPro-Regular.otf");
font = hb_font_create (face);
ret = hb_font_get_glyph_name (font, 0, name, 64);
g_assert_true (ret);
g_assert_cmpstr (name, ==, ".notdef");
ret = hb_font_get_glyph_name (font, 1, name, 64);
g_assert_true (ret);
g_assert_cmpstr (name, ==, "space");
ret = hb_font_get_glyph_name (font, 2, name, 64);
g_assert_true (ret);
g_assert_cmpstr (name, ==, "A");
/* beyond last glyph */
ret = hb_font_get_glyph_name (font, 2000, name, 64);
g_assert_false (ret);
hb_font_destroy (font);
hb_face_destroy (face);
}
/* Regression tests for issue #5946: hb_font_get_glyph_name (..., size=0)
* must not underflow and write past a zero-capacity buffer, while preserving
* the long-standing "return value only" probe behavior. */
static void
test_glyph_names_zero_size (void)
{
hb_face_t *face;
hb_font_t *font;
char guard[4] = { 0x7f, 0x7f, 0x7f, 0x7f };
face = hb_test_open_font_file ("fonts/adwaita.ttf");
font = hb_font_create (face);
/* size == 0 must not write to the buffer. */
g_assert_true (hb_font_get_glyph_name (font, 1, guard, 0));
g_assert_cmpint (guard[0], ==, 0x7f);
g_assert_cmpint (guard[1], ==, 0x7f);
g_assert_cmpint (guard[2], ==, 0x7f);
g_assert_cmpint (guard[3], ==, 0x7f);
hb_font_destroy (font);
hb_face_destroy (face);
}
static void
test_glyph_names_zero_size_post (void)
{
hb_face_t *face;
hb_font_t *font;
hb_bool_t ret;
char guard[8] = { 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f };
unsigned int i;
face = hb_test_open_font_file ("fonts/adwaita.ttf");
font = hb_font_create (face);
/* Named glyph, size == 0: keep the old return-value-only probe behavior
* and leave the buffer untouched. */
ret = hb_font_get_glyph_name (font, 1, guard, 0);
g_assert_true (ret);
for (i = 0; i < 8; i++)
g_assert_cmpint (guard[i], ==, 0x7f);
/* .notdef, size == 0: same invariant. Confirms it is not glyph-specific. */
ret = hb_font_get_glyph_name (font, 0, guard, 0);
g_assert_true (ret);
for (i = 0; i < 8; i++)
g_assert_cmpint (guard[i], ==, 0x7f);
/* Perform a successful sized lookup first, then retry with size == 0.
* Confirms no residual state corrupts the zero-size path. */
{
char name[64];
ret = hb_font_get_glyph_name (font, 2, name, 64);
g_assert_true (ret);
g_assert_cmpstr (name, ==, "icon0");
}
ret = hb_font_get_glyph_name (font, 2, guard, 0);
g_assert_true (ret);
for (i = 0; i < 8; i++)
g_assert_cmpint (guard[i], ==, 0x7f);
hb_font_destroy (font);
hb_face_destroy (face);
}
static void
test_glyph_names_zero_size_cff (void)
{
hb_face_t *face;
hb_font_t *font;
hb_bool_t ret;
char guard[8] = { 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f };
unsigned int i;
face = hb_test_open_font_file ("fonts/SourceSansPro-Regular.otf");
font = hb_font_create (face);
/* CFF-backed named glyph, size == 0: preserve the return-value-only probe
* behavior without writing into the buffer. */
ret = hb_font_get_glyph_name (font, 2, guard, 0);
g_assert_true (ret);
for (i = 0; i < 8; i++)
g_assert_cmpint (guard[i], ==, 0x7f);
/* .notdef on CFF. */
ret = hb_font_get_glyph_name (font, 0, guard, 0);
g_assert_true (ret);
for (i = 0; i < 8; i++)
g_assert_cmpint (guard[i], ==, 0x7f);
hb_font_destroy (font);
hb_face_destroy (face);
}
static void
test_glyph_names_zero_size_unnamed_glyph (void)
{
/* size == 0 on a glyph beyond the font's range must also return false
* and must not write. */
hb_face_t *face;
hb_font_t *font;
hb_bool_t ret;
char guard[4] = { 0x7f, 0x7f, 0x7f, 0x7f };
unsigned int i;
face = hb_test_open_font_file ("fonts/adwaita.ttf");
font = hb_font_create (face);
ret = hb_font_get_glyph_name (font, 100, guard, 0);
g_assert_false (ret);
for (i = 0; i < 4; i++)
g_assert_cmpint (guard[i], ==, 0x7f);
hb_font_destroy (font);
hb_face_destroy (face);
}
static void
test_glyph_names_zero_size_null_buffer (void)
{
/* name == NULL with size == 0 is a valid return-value-only probe. */
hb_face_t *face;
hb_font_t *font;
hb_bool_t ret;
face = hb_test_open_font_file ("fonts/adwaita.ttf");
font = hb_font_create (face);
/* Glyph with a name. */
ret = hb_font_get_glyph_name (font, 1, NULL, 0);
g_assert_true (ret);
/* Glyph beyond the font's range. */
ret = hb_font_get_glyph_name (font, 100, NULL, 0);
g_assert_false (ret);
hb_font_destroy (font);
hb_face_destroy (face);
}
static void
test_glyph_names_size_one (void)
{
/* Boundary: size == 1. The backend must produce exactly a trailing NUL
* at name[0] (len = min(|name|, size-1) = 0 bytes of name copied) and
* must not touch any byte past name[0]. */
hb_face_t *face;
hb_font_t *font;
hb_bool_t ret;
char buf[4] = { 0x7f, 0x7f, 0x7f, 0x7f };
face = hb_test_open_font_file ("fonts/adwaita.ttf");
font = hb_font_create (face);
ret = hb_font_get_glyph_name (font, 1, buf, 1);
g_assert_true (ret);
g_assert_cmpint (buf[0], ==, 0);
/* Bytes beyond the caller-declared capacity must remain intact. */
g_assert_cmpint (buf[1], ==, 0x7f);
g_assert_cmpint (buf[2], ==, 0x7f);
g_assert_cmpint (buf[3], ==, 0x7f);
hb_font_destroy (font);
hb_face_destroy (face);
}
static void
test_glyph_names_small_buffer_truncation (void)
{
/* Non-regression sanity: a small-but-nonzero buffer must truncate to
* size-1 bytes of the name plus a trailing NUL. */
hb_face_t *face;
hb_font_t *font;
hb_bool_t ret;
char buf[4] = { 0x7f, 0x7f, 0x7f, 0x7f };
face = hb_test_open_font_file ("fonts/adwaita.ttf");
font = hb_font_create (face);
/* ".notdef" truncated to capacity 4 -> ".no\0". */
ret = hb_font_get_glyph_name (font, 0, buf, 4);
g_assert_true (ret);
g_assert_cmpint (buf[0], ==, '.');
g_assert_cmpint (buf[1], ==, 'n');
g_assert_cmpint (buf[2], ==, 'o');
g_assert_cmpint (buf[3], ==, 0);
hb_font_destroy (font);
hb_face_destroy (face);
}
static void
test_glyph_names_exact_fit_buffer (void)
{
/* Non-regression sanity: a buffer exactly sized to fit the full name
* (including the trailing NUL) must hold the full name. For ".notdef"
* that is 8 bytes. */
hb_face_t *face;
hb_font_t *font;
hb_bool_t ret;
char buf[8] = { 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f };
face = hb_test_open_font_file ("fonts/adwaita.ttf");
font = hb_font_create (face);
ret = hb_font_get_glyph_name (font, 0, buf, 8);
g_assert_true (ret);
g_assert_cmpstr (buf, ==, ".notdef");
g_assert_cmpint (buf[7], ==, 0);
hb_font_destroy (font);
hb_face_destroy (face);
}
static void
test_glyph_names_nonzero_sizes_unchanged (void)
{
/* Non-regression sanity: the existing behaviour at typical sizes must
* be preserved by the fix. */
hb_face_t *face;
hb_font_t *font;
hb_bool_t ret;
char name[64];
face = hb_test_open_font_file ("fonts/adwaita.ttf");
font = hb_font_create (face);
ret = hb_font_get_glyph_name (font, 0, name, 64);
g_assert_true (ret);
g_assert_cmpstr (name, ==, ".notdef");
ret = hb_font_get_glyph_name (font, 1, name, 64);
g_assert_true (ret);
g_assert_cmpstr (name, ==, ".space");
/* Beyond last glyph still returns false. */
ret = hb_font_get_glyph_name (font, 100, name, 64);
g_assert_false (ret);
hb_font_destroy (font);
hb_face_destroy (face);
}
static void
test_glyph_names_zero_size_sub_font (void)
{
/* Sub-fonts delegate to the parent's glyph-name funcs. Preserve the
* return-value-only probe behavior through the parent chain as well. */
hb_face_t *face;
hb_font_t *parent;
hb_font_t *font;
hb_bool_t ret;
char guard[4] = { 0x7f, 0x7f, 0x7f, 0x7f };
unsigned int i;
face = hb_test_open_font_file ("fonts/adwaita.ttf");
parent = hb_font_create (face);
font = hb_font_create_sub_font (parent);
ret = hb_font_get_glyph_name (font, 1, guard, 0);
g_assert_true (ret);
for (i = 0; i < 4; i++)
g_assert_cmpint (guard[i], ==, 0x7f);
hb_font_destroy (font);
hb_font_destroy (parent);
hb_face_destroy (face);
}
int
main (int argc, char **argv)
{
hb_test_init (&argc, &argv);
hb_test_add (test_glyph_names_post);
hb_test_add (test_glyph_names_cff);
hb_test_add (test_glyph_names_zero_size);
hb_test_add (test_glyph_names_zero_size_post);
hb_test_add (test_glyph_names_zero_size_cff);
hb_test_add (test_glyph_names_zero_size_unnamed_glyph);
hb_test_add (test_glyph_names_zero_size_null_buffer);
hb_test_add (test_glyph_names_size_one);
hb_test_add (test_glyph_names_small_buffer_truncation);
hb_test_add (test_glyph_names_exact_fit_buffer);
hb_test_add (test_glyph_names_nonzero_sizes_unchanged);
hb_test_add (test_glyph_names_zero_size_sub_font);
return hb_test_run();
}