[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. */ /* OpenType fundamentals. */
HB_OT_TABLE (OT, head) HB_OT_TABLE (OT, head)
HB_OT_TABLE (OT, maxp)
#if !defined(HB_NO_FACE_COLLECT_UNICODES) || !defined(HB_NO_OT_FONT) #if !defined(HB_NO_FACE_COLLECT_UNICODES) || !defined(HB_NO_OT_FONT)
HB_OT_ACCELERATOR (OT, cmap) HB_OT_ACCELERATOR (OT, cmap)
#endif #endif

View File

@ -28,6 +28,7 @@
#define HB_OT_HMTX_TABLE_HH #define HB_OT_HMTX_TABLE_HH
#include "hb-open-type.hh" #include "hb-open-type.hh"
#include "hb-ot-maxp-table.hh"
#include "hb-ot-hhea-table.hh" #include "hb-ot-hhea-table.hh"
#include "hb-ot-var-hvar-table.hh" #include "hb-ot-var-hvar-table.hh"
#include "hb-ot-metrics.hh" #include "hb-ot-metrics.hh"
@ -172,8 +173,17 @@ struct hmtxvmtx
accelerator_t (hb_face_t *face, accelerator_t (hb_face_t *face,
unsigned int default_advance_ = 0) 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); 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 ? num_long_metrics = T::is_horizontal ?
face->table.hhea->numberOfLongMetrics : face->table.hhea->numberOfLongMetrics :
#ifndef HB_NO_VERTICAL #ifndef HB_NO_VERTICAL
@ -182,25 +192,27 @@ struct hmtxvmtx
0 0
#endif #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)) if (unlikely (num_long_metrics * 4 > len))
num_long_metrics = len / 4; 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. /* We MUST set num_bearings to zero if num_long_metrics is zero.
* Our get_advance() depends on that. */ * Our get_advance() depends on that. */
if (unlikely (!num_long_metrics)) if (unlikely (!num_long_metrics))
{
num_bearings = num_long_metrics = 0; 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 () ~accelerator_t ()
{ {
@ -239,18 +251,31 @@ struct hmtxvmtx
unsigned int get_advance (hb_codepoint_t glyph) const unsigned int get_advance (hb_codepoint_t glyph) const
{ {
if (unlikely (glyph >= num_bearings)) /* OpenType case. */
{ if (glyph < num_bearings)
/* If num_bearings is zero, it means we don't have the metrics table return table->longMetricZ[hb_min (glyph, (uint32_t) num_long_metrics - 1)].advance;
* 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;
}
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, unsigned int get_advance (hb_codepoint_t glyph,
@ -272,9 +297,12 @@ struct hmtxvmtx
} }
protected: protected:
// 0 <= num_long_metrics <= num_bearings // 0 <= num_long_metrics <= num_bearings <= num_advances <= num_glyphs
unsigned int num_long_metrics; unsigned num_long_metrics;
unsigned int num_bearings; unsigned num_bearings;
unsigned num_advances;
unsigned num_glyphs;
unsigned int default_advance; unsigned int default_advance;
private: private:
@ -306,6 +334,8 @@ struct hmtxvmtx
* the end. This allows a monospaced * the end. This allows a monospaced
* font to vary the side bearing * font to vary the side bearing
* values for each glyph. */ * values for each glyph. */
/*UnsizedArrayOf<UFWORD>advancesX;*/
/* TODO Document. */
public: public:
DEFINE_SIZE_ARRAY (0, longMetricZ); 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, 2), ==, 3);
g_assert_cmpuint (hb_font_get_glyph_h_advance (font, 3), ==, 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, 4), ==, 3);
g_assert_cmpuint (hb_font_get_glyph_h_advance (font, 5), ==, 3); g_assert_cmpuint (hb_font_get_glyph_h_advance (font, 5), ==, 8);
g_assert_cmpuint (hb_font_get_glyph_h_advance (font, 6), ==, 3); g_assert_cmpuint (hb_font_get_glyph_h_advance (font, 6), ==, 9);
g_assert_cmpuint (hb_font_get_glyph_h_advance (font, 7), ==, 0); 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, 8), ==, 0);
g_assert_cmpuint (hb_font_get_glyph_h_advance (font, 9), ==, 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); g_assert_cmpuint (hb_font_get_glyph_h_advance (font,10), ==, 0);