Merge pull request #4237 from harfbuzz/layout-font-extents

[layout] Add hb_ot_layout_get_font_extents()
pull/4240/head
Behdad Esfahbod 2 years ago committed by GitHub
commit 592a3d0e96
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      docs/harfbuzz-sections.txt
  2. 21
      src/hb-ot-layout-base-table.hh
  3. 177
      src/hb-ot-layout.cc
  4. 30
      src/hb-ot-layout.h
  5. 2
      src/hb-ot-tag.cc
  6. 1
      test/api/Makefile.am
  7. BIN
      test/api/fonts/base-minmax.ttf
  8. 1
      test/api/meson.build
  9. 66
      test/api/test-base-minmax.c
  10. 12
      test/api/test-baseline.c

@ -626,9 +626,13 @@ hb_ot_layout_feature_get_lookups
hb_ot_layout_feature_get_name_ids
hb_ot_layout_feature_with_variations_get_lookups
hb_ot_layout_get_attach_points
hb_ot_layout_get_font_extents
hb_ot_layout_get_font_extents2
hb_ot_layout_get_horizontal_baseline_tag_for_script
hb_ot_layout_get_baseline
hb_ot_layout_get_baseline2
hb_ot_layout_get_baseline_with_fallback
hb_ot_layout_get_baseline_with_fallback2
hb_ot_layout_get_glyph_class
hb_ot_layout_get_glyphs_in_class
hb_ot_layout_get_ligature_carets

