[hmtx] Implement [boring-expansion] >64k expansion

This implements https://github.com/be-fonts/boring-expansion-spec/issues/7
This commit is contained in:
Behdad Esfahbod 2022-02-15 14:15:12 -06:00
parent 379e526aa4
commit 8b7ccc41c4
3 changed files with 59 additions and 28 deletions

View File

@ -47,6 +47,7 @@
/* OpenType fundamentals. */
HB_OT_TABLE (OT, head)
HB_OT_TABLE (OT, maxp)
#if !defined(HB_NO_FACE_COLLECT_UNICODES) || !defined(HB_NO_OT_FONT)
HB_OT_ACCELERATOR (OT, cmap)
#endif

View File

@ -28,6 +28,7 @@
#define HB_OT_HMTX_TABLE_HH
#include "hb-open-type.hh"
#include "hb-ot-maxp-table.hh"
#include "hb-ot-hhea-table.hh"
#include "hb-ot-var-hvar-table.hh"
#include "hb-ot-metrics.hh"
@ -172,8 +173,17 @@ struct hmtxvmtx
accelerator_t (hb_face_t *face,
unsigned int default_advance_ = 0)
{
table = hb_sanitize_context_t ().reference_table<hmtxvmtx> (face, T::tableTag);
var_table = hb_sanitize_context_t ().reference_table<HVARVVAR> (face, T::variationsTag);
default_advance = default_advance_ ? default_advance_ : hb_face_get_upem (face);
/* Populate count variables and sort them out as we go */
unsigned int len = table.get_length ();
if (len & 1)
len--;
num_long_metrics = T::is_horizontal ?
face->table.hhea->numberOfLongMetrics :
#ifndef HB_NO_VERTICAL
@ -182,25 +192,27 @@ struct hmtxvmtx
0
#endif
;
table = hb_sanitize_context_t ().reference_table<hmtxvmtx> (face, T::tableTag);
/* Cap num_bearings and num_long_metrics based on table length. */
unsigned int len = table.get_length ();
if (unlikely (num_long_metrics * 4 > len))
num_long_metrics = len / 4;
num_bearings = num_long_metrics + (len - 4 * num_long_metrics) / 2;
len -= num_long_metrics * 4;
num_bearings = face->table.maxp->get_num_glyphs ();
if (unlikely (num_bearings < num_long_metrics))
num_bearings = num_long_metrics;
if (unlikely ((num_bearings - num_long_metrics) * 2 > len))
num_bearings = num_long_metrics + len / 2;
len -= (num_bearings - num_long_metrics) * 2;
/* We MUST set num_bearings to zero if num_long_metrics is zero.
* Our get_advance() depends on that. */
if (unlikely (!num_long_metrics))
{
num_bearings = num_long_metrics = 0;
table.destroy ();
table = hb_blob_get_empty ();
}
var_table = hb_sanitize_context_t ().reference_table<HVARVVAR> (face, T::variationsTag);
num_advances = num_bearings + len / 2;
num_glyphs = face->get_num_glyphs ();
if (num_glyphs < num_advances)
num_glyphs = num_advances;
}
~accelerator_t ()
{
@ -239,18 +251,31 @@ struct hmtxvmtx
unsigned int get_advance (hb_codepoint_t glyph) const
{
if (unlikely (glyph >= num_bearings))
{
/* If num_bearings is zero, it means we don't have the metrics table
* for this direction: return default advance. Otherwise, it means that the
* glyph index is out of bound: return zero. */
if (num_bearings)
return 0;
else
return default_advance;
}
/* OpenType case. */
if (glyph < num_bearings)
return table->longMetricZ[hb_min (glyph, (uint32_t) num_long_metrics - 1)].advance;
return table->longMetricZ[hb_min (glyph, (uint32_t) num_long_metrics - 1)].advance;
/* If num_advances is zero, it means we don't have the metrics table
* for this direction: return default advance. Otherwise, there's a
* well-defined answer. */
if (unlikely (!num_advances))
return default_advance;
if (unlikely (glyph >= num_glyphs))
return 0;
/* num_bearings <= glyph < num_glyphs;
* num_bearings <= num_advances */
/* TODO Optimize */
if (num_bearings == num_advances)
return get_advance (num_bearings - 1);
const FWORD *bearings = (const FWORD *) &table->longMetricZ[num_long_metrics];
const UFWORD *advances = (const UFWORD *) &bearings[num_bearings - num_long_metrics];
return advances[hb_min (glyph - num_bearings, num_advances - num_bearings - 1)];
}
unsigned int get_advance (hb_codepoint_t glyph,
@ -272,9 +297,12 @@ struct hmtxvmtx
}
protected:
// 0 <= num_long_metrics <= num_bearings
unsigned int num_long_metrics;
unsigned int num_bearings;
// 0 <= num_long_metrics <= num_bearings <= num_advances <= num_glyphs
unsigned num_long_metrics;
unsigned num_bearings;
unsigned num_advances;
unsigned num_glyphs;
unsigned int default_advance;
private:
@ -306,6 +334,8 @@ struct hmtxvmtx
* the end. This allows a monospaced
* font to vary the side bearing
* values for each glyph. */
/*UnsizedArrayOf<UFWORD>advancesX;*/
/* TODO Document. */
public:
DEFINE_SIZE_ARRAY (0, longMetricZ);
};

View File

@ -77,9 +77,9 @@ test_maxp_and_hmtx (void)
g_assert_cmpuint (hb_font_get_glyph_h_advance (font, 2), ==, 3);
g_assert_cmpuint (hb_font_get_glyph_h_advance (font, 3), ==, 3);
g_assert_cmpuint (hb_font_get_glyph_h_advance (font, 4), ==, 3);
g_assert_cmpuint (hb_font_get_glyph_h_advance (font, 5), ==, 3);
g_assert_cmpuint (hb_font_get_glyph_h_advance (font, 6), ==, 3);
g_assert_cmpuint (hb_font_get_glyph_h_advance (font, 7), ==, 0);
g_assert_cmpuint (hb_font_get_glyph_h_advance (font, 5), ==, 8);
g_assert_cmpuint (hb_font_get_glyph_h_advance (font, 6), ==, 9);
g_assert_cmpuint (hb_font_get_glyph_h_advance (font, 7), ==, 9);
g_assert_cmpuint (hb_font_get_glyph_h_advance (font, 8), ==, 0);
g_assert_cmpuint (hb_font_get_glyph_h_advance (font, 9), ==, 0);
g_assert_cmpuint (hb_font_get_glyph_h_advance (font,10), ==, 0);