425 lines
12 KiB
C++
425 lines
12 KiB
C++
/*
|
|
* Copyright © 2017 Google, 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.
|
|
*
|
|
* Google Author(s): Behdad Esfahbod
|
|
*/
|
|
|
|
#ifndef HB_OT_KERN_TABLE_HH
|
|
#define HB_OT_KERN_TABLE_HH
|
|
|
|
#include "hb-aat-layout-kerx-table.hh"
|
|
|
|
|
|
/*
|
|
* kern -- Kerning
|
|
* https://docs.microsoft.com/en-us/typography/opentype/spec/kern
|
|
* https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6kern.html
|
|
*/
|
|
#define HB_OT_TAG_kern HB_TAG('k','e','r','n')
|
|
|
|
|
|
namespace OT {
|
|
|
|
|
|
template <typename KernSubTableHeader>
|
|
struct KernSubTableFormat3
|
|
{
|
|
int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
|
|
{
|
|
hb_array_t<const FWORD> kernValue = kernValueZ.as_array (kernValueCount);
|
|
hb_array_t<const HBUINT8> leftClass = StructAfter<const UnsizedArrayOf<HBUINT8>> (kernValue).as_array (glyphCount);
|
|
hb_array_t<const HBUINT8> rightClass = StructAfter<const UnsizedArrayOf<HBUINT8>> (leftClass).as_array (glyphCount);
|
|
hb_array_t<const HBUINT8> kernIndex = StructAfter<const UnsizedArrayOf<HBUINT8>> (rightClass).as_array (leftClassCount * rightClassCount);
|
|
|
|
unsigned int leftC = leftClass[left];
|
|
unsigned int rightC = rightClass[right];
|
|
if (unlikely (leftC >= leftClassCount || rightC >= rightClassCount))
|
|
return 0;
|
|
unsigned int i = leftC * rightClassCount + rightC;
|
|
return kernValue[kernIndex[i]];
|
|
}
|
|
|
|
bool apply (AAT::hb_aat_apply_context_t *c) const
|
|
{
|
|
TRACE_APPLY (this);
|
|
|
|
if (!c->plan->requested_kerning)
|
|
return false;
|
|
|
|
if (header.coverage & header.Backwards)
|
|
return false;
|
|
|
|
hb_kern_machine_t<KernSubTableFormat3> machine (*this, header.coverage & header.CrossStream);
|
|
machine.kern (c->font, c->buffer, c->plan->kern_mask);
|
|
|
|
return_trace (true);
|
|
}
|
|
|
|
bool sanitize (hb_sanitize_context_t *c) const
|
|
{
|
|
TRACE_SANITIZE (this);
|
|
return_trace (c->check_struct (this) &&
|
|
hb_barrier () &&
|
|
c->check_range (kernValueZ,
|
|
kernValueCount * sizeof (FWORD) +
|
|
glyphCount * 2 +
|
|
leftClassCount * rightClassCount));
|
|
}
|
|
|
|
template <typename set_t>
|
|
void collect_glyphs (set_t &left_set, set_t &right_set, unsigned num_glyphs) const
|
|
{
|
|
set_t set;
|
|
if (likely (glyphCount))
|
|
set.add_range (0, glyphCount - 1);
|
|
left_set.union_ (set);
|
|
right_set.union_ (set);
|
|
}
|
|
|
|
protected:
|
|
KernSubTableHeader
|
|
header;
|
|
HBUINT16 glyphCount; /* The number of glyphs in this font. */
|
|
HBUINT8 kernValueCount; /* The number of kerning values. */
|
|
HBUINT8 leftClassCount; /* The number of left-hand classes. */
|
|
HBUINT8 rightClassCount;/* The number of right-hand classes. */
|
|
HBUINT8 flags; /* Set to zero (reserved for future use). */
|
|
UnsizedArrayOf<FWORD>
|
|
kernValueZ; /* The kerning values.
|
|
* Length kernValueCount. */
|
|
#if 0
|
|
UnsizedArrayOf<HBUINT8>
|
|
leftClass; /* The left-hand classes.
|
|
* Length glyphCount. */
|
|
UnsizedArrayOf<HBUINT8>
|
|
rightClass; /* The right-hand classes.
|
|
* Length glyphCount. */
|
|
UnsizedArrayOf<HBUINT8>kernIndex;
|
|
/* The indices into the kernValue array.
|
|
* Length leftClassCount * rightClassCount */
|
|
#endif
|
|
public:
|
|
DEFINE_SIZE_ARRAY (KernSubTableHeader::static_size + 6, kernValueZ);
|
|
};
|
|
|
|
template <typename KernSubTableHeader>
|
|
struct KernSubTable
|
|
{
|
|
unsigned int get_size () const { return u.header.length; }
|
|
unsigned int get_type () const { return u.header.format; }
|
|
|
|
int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
|
|
{
|
|
switch (get_type ()) {
|
|
/* This method hooks up to hb_font_t's get_h_kerning. Only support Format0. */
|
|
case 0: return u.format0.get_kerning (left, right);
|
|
default:return 0;
|
|
}
|
|
}
|
|
|
|
template <typename context_t, typename ...Ts>
|
|
typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
|
|
{
|
|
unsigned int subtable_type = get_type ();
|
|
TRACE_DISPATCH (this, subtable_type);
|
|
switch (subtable_type) {
|
|
case 0: return_trace (c->dispatch (u.format0));
|
|
#ifndef HB_NO_AAT_SHAPE
|
|
case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
|
|
#endif
|
|
case 2: return_trace (c->dispatch (u.format2));
|
|
#ifndef HB_NO_AAT_SHAPE
|
|
case 3: return_trace (c->dispatch (u.format3, std::forward<Ts> (ds)...));
|
|
#endif
|
|
default: return_trace (c->default_return_value ());
|
|
}
|
|
}
|
|
|
|
template <typename set_t>
|
|
void collect_glyphs (set_t &left_set, set_t &right_set, unsigned num_glyphs) const
|
|
{
|
|
unsigned int subtable_type = get_type ();
|
|
switch (subtable_type) {
|
|
case 0: u.format0.collect_glyphs (left_set, right_set, num_glyphs); return;
|
|
case 1: u.format1.collect_glyphs (left_set, right_set, num_glyphs); return;
|
|
case 2: u.format2.collect_glyphs (left_set, right_set, num_glyphs); return;
|
|
case 3: u.format3.collect_glyphs (left_set, right_set, num_glyphs); return;
|
|
default: return;
|
|
}
|
|
}
|
|
|
|
bool sanitize (hb_sanitize_context_t *c) const
|
|
{
|
|
TRACE_SANITIZE (this);
|
|
if (unlikely (!(u.header.sanitize (c) &&
|
|
hb_barrier () &&
|
|
u.header.length >= u.header.min_size &&
|
|
c->check_range (this, u.header.length)))) return_trace (false);
|
|
|
|
return_trace (dispatch (c));
|
|
}
|
|
|
|
public:
|
|
union {
|
|
KernSubTableHeader header;
|
|
AAT::KerxSubTableFormat0<KernSubTableHeader> format0;
|
|
AAT::KerxSubTableFormat1<KernSubTableHeader> format1;
|
|
AAT::KerxSubTableFormat2<KernSubTableHeader> format2;
|
|
KernSubTableFormat3<KernSubTableHeader> format3;
|
|
} u;
|
|
public:
|
|
DEFINE_SIZE_MIN (KernSubTableHeader::static_size);
|
|
};
|
|
|
|
|
|
struct KernOTSubTableHeader
|
|
{
|
|
static constexpr bool apple = false;
|
|
typedef AAT::ObsoleteTypes Types;
|
|
|
|
unsigned tuple_count () const { return 0; }
|
|
bool is_horizontal () const { return (coverage & Horizontal); }
|
|
|
|
enum Coverage
|
|
{
|
|
Horizontal = 0x01u,
|
|
Minimum = 0x02u,
|
|
CrossStream = 0x04u,
|
|
Override = 0x08u,
|
|
|
|
/* Not supported: */
|
|
Backwards = 0x00u,
|
|
Variation = 0x00u,
|
|
};
|
|
|
|
bool sanitize (hb_sanitize_context_t *c) const
|
|
{
|
|
TRACE_SANITIZE (this);
|
|
return_trace (c->check_struct (this));
|
|
}
|
|
|
|
public:
|
|
HBUINT16 versionZ; /* Unused. */
|
|
HBUINT16 length; /* Length of the subtable (including this header). */
|
|
HBUINT8 format; /* Subtable format. */
|
|
HBUINT8 coverage; /* Coverage bits. */
|
|
public:
|
|
DEFINE_SIZE_STATIC (6);
|
|
};
|
|
|
|
struct KernOT : AAT::KerxTable<KernOT>
|
|
{
|
|
friend struct AAT::KerxTable<KernOT>;
|
|
|
|
static constexpr hb_tag_t tableTag = HB_OT_TAG_kern;
|
|
static constexpr unsigned minVersion = 0u;
|
|
|
|
typedef KernOTSubTableHeader SubTableHeader;
|
|
typedef SubTableHeader::Types Types;
|
|
typedef KernSubTable<SubTableHeader> SubTable;
|
|
|
|
protected:
|
|
HBUINT16 version; /* Version--0x0000u */
|
|
HBUINT16 tableCount; /* Number of subtables in the kerning table. */
|
|
SubTable firstSubTable; /* Subtables. */
|
|
public:
|
|
DEFINE_SIZE_MIN (4);
|
|
};
|
|
|
|
|
|
struct KernAATSubTableHeader
|
|
{
|
|
static constexpr bool apple = true;
|
|
typedef AAT::ObsoleteTypes Types;
|
|
|
|
unsigned tuple_count () const { return 0; }
|
|
bool is_horizontal () const { return !(coverage & Vertical); }
|
|
|
|
enum Coverage
|
|
{
|
|
Vertical = 0x80u,
|
|
CrossStream = 0x40u,
|
|
Variation = 0x20u,
|
|
|
|
/* Not supported: */
|
|
Backwards = 0x00u,
|
|
};
|
|
|
|
bool sanitize (hb_sanitize_context_t *c) const
|
|
{
|
|
TRACE_SANITIZE (this);
|
|
return_trace (c->check_struct (this));
|
|
}
|
|
|
|
public:
|
|
HBUINT32 length; /* Length of the subtable (including this header). */
|
|
HBUINT8 coverage; /* Coverage bits. */
|
|
HBUINT8 format; /* Subtable format. */
|
|
HBUINT16 tupleIndex; /* The tuple index (used for variations fonts).
|
|
* This value specifies which tuple this subtable covers.
|
|
* Note: We don't implement. */
|
|
public:
|
|
DEFINE_SIZE_STATIC (8);
|
|
};
|
|
|
|
struct KernAAT : AAT::KerxTable<KernAAT>
|
|
{
|
|
friend struct AAT::KerxTable<KernAAT>;
|
|
|
|
static constexpr hb_tag_t tableTag = HB_OT_TAG_kern;
|
|
static constexpr unsigned minVersion = 0x00010000u;
|
|
|
|
typedef KernAATSubTableHeader SubTableHeader;
|
|
typedef SubTableHeader::Types Types;
|
|
typedef KernSubTable<SubTableHeader> SubTable;
|
|
|
|
protected:
|
|
HBUINT32 version; /* Version--0x00010000u */
|
|
HBUINT32 tableCount; /* Number of subtables in the kerning table. */
|
|
SubTable firstSubTable; /* Subtables. */
|
|
public:
|
|
DEFINE_SIZE_MIN (8);
|
|
};
|
|
|
|
struct kern
|
|
{
|
|
static constexpr hb_tag_t tableTag = HB_OT_TAG_kern;
|
|
|
|
bool has_data () const { return u.version32; }
|
|
unsigned get_type () const { return u.major; }
|
|
|
|
bool has_state_machine () const
|
|
{
|
|
switch (get_type ()) {
|
|
case 0: return u.ot.has_state_machine ();
|
|
#ifndef HB_NO_AAT_SHAPE
|
|
case 1: return u.aat.has_state_machine ();
|
|
#endif
|
|
default:return false;
|
|
}
|
|
}
|
|
|
|
bool has_cross_stream () const
|
|
{
|
|
switch (get_type ()) {
|
|
case 0: return u.ot.has_cross_stream ();
|
|
#ifndef HB_NO_AAT_SHAPE
|
|
case 1: return u.aat.has_cross_stream ();
|
|
#endif
|
|
default:return false;
|
|
}
|
|
}
|
|
|
|
int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right) const
|
|
{
|
|
switch (get_type ()) {
|
|
case 0: return u.ot.get_h_kerning (left, right);
|
|
#ifndef HB_NO_AAT_SHAPE
|
|
case 1: return u.aat.get_h_kerning (left, right);
|
|
#endif
|
|
default:return 0;
|
|
}
|
|
}
|
|
|
|
bool apply (AAT::hb_aat_apply_context_t *c,
|
|
const AAT::kern_accelerator_data_t *accel_data = nullptr) const
|
|
{ return dispatch (c, accel_data); }
|
|
|
|
template <typename context_t, typename ...Ts>
|
|
typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
|
|
{
|
|
unsigned int subtable_type = get_type ();
|
|
TRACE_DISPATCH (this, subtable_type);
|
|
switch (subtable_type) {
|
|
case 0: return_trace (c->dispatch (u.ot, std::forward<Ts> (ds)...));
|
|
#ifndef HB_NO_AAT_SHAPE
|
|
case 1: return_trace (c->dispatch (u.aat, std::forward<Ts> (ds)...));
|
|
#endif
|
|
default: return_trace (c->default_return_value ());
|
|
}
|
|
}
|
|
|
|
bool sanitize (hb_sanitize_context_t *c) const
|
|
{
|
|
TRACE_SANITIZE (this);
|
|
if (!u.version32.sanitize (c)) return_trace (false);
|
|
hb_barrier ();
|
|
return_trace (dispatch (c));
|
|
}
|
|
|
|
AAT::kern_accelerator_data_t create_accelerator_data (unsigned num_glyphs) const
|
|
{
|
|
switch (get_type ()) {
|
|
case 0: return u.ot.create_accelerator_data (num_glyphs);
|
|
#ifndef HB_NO_AAT_SHAPE
|
|
case 1: return u.aat.create_accelerator_data (num_glyphs);
|
|
#endif
|
|
default:return AAT::kern_accelerator_data_t ();
|
|
}
|
|
}
|
|
|
|
struct accelerator_t
|
|
{
|
|
accelerator_t (hb_face_t *face)
|
|
{
|
|
hb_sanitize_context_t sc;
|
|
this->table = sc.reference_table<kern> (face);
|
|
this->accel_data = this->table->create_accelerator_data (face->get_num_glyphs ());
|
|
}
|
|
~accelerator_t ()
|
|
{
|
|
this->table.destroy ();
|
|
}
|
|
|
|
hb_blob_t *get_blob () const { return table.get_blob (); }
|
|
|
|
bool apply (AAT::hb_aat_apply_context_t *c) const
|
|
{
|
|
return table->apply (c, &accel_data);
|
|
}
|
|
|
|
hb_blob_ptr_t<kern> table;
|
|
AAT::kern_accelerator_data_t accel_data;
|
|
};
|
|
|
|
protected:
|
|
union {
|
|
HBUINT32 version32;
|
|
HBUINT16 major;
|
|
KernOT ot;
|
|
#ifndef HB_NO_AAT_SHAPE
|
|
KernAAT aat;
|
|
#endif
|
|
} u;
|
|
public:
|
|
DEFINE_SIZE_UNION (4, version32);
|
|
};
|
|
|
|
struct kern_accelerator_t : kern::accelerator_t {
|
|
kern_accelerator_t (hb_face_t *face) : kern::accelerator_t (face) {}
|
|
};
|
|
|
|
} /* namespace OT */
|
|
|
|
|
|
#endif /* HB_OT_KERN_TABLE_HH */
|