@ -170,8 +170,8 @@ struct FeatMinMaxRecord
{
TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this) &&
minCoord.sanitize (c, this) &&
maxCoord.sanitize (c, this)));
minCoord.sanitize (c, base) &&
maxCoord.sanitize (c, base)));
}
protected:
@ -187,7 +187,6 @@ struct FeatMinMaxRecord
* of MinMax table (may be NULL) */
public:
DEFINE_SIZE_STATIC (8);
};
struct MinMax
@ -274,7 +273,7 @@ struct BaseLangSysRecord
{
TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this) &&
minMax.sanitize (c, this)));
minMax.sanitize (c, base)));
}
protected:
@ -297,7 +296,8 @@ struct BaseScript
const BaseCoord &get_base_coord (int baseline_tag_index) const
{ return (this+baseValues).get_base_coord (baseline_tag_index); }
bool has_data () const { return baseValues; }
bool has_values () const { return baseValues; }
bool has_min_max () const { return defaultMinMax; /* TODO What if only per-language is present? */ }
bool sanitize (hb_sanitize_context_t *c) const
{
@ -383,7 +383,7 @@ struct Axis
const BaseCoord **coord) const
{
const BaseScript &base_script = (this+baseScriptList).get_base_script (script_tag);
if (!base_script.has_data ())
if (!base_script.has_values ())
{
*coord = nullptr;
return false;
@ -410,7 +410,7 @@ struct Axis
const BaseCoord **max_coord) const
{
const BaseScript &base_script = (this+baseScriptList).get_base_script (script_tag);
if (!base_script.has_data ())
if (!base_script.has_min_max ())
{
*min_coord = *max_coord = nullptr;
return false;
@ -425,8 +425,8 @@ struct Axis
{
TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this) &&
(this+baseTagList).sanitize (c) &&
(this+baseScriptList).sanitize (c)));
baseTagList.sanitize (c, this) &&
baseScriptList.sanitize (c, this)));
}
protected:
@ -473,14 +473,13 @@ struct BASE
return true;
}
/* TODO: Expose this separately sometime? */
bool get_min_max (hb_font_t *font,
hb_direction_t direction,
hb_tag_t script_tag,
hb_tag_t language_tag,
hb_tag_t feature_tag,
hb_position_t *min,
hb_position_t *max)
hb_position_t *max) const
{
const BaseCoord *min_coord, *max_coord;
if (!get_axis (direction).get_min_max (script_tag, language_tag, feature_tag,

@ -2036,6 +2036,112 @@ hb_ot_layout_substitute_lookup (OT::hb_ot_apply_context_t *c,
}
#ifndef HB_NO_BASE
static void
choose_base_tags (hb_script_t script,
hb_language_t language,
hb_tag_t *script_tag,
hb_tag_t *language_tag)
{
hb_tag_t script_tags[HB_OT_MAX_TAGS_PER_SCRIPT];
unsigned script_count = ARRAY_LENGTH (script_tags);
hb_tag_t language_tags[HB_OT_MAX_TAGS_PER_LANGUAGE];
unsigned language_count = ARRAY_LENGTH (language_tags);
hb_ot_tags_from_script_and_language (script, language,
&script_count, script_tags,
&language_count, language_tags);
*script_tag = script_count ? script_tags[script_count - 1] : HB_OT_TAG_DEFAULT_SCRIPT;
*language_tag = language_count ? language_tags[language_count - 1] : HB_OT_TAG_DEFAULT_LANGUAGE;
}
/**
* hb_ot_layout_get_font_extents:
* @font: a font
* @direction: text direction.
* @script_tag: script tag.
* @language_tag: language tag.
* @extents: (out) (nullable): font extents if found.
*
* Fetches script/language-specific font extents. These values are
* looked up in the `BASE` table's `MinMax` records.
*
* If no such extents are found, the default extents for the font are
* fetched. As such, the return value of this function can for the
* most part be ignored. Note that the per-script/language extents
* do not have a line-gap value, and the line-gap is set to zero in
* that case.
*
* Return value: `true` if found script/language-specific font extents.
*
* XSince: REPLACEME
**/
hb_bool_t
hb_ot_layout_get_font_extents (hb_font_t *font,
hb_direction_t direction,
hb_tag_t script_tag,
hb_tag_t language_tag,
hb_font_extents_t *extents)
{
hb_position_t min, max;
if (font->face->table.BASE->get_min_max (font, direction, script_tag, language_tag, HB_TAG_NONE,
&min, &max))
{
if (extents)
{
extents->ascender = max;
extents->descender = min;
extents->line_gap = 0;
}
return true;
}
hb_font_get_extents_for_direction (font, direction, extents);
return false;
}
/**
* hb_ot_layout_get_font_extents2:
* @font: a font
* @direction: text direction.
* @script: script.
* @language: (nullable): language.
* @extents: (out) (nullable): font extents if found.
*
* Fetches script/language-specific font extents. These values are
* looked up in the `BASE` table's `MinMax` records.
*
* If no such extents are found, the default extents for the font are
* fetched. As such, the return value of this function can for the
* most part be ignored. Note that the per-script/language extents
* do not have a line-gap value, and the line-gap is set to zero in
* that case.
*
* This function is like hb_ot_layout_get_font_extents() but takes
* #hb_script_t and #hb_language_t instead of OpenType #hb_tag_t.
*
* Return value: `true` if found script/language-specific font extents.
*
* XSince: REPLACEME
**/
hb_bool_t
hb_ot_layout_get_font_extents2 (hb_font_t *font,
hb_direction_t direction,
hb_script_t script,
hb_language_t language,
hb_font_extents_t *extents)
{
hb_tag_t script_tag, language_tag;
choose_base_tags (script, language, &script_tag, &language_tag);
return hb_ot_layout_get_font_extents (font,
direction,
script_tag,
language_tag,
extents);
}
/**
* hb_ot_layout_get_horizontal_baseline_tag_for_script:
* @script: a script tag.
@ -2133,6 +2239,42 @@ hb_ot_layout_get_baseline (hb_font_t *font,
return font->face->table.BASE->get_baseline (font, baseline_tag, direction, script_tag, language_tag, coord);
}
/**
* hb_ot_layout_get_baseline2:
* @font: a font
* @baseline_tag: a baseline tag
* @direction: text direction.
* @script: script.
* @language: (nullable): language, currently unused.
* @coord: (out) (nullable): baseline value if found.
*
* Fetches a baseline value from the face.
*
* This function is like hb_ot_layout_get_baseline() but takes
* #hb_script_t and #hb_language_t instead of OpenType #hb_tag_t.
*
* Return value: `true` if found baseline value in the font.
*
* XSince: REPLACEME
**/
hb_bool_t
hb_ot_layout_get_baseline2 (hb_font_t *font,
hb_ot_layout_baseline_tag_t baseline_tag,
hb_direction_t direction,
hb_script_t script,
hb_language_t language,
hb_position_t *coord /* OUT. May be NULL. */)
{
hb_tag_t script_tag, language_tag;
choose_base_tags (script, language, &script_tag, &language_tag);
return hb_ot_layout_get_baseline (font,
baseline_tag,
direction,
script_tag,
language_tag,
coord);
}
/**
* hb_ot_layout_get_baseline_with_fallback:
* @font: a font
@ -2355,6 +2497,41 @@ hb_ot_layout_get_baseline_with_fallback (hb_font_t *font,
}
}
/**
* hb_ot_layout_get_baseline_with_fallback2:
* @font: a font
* @baseline_tag: a baseline tag
* @direction: text direction.
* @script: script.
* @language: (nullable): language, currently unused.
* @coord: (out): baseline value if found.
*
* Fetches a baseline value from the face, and synthesizes
* it if the font does not have it.
*
* This function is like hb_ot_layout_get_baseline_with_fallback() but takes
* #hb_script_t and #hb_language_t instead of OpenType #hb_tag_t.
*
* XSince: REPLACEME
**/
void
hb_ot_layout_get_baseline_with_fallback2 (hb_font_t *font,
hb_ot_layout_baseline_tag_t baseline_tag,
hb_direction_t direction,
hb_script_t script,
hb_language_t language,
hb_position_t *coord /* OUT */)
{
hb_tag_t script_tag, language_tag;
choose_base_tags (script, language, &script_tag, &language_tag);
hb_ot_layout_get_baseline_with_fallback (font,
baseline_tag,
direction,
script_tag,
language_tag,
coord);
}
#endif

@ -447,6 +447,20 @@ hb_ot_layout_feature_get_characters (hb_face_t *face,
* BASE
*/
HB_EXTERN hb_bool_t
hb_ot_layout_get_font_extents (hb_font_t *font,
hb_direction_t direction,
hb_tag_t script_tag,
hb_tag_t language_tag,
hb_font_extents_t *extents);
HB_EXTERN hb_bool_t
hb_ot_layout_get_font_extents2 (hb_font_t *font,
hb_direction_t direction,
hb_script_t script,
hb_language_t language,
hb_font_extents_t *extents);
/**
* hb_ot_layout_baseline_tag_t:
* @HB_OT_LAYOUT_BASELINE_TAG_ROMAN: The baseline used by alphabetic scripts such as Latin, Cyrillic and Greek.
@ -499,6 +513,14 @@ hb_ot_layout_get_baseline (hb_font_t *font,
hb_tag_t language_tag,
hb_position_t *coord /* OUT. May be NULL. */);
HB_EXTERN hb_bool_t
hb_ot_layout_get_baseline2 (hb_font_t *font,
hb_ot_layout_baseline_tag_t baseline_tag,
hb_direction_t direction,
hb_script_t script,
hb_language_t language,
hb_position_t *coord /* OUT. May be NULL. */);
HB_EXTERN void
hb_ot_layout_get_baseline_with_fallback (hb_font_t *font,
hb_ot_layout_baseline_tag_t baseline_tag,
@ -507,6 +529,14 @@ hb_ot_layout_get_baseline_with_fallback (hb_font_t *font,
hb_tag_t language_tag,
hb_position_t *coord /* OUT */);
HB_EXTERN void
hb_ot_layout_get_baseline_with_fallback2 (hb_font_t *font,
hb_ot_layout_baseline_tag_t baseline_tag,
hb_direction_t direction,
hb_script_t script,
hb_language_t language,
hb_position_t *coord /* OUT */);
HB_END_DECLS
#endif /* HB_OT_LAYOUT_H */

@ -412,7 +412,7 @@ parse_private_use_subtag (const char *private_use_subtag,
/**
* hb_ot_tags_from_script_and_language:
* @script: an #hb_script_t to convert.
* @language: an #hb_language_t to convert.
* @language: (nullable): an #hb_language_t to convert.
* @script_count: (inout) (optional): maximum number of script tags to retrieve (IN)
* and actual number of script tags retrieved (OUT)
* @script_tags: (out) (optional): array of size at least @script_count to store the

@ -33,6 +33,7 @@ noinst_PROGRAMS = $(TEST_PROGS)
TEST_PROGS = \
test-aat-layout \
test-baseline \
test-base-minmax \
test-be-glyph-advance \
test-be-num-glyphs \
test-blob \

Binary file not shown.

@ -6,6 +6,7 @@ endif
tests = [
'test-aat-layout.c',
'test-baseline.c',
'test-base-minmax.c',
'test-be-glyph-advance.c',
'test-be-num-glyphs.c',
'test-blob.c',

@ -0,0 +1,66 @@
/*
* Copyright © 2023 Behdad Esfahbod
*
* 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 <hb-ot.h>
/* Unit tests for hb-ot-layout.h font extents */
static void
test_ot_layout_font_extents (void)
{
hb_face_t *face = hb_test_open_font_file ("fonts/base-minmax.ttf");
hb_font_t *font = hb_font_create (face);
hb_font_extents_t extents;
g_assert (hb_ot_layout_get_font_extents2 (font, HB_DIRECTION_LTR,
HB_SCRIPT_LATIN, HB_LANGUAGE_INVALID,
&extents));
g_assert_cmpint (extents.ascender, ==, 2000);
g_assert (hb_ot_layout_get_font_extents2 (font, HB_DIRECTION_LTR,
HB_SCRIPT_LATIN, hb_language_from_string ("xx", -1),
&extents));
g_assert_cmpint (extents.ascender, ==, 2000);
g_assert (!hb_ot_layout_get_font_extents2 (font, HB_DIRECTION_LTR,
HB_SCRIPT_ARABIC, HB_LANGUAGE_INVALID,
&extents));
g_assert_cmpint (extents.ascender, ==, 3000);
hb_font_destroy (font);
hb_face_destroy (face);
}
int
main (int argc, char **argv)
{
hb_test_init (&argc, &argv);
hb_test_add (test_ot_layout_font_extents);
return hb_test_run();
}

@ -41,6 +41,12 @@ test_ot_layout_base (void)
&position));
g_assert_cmpint (46, ==, position);
g_assert (hb_ot_layout_get_baseline2 (font, HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_BOTTOM_OR_LEFT, HB_DIRECTION_TTB,
HB_SCRIPT_HAN,
hb_language_from_string ("en", -1),
&position));
g_assert_cmpint (46, ==, position);
g_assert (!hb_ot_layout_get_baseline (font, HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_TOP_OR_RIGHT, HB_DIRECTION_TTB,
HB_TAG ('h','a','n','i'),
HB_TAG ('E','N','G',' '),
@ -63,6 +69,12 @@ test_ot_layout_base_with_fallback (void)
&position);
g_assert_cmpint (46, ==, position);
hb_ot_layout_get_baseline_with_fallback2 (font, HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_BOTTOM_OR_LEFT, HB_DIRECTION_TTB,
HB_SCRIPT_HAN,
hb_language_from_string ("en", -1),
&position);
g_assert_cmpint (46, ==, position);
hb_ot_layout_get_baseline_with_fallback (font, HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_TOP_OR_RIGHT, HB_DIRECTION_TTB,
HB_TAG ('h','a','n','i'),
HB_TAG ('E','N','G',' '),

Loading…
Cancel
Save