Added CFF support

Added sources hb-ot-cff-table.hh, hb-subset-cff.cc & hh
Templatized Index because CFF uses 16-bit count while CFF2 uses 32-bit
Misc code cleanup & bug fixes
pull/1113/head
Michiharu Ariza 6 years ago
parent 161ece4c30
commit 64c5412264
  1. 3
      src/Makefile.sources
  2. 465
      src/hb-ot-cff-common-private.hh
  3. 919
      src/hb-ot-cff-table.hh
  4. 231
      src/hb-ot-cff2-table.hh
  5. 1
      src/hb-ot-font.cc
  6. 6
      src/hb-subset-cff-common-private.cc
  7. 4
      src/hb-subset-cff-common-private.hh
  8. 560
      src/hb-subset-cff.cc
  9. 38
      src/hb-subset-cff.hh
  10. 100
      src/hb-subset-cff2.cc
  11. 4
      src/hb-subset.cc

@ -26,6 +26,7 @@ HB_BASE_sources = \
hb-ot-color-cbdt-table.hh \
hb-ot-cmap-table.hh \
hb-ot-glyf-table.hh \
hb-ot-cff-table.hh \
hb-ot-cff2-table.hh \
hb-ot-hdmx-table.hh \
hb-ot-head-table.hh \
@ -210,6 +211,7 @@ HB_SUBSET_sources = \
hb-static.cc \
hb-subset.cc \
hb-subset-glyf.cc \
hb-subset-cff.cc \
hb-subset-cff2.cc \
hb-subset-cff-common-private.cc \
hb-subset-input.cc \
@ -219,6 +221,7 @@ HB_SUBSET_sources = \
HB_SUBSET_headers = \
hb-subset.h \
hb-subset-glyf.hh \
hb-subset-cff.hh \
hb-subset-cff2.hh \
hb-subset-cff-common-private.hh \
hb-subset-plan.hh \

@ -34,6 +34,11 @@ namespace CFF {
using namespace OT;
/* utility macro */
template<typename Type>
static inline const Type& StructAtOffsetOrNull(const void *P, unsigned int offset)
{ return offset? (* reinterpret_cast<const Type*> ((const char *) P + offset)): Null(Type); }
const float UNSET_REAL_VALUE = -1.0f;
enum OpCode {
@ -153,12 +158,6 @@ protected:
} u;
};
/* pair of table offset and length */
struct offset_size_pair {
unsigned int offset;
unsigned int size;
};
/* byte string */
struct UnsizedByteStr : UnsizedArrayOf <HBUINT8>
{
@ -209,6 +208,18 @@ struct ByteStr
return (*str)[i];
}
inline bool serialize (hb_serialize_context_t *c, const ByteStr &src)
{
TRACE_SANITIZE (this);
HBUINT8 *dest = c->allocate_size<HBUINT8> (src.len);
if (unlikely (dest == nullptr))
return_trace (false);
memcpy (dest, src.str, src.len);
return_trace (true);
}
inline unsigned int get_size (void) const { return len; }
inline bool check_limit (unsigned int offset, unsigned int count) const
{ return (offset + count <= len); }
@ -229,6 +240,7 @@ inline unsigned int calcOffSize(unsigned int offset)
}
/* CFF INDEX */
template <typename COUNT>
struct Index
{
inline bool sanitize (hb_sanitize_context_t *c) const
@ -259,35 +271,34 @@ struct Index
}
inline bool serialize (hb_serialize_context_t *c,
unsigned int offSize,
const hb_vector_t<ByteStr> &bytesArray)
unsigned int offSize_,
const hb_vector_t<ByteStr> &byteArray)
{
TRACE_SERIALIZE (this);
/* serialize Index header */
if (unlikely (!c->extend_min (*this))) return_trace (false);
this->count.set (bytesArray.len);
this->offSize.set (offSize);
if (!unlikely (c->allocate_size<HBUINT8> (offSize * (bytesArray.len + 1))))
this->count.set (byteArray.len);
this->offSize.set (offSize_);
if (!unlikely (c->allocate_size<HBUINT8> (offSize_ * (byteArray.len + 1))))
return_trace (false);
/* serialize indices */
unsigned int offset = 1;
unsigned int i = 0;
for (; i < bytesArray.len; i++)
for (; i < byteArray.len; i++)
{
set_offset_at (i, offset);
offset += bytesArray[i].len;
offset += byteArray[i].get_size ();
}
set_offset_at (i, offset);
/* serialize data */
for (unsigned int i = 0; i < bytesArray.len; i++)
for (unsigned int i = 0; i < byteArray.len; i++)
{
HBUINT8 *dest = c->allocate_size<HBUINT8> (bytesArray[i].len);
if (dest == nullptr)
ByteStr *dest = c->start_embed<ByteStr> ();
if (unlikely (dest == nullptr ||
!dest->serialize (c, byteArray[i])))
return_trace (false);
memcpy (dest, &bytesArray[i].str[0], bytesArray[i].len);
}
return_trace (true);
}
@ -319,12 +330,12 @@ struct Index
{ return offset_at (index + 1) - offset_at (index); }
inline const char *data_base (void) const
{ return (const char *)this + 5 + offset_array_size (); }
{ return (const char *)this + min_size + offset_array_size (); }
inline unsigned int data_size (void) const
{ return HBINT8::static_size; };
inline ByteStr operator [] (unsigned int index) const
ByteStr operator [] (unsigned int index) const
{
if (likely (index < count))
return ByteStr (data_base () + offset_at (index) - 1, offset_at (index + 1) - offset_at (index));
@ -358,23 +369,203 @@ struct Index
}
public:
HBUINT32 count; /* Number of object data. Note there are (count+1) offsets */
COUNT count; /* Number of object data. Note there are (count+1) offsets */
HBUINT8 offSize; /* The byte size of each offset in the offsets array. */
HBUINT8 offsets[VAR]; /* The array of (count + 1) offsets into objects array (1-base). */
/* HBUINT8 data[VAR]; Object data */
public:
DEFINE_SIZE_ARRAY (5, offsets);
DEFINE_SIZE_ARRAY (COUNT::static_size + HBUINT8::static_size, offsets);
};
template <typename Type>
struct IndexOf : Index
template <typename COUNT, typename TYPE>
struct IndexOf : Index<COUNT>
{
inline ByteStr operator [] (unsigned int index) const
inline const ByteStr operator [] (unsigned int index) const
{
if (likely (index < count))
return ByteStr (data_base () + offset_at (index) - 1, length_at (index));
if (likely (index < Index<COUNT>::count))
return ByteStr (Index<COUNT>::data_base () + Index<COUNT>::offset_at (index) - 1, Index<COUNT>::length_at (index));
return Null(ByteStr);
}
template <typename DATA, typename PARAM1, typename PARAM2>
inline bool serialize (hb_serialize_context_t *c,
unsigned int offSize_,
const hb_vector_t<DATA> &dataArray,
const hb_vector_t<unsigned int> &dataSizeArray,
const PARAM1 &param1,
const PARAM2 &param2)
{
TRACE_SERIALIZE (this);
/* serialize Index header */
if (unlikely (!c->extend_min (*this))) return_trace (false);
this->count.set (dataArray.len);
this->offSize.set (offSize_);
if (!unlikely (c->allocate_size<HBUINT8> (offSize_ * (dataArray.len + 1))))
return_trace (false);
/* serialize indices */
unsigned int offset = 1;
unsigned int i = 0;
for (; i < dataArray.len; i++)
{
Index<COUNT>::set_offset_at (i, offset);
offset += dataSizeArray[i];
}
Index<COUNT>::set_offset_at (i, offset);
/* serialize data */
for (unsigned int i = 0; i < dataArray.len; i++)
{
TYPE *dest = c->start_embed<TYPE> ();
if (unlikely (dest == nullptr ||
!dest->serialize (c, dataArray[i], param1, param2)))
return_trace (false);
}
return_trace (true);
}
/* in parallel to above */
template <typename DATA, typename PARAM>
inline static unsigned int calculate_serialized_size (unsigned int &offSize_ /* OUT */,
const hb_vector_t<DATA> &dataArray,
hb_vector_t<unsigned int> &dataSizeArray, /* OUT */
const PARAM &param)
{
/* determine offset size */
unsigned int totalDataSize = 0;
for (unsigned int i = 0; i < dataArray.len; i++)
{
unsigned int dataSize = TYPE::calculate_serialized_size (dataArray[i], param);
dataSizeArray[i] = dataSize;
totalDataSize += dataSize;
}
offSize_ = calcOffSize (totalDataSize);
return Index<COUNT>::calculate_serialized_size (offSize_, dataArray.len, totalDataSize);
}
};
inline float parse_bcd (const ByteStr& str, unsigned int& offset, float& v)
{
// XXX: TODO
v = 0;
for (;;) {
if (++offset >= str.len)
return false;
unsigned char byte = str[offset];
if (((byte & 0xF0) == 0xF0) || ((byte & 0x0F) == 0x0F))
break;
}
return true;
}
/* operand stack */
struct Stack
{
inline void init (void) { size = 0; }
inline void fini (void) { }
inline void push (const Number &v)
{
if (likely (size < kSizeLimit))
numbers[size++] = v;
}
inline void push_int (int v)
{
Number n;
n.set_int (v);
push (n);
}
inline void push_real (float v)
{
Number n;
n.set_real (v);
push (n);
}
inline const Number& pop (void)
{
if (likely (size > 0))
return numbers[--size];
else
return Null(Number);
}
inline bool check_push (void)
{
if (likely (size < kSizeLimit)) {
size++;
return true;
} else
return false;
}
inline bool check_pop (void)
{
if (likely (0 < size)) {
size--;
return true;
} else
return false;
}
inline bool check_pop_num (Number& n)
{
if (unlikely (!this->check_underflow (1)))
return false;
n = this->pop ();
return true;
}
inline bool check_pop_uint (unsigned int& v)
{
uint32_t i;
if (unlikely (!this->check_underflow (1)))
return false;
i = this->pop ().to_int ();
if (unlikely (i < 0))
return false;
v = (uint32_t)i;
return true;
}
inline bool check_pop_delta (hb_vector_t<Number>& vec, bool even=false)
{
if (even && unlikely ((this->size & 1) != 0))
return false;
float val = 0.0f;
for (unsigned int i = 0; i < size; i++) {
val += numbers[i].to_real ();
Number *n = vec.push ();
n->set_real (val);
}
return true;
}
inline bool push_longint_from_str (const ByteStr& str, unsigned int& offset)
{
if (unlikely (!str.check_limit (offset, 5) || !check_overflow (1)))
return false;
push_int ((int32_t)*(const HBUINT32*)&str[offset + 1]);
offset += 4;
return true;
}
inline void clear (void) { size = 0; }
inline bool check_overflow (unsigned int count) const { return (count <= kSizeLimit) && (count + size <= kSizeLimit); }
inline bool check_underflow (unsigned int count) const { return (count <= size); }
inline unsigned int get_size (void) const { return size; }
inline bool is_empty (void) const { return size == 0; }
static const unsigned int kSizeLimit = 513;
unsigned int size;
Number numbers[kSizeLimit];
};
/* an operator prefixed by its operands in a byte string */
@ -438,21 +629,6 @@ struct DictValues
hb_vector_t<VAL> values;
};
/* base of OP_SERIALIZER */
struct OpSerializer
{
protected:
inline bool copy_opstr (hb_serialize_context_t *c, const OpStr& opstr) const
{
TRACE_SERIALIZE (this);
HBUINT8 *d = c->allocate_size<HBUINT8> (opstr.str.len);
if (unlikely (d == nullptr)) return_trace (false);
memcpy (d, &opstr.str.str[0], opstr.str.len);
return_trace (true);
}
};
/* Top Dict, Font Dict, Private Dict */
struct Dict : UnsizedByteStr
{
@ -516,22 +692,31 @@ struct TopDict : Dict {};
struct FontDict : Dict {};
struct PrivateDict : Dict {};
struct FDArray : IndexOf<FontDict>
struct TableInfo
{
void init (void) { offset = size = 0; }
unsigned int offset;
unsigned int size;
};
template <typename COUNT>
struct FDArray : IndexOf<COUNT, FontDict>
{
template <typename DICTVAL, typename OP_SERIALIZER>
inline bool serialize (hb_serialize_context_t *c,
unsigned int offSize,
unsigned int offSize_,
const hb_vector_t<DICTVAL> &fontDicts,
unsigned int fdCount,
const hb_vector_t<hb_codepoint_t> &fdmap,
OP_SERIALIZER& opszr,
const hb_vector_t<offset_size_pair> &privatePairs)
const hb_vector_t<TableInfo> &privateInfos)
{
TRACE_SERIALIZE (this);
if (unlikely (!c->extend_min (*this))) return_trace (false);
this->count.set (fdCount);
this->offSize.set (offSize);
if (!unlikely (c->allocate_size<HBUINT8> (offSize * (fdCount + 1))))
this->offSize.set (offSize_);
if (!unlikely (c->allocate_size<HBUINT8> (offSize_ * (fdCount + 1))))
return_trace (false);
/* serialize font dict offsets */
@ -540,17 +725,17 @@ struct FDArray : IndexOf<FontDict>
for (unsigned i = 0; i < fontDicts.len; i++)
if (!fdmap.len || fdmap[i] != HB_SET_VALUE_INVALID)
{
set_offset_at (fid++, offset);
IndexOf<COUNT, FontDict>::set_offset_at (fid++, offset);
offset += FontDict::calculate_serialized_size (fontDicts[i], opszr);
}
set_offset_at (fid, offset);
IndexOf<COUNT, FontDict>::set_offset_at (fid, offset);
/* serialize font dicts */
for (unsigned int i = 0; i < fontDicts.len; i++)
if (fdmap[i] != HB_SET_VALUE_INVALID)
{
FontDict *dict = c->start_embed<FontDict> ();
if (unlikely (!dict->serialize (c, fontDicts[i], opszr, privatePairs[i])))
if (unlikely (!dict->serialize (c, fontDicts[i], opszr, privateInfos[i])))
return_trace (false);
}
return_trace (true);
@ -558,7 +743,7 @@ struct FDArray : IndexOf<FontDict>
/* in parallel to above */
template <typename OP_SERIALIZER, typename DICTVAL>
inline static unsigned int calculate_serialized_size (unsigned int &offSize /* OUT */,
inline static unsigned int calculate_serialized_size (unsigned int &offSize_ /* OUT */,
const hb_vector_t<DICTVAL> &fontDicts,
unsigned int fdCount,
const hb_vector_t<hb_codepoint_t> &fdmap,
@ -569,8 +754,8 @@ struct FDArray : IndexOf<FontDict>
if (!fdmap.len || fdmap[i] != HB_SET_VALUE_INVALID)
dictsSize += FontDict::calculate_serialized_size (fontDicts[i], opszr);
offSize = calcOffSize (dictsSize + 1);
return Index::calculate_serialized_size (offSize, fdCount, dictsSize);
offSize_ = calcOffSize (dictsSize + 1);
return Index<COUNT>::calculate_serialized_size (offSize_, fdCount, dictsSize);
}
};
@ -641,9 +826,9 @@ struct FDSelect3_4 {
inline hb_codepoint_t get_fd (hb_codepoint_t glyph) const
{
for (unsigned int i = 0; i < nRanges; i++)
if (glyph < ranges[i + 1].first)
return (hb_codepoint_t)ranges[i].fd;
for (unsigned int i = 1; i < nRanges; i++)
if (glyph < ranges[i].first)
return (hb_codepoint_t)ranges[i - 1].fd;
assert (false);
}
@ -709,131 +894,89 @@ struct FDSelect {
FDSelect3 format3;
} u;
DEFINE_SIZE_MIN (2);
DEFINE_SIZE_MIN (1);
};
typedef Index CharStrings;
typedef Index Subrs;
inline float parse_bcd (const ByteStr& str, unsigned int& offset, float& v)
{
// XXX: TODO
v = 0;
for (;;) {
if (++offset >= str.len)
return false;
unsigned char byte = str[offset];
if (((byte & 0xF0) == 0xF0) || ((byte & 0x0F) == 0x0F))
break;
}
return true;
}
struct Stack
struct TopDictValues : DictValues<OpStr>
{
inline void init (void) { size = 0; }
inline void fini (void) { }
inline void push (const Number &v)
inline void init (void)
{
if (likely (size < kSizeLimit))
numbers[size++] = v;
DictValues<OpStr>::init ();
charStringsOffset = 0;
FDArrayOffset = 0;
}
inline void push_int (int v)
inline void fini (void)
{
Number n;
n.set_int (v);
push (n);
DictValues<OpStr>::fini ();
}
inline void push_real (float v)
inline unsigned int calculate_serialized_op_size (const OpStr& opstr) const
{
Number n;
n.set_real (v);
push (n);
}
inline const Number& pop (void)
switch (opstr.op)
{
if (likely (size > 0))
return numbers[--size];
else
return Null(Number);
}
case OpCode_CharStrings:
case OpCode_FDArray:
return OpCode_Size (OpCode_longint) + 4 + OpCode_Size (opstr.op);
inline bool check_push (void)
{
if (likely (size < kSizeLimit)) {
size++;
return true;
} else
return false;
default:
return opstr.str.len;
}
inline bool check_pop (void)
{
if (likely (0 < size)) {
size--;
return true;
} else
return false;
}
inline bool check_pop_num (Number& n)
{
if (unlikely (!this->check_underflow (1)))
return false;
n = this->pop ();
return true;
}
unsigned int charStringsOffset;
unsigned int FDArrayOffset;
};
inline bool check_pop_uint (unsigned int& v)
struct TopDictOpSet
{
uint32_t i;
if (unlikely (!this->check_underflow (1)))
static inline bool process_op (const ByteStr& str, unsigned int& offset, OpCode op, Stack& stack, TopDictValues& dictval)
{
switch (op) {
case OpCode_CharStrings:
if (unlikely (!stack.check_pop_uint (dictval.charStringsOffset)))
return false;
i = this->pop ().to_int ();
if (unlikely (i <= 0))
stack.clear ();
break;
case OpCode_FDArray:
if (unlikely (!stack.check_pop_uint (dictval.FDArrayOffset)))
return false;
v = (uint32_t)i;
return true;
}
stack.clear ();
break;
case OpCode_longint: /* 5-byte integer */
return stack.push_longint_from_str (str, offset);
inline bool check_pop_delta (hb_vector_t<Number>& vec, bool even=false)
{
if (even && unlikely ((this->size & 1) != 0))
case OpCode_BCD: /* real number */
float v;
if (unlikely (stack.check_overflow (1) || !parse_bcd (str, offset, v)))
return false;
stack.push_real (v);
return true;
float val = 0.0f;
for (unsigned int i = 0; i < size; i++) {
val += numbers[i].to_real ();
Number *n = vec.push ();
n->set_real (val);
default:
/* XXX: invalid */
stack.clear ();
return false;
}
return true;
}
inline void clear (void) { size = 0; }
inline bool check_overflow (unsigned int count) { return (count <= kSizeLimit) && (count + size <= kSizeLimit); }
inline bool check_underflow (unsigned int count) { return (count <= size); }
static const unsigned int kSizeLimit = 513;
unsigned int size;
Number numbers[kSizeLimit];
};
template <typename Offset>
inline bool check_pop_offset (Stack& stack, Offset& offset)
/* base of OP_SERIALIZER */
struct OpSerializer
{
unsigned int v;
if (unlikely (!stack.check_pop_uint (v)))
return false;
offset.set (v);
return true;
protected:
inline bool copy_opstr (hb_serialize_context_t *c, const OpStr& opstr) const
{
TRACE_SERIALIZE (this);
HBUINT8 *d = c->allocate_size<HBUINT8> (opstr.str.len);
if (unlikely (d == nullptr)) return_trace (false);
memcpy (d, &opstr.str.str[0], opstr.str.len);
return_trace (true);
}
};
template <typename OpSet, typename Param>
struct Interpreter {
@ -919,24 +1062,6 @@ struct Interpreter {
Stack stack;
};
/* used by subsettter */
struct SubTableOffsets {
inline SubTableOffsets (void)
{
memset (this, 0, sizeof(*this));
}
unsigned int topDictSize;
unsigned int varStoreOffset;
unsigned int FDSelectOffset;
unsigned int FDSelectSize;
unsigned int FDArrayOffset;
unsigned int FDArrayOffSize;
unsigned int charStringsOffset;
unsigned int charStringsOffSize;
unsigned int privateDictsOffset;
};
} /* namespace CFF */
#endif /* HB_OT_CFF_COMMON_PRIVATE_HH */

@ -0,0 +1,919 @@
/*
* Copyright © 2018 Adobe Systems Incorporated.
*
* 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.
*
* Adobe Author(s): Michiharu Ariza
*/
#ifndef HB_OT_CFF_TABLE_HH
#define HB_OT_CFF_TABLE_HH
#include "hb-ot-cff-common-private.hh"
#include "hb-subset-cff.hh"
namespace CFF {
/*
* CFF -- Compact Font Format (CFF)
* http://www.adobe.com/content/dam/acom/en/devnet/font/pdfs/5176.CFF.pdf
*/
#define HB_OT_TAG_cff HB_TAG('C','F','F',' ')
typedef Index<HBUINT16> CFFIndex;
template <typename Type> struct CFFIndexOf : IndexOf<HBUINT16, Type> {};
typedef Index<HBUINT16> CFFIndex;
typedef CFFIndex CFFCharStrings;
typedef FDArray<HBUINT16> CFFFDArray;
typedef CFFIndex CFFSubrs;
struct CFFFDSelect : FDSelect {};
/* Encoding */
struct Encoding0 {
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this) && codes[nCodes - 1].sanitize (c));
}
inline bool get_code (hb_codepoint_t glyph, hb_codepoint_t &code) const
{
if (glyph < nCodes)
{
code = (hb_codepoint_t)codes[glyph];
return true;
}
else
return false;
}
inline unsigned int get_size (void) const
{ return HBUINT8::static_size * (nCodes + 1); }
HBUINT8 nCodes;
HBUINT8 codes[VAR];
DEFINE_SIZE_ARRAY(1, codes);
};
struct Encoding1_Range {
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this));
}
HBUINT8 first;
HBUINT8 nLeft;
DEFINE_SIZE_STATIC (2);
};
struct Encoding1 {
inline unsigned int get_size (void) const
{ return HBUINT8::static_size + Encoding1_Range::static_size * nRanges; }
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this) && ((nRanges == 0) || (ranges[nRanges - 1]).sanitize (c)));
}
inline bool get_code (hb_codepoint_t glyph, hb_codepoint_t &code) const
{
for (unsigned int i = 0; i < nRanges; i++)
{
if (glyph <= ranges[i].nLeft)
{
code = (hb_codepoint_t)ranges[i].first + glyph;
return true;
}
glyph -= (ranges[i].nLeft + 1);
}
return false;
}
HBUINT8 nRanges;
Encoding1_Range ranges[VAR];
DEFINE_SIZE_ARRAY (1, ranges);
};
struct EncSupplement {
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this));
}
HBUINT8 code;
HBUINT16 glyph;
DEFINE_SIZE_STATIC (3);
};
struct CFFSuppEncData {
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this) && ((nSups == 0) || (supps[nSups - 1]).sanitize (c)));
}
inline bool get_code (hb_codepoint_t glyph, hb_codepoint_t &code) const
{
for (unsigned int i = 0; i < nSups; i++)
if (glyph == supps[i].glyph)
{
code = supps[i].code;
return true;
}
return false;
}
inline unsigned int get_size (void) const
{ return HBUINT8::static_size + EncSupplement::static_size * nSups; }
HBUINT8 nSups;
EncSupplement supps[VAR];
DEFINE_SIZE_ARRAY (1, supps);
};
struct Encoding {
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
if (unlikely (!c->check_struct (this)))
return_trace (false);
unsigned int fmt = format & 0x7F;
if (unlikely (fmt > 1))
return_trace (false);
if (unlikely (!((fmt == 0)? u.format0.sanitize (c): u.format1.sanitize (c))))
return_trace (false);
return_trace (((format & 0x80) == 0) || suppEncData ().sanitize (c));
}
inline bool serialize (hb_serialize_context_t *c, const Encoding &src, unsigned int num_glyphs)
{
TRACE_SERIALIZE (this);
unsigned int size = src.get_size ();
Encoding *dest = c->allocate_size<Encoding> (size);
if (unlikely (dest == nullptr)) return_trace (false);
memcpy (dest, &src, size);
return_trace (true);
}
inline unsigned int calculate_serialized_size (void) const
{ return get_size (); } // XXX: TODO
inline unsigned int get_size (void) const
{
unsigned int size = min_size;
if ((format & 0x7F) == 0)
size += u.format0.get_size ();
else
size += u.format1.get_size ();
if ((format & 0x80) != 0)
size += suppEncData ().get_size ();
return size;
}
inline bool get_code (hb_codepoint_t glyph, hb_codepoint_t &code) const
{
// XXX: TODO
return false;
}
inline const CFFSuppEncData &suppEncData (void) const
{
if ((format & 0x7F) == 0)
return StructAfter<CFFSuppEncData> (u.format0.codes[u.format0.nCodes-1]);
else
return StructAfter<CFFSuppEncData> (u.format1.ranges[u.format1.nRanges-1]);
}
HBUINT8 format;
union {
Encoding0 format0;
Encoding1 format1;
} u;
/* CFFSuppEncData suppEncData; */
DEFINE_SIZE_MIN (1);
};
/* Charset */
struct Charset0 {
inline bool sanitize (hb_sanitize_context_t *c, unsigned int num_glyphs) const
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this) && sids[num_glyphs - 1].sanitize (c));
}
inline unsigned int get_sid (hb_codepoint_t glyph)
{
if (glyph == 0)
return 0;
else
return sids[glyph - 1];
}
inline unsigned int get_size (unsigned int num_glyphs) const
{
assert (num_glyphs > 0);
return HBUINT16::static_size * (num_glyphs - 1);
}
HBUINT16 sids[VAR];
DEFINE_SIZE_ARRAY(0, sids);
};
template <typename TYPE>
struct Charset_Range {
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this));
}
HBUINT8 first;
TYPE nLeft;
DEFINE_SIZE_STATIC (1 + TYPE::static_size);
};
template <typename TYPE>
struct Charset1_2 {
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this) && ((nRanges == 0) || (ranges[nRanges - 1]).sanitize (c)));
}
inline bool get_sid (hb_codepoint_t glyph, unsigned int &sid) const
{
for (unsigned int i = 0; i < nRanges; i++)
{
if (glyph <= ranges[i].nLeft)
{
sid = (hb_codepoint_t)ranges[i].first + glyph;
return true;
}
glyph -= (ranges[i].nLeft + 1);
}
return false;
}
inline unsigned int get_size (void) const
{ return HBUINT8::static_size + Charset_Range<TYPE>::static_size * nRanges; }
HBUINT8 nRanges;
Charset_Range<TYPE> ranges[VAR];
DEFINE_SIZE_ARRAY (1, ranges);
};
struct Charset {
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
if (unlikely (!c->check_struct (this)))
return_trace (false);
if (format == 0)
return_trace (u.format0.sanitize (c, c->get_num_glyphs ()));
else if (format == 1)
return_trace (u.format1.sanitize (c));
else if (likely (format == 2))
return_trace (u.format2.sanitize (c));
else
return_trace (false);
}
inline bool serialize (hb_serialize_context_t *c, const Charset &src, unsigned int num_glyphs)
{
TRACE_SERIALIZE (this);
unsigned int size = src.get_size (num_glyphs);
Charset *dest = c->allocate_size<Charset> (size);
if (unlikely (dest == nullptr)) return_trace (false);
memcpy (dest, &src, size);
return_trace (true);
}
inline unsigned int calculate_serialized_size (unsigned int num_glyphs) const
{ return get_size (num_glyphs); } // XXX: TODO
inline unsigned int get_size (unsigned int num_glyphs) const
{
unsigned int size = min_size;
if (format == 0)
size += u.format0.get_size (num_glyphs);
else if (format == 1)
size += u.format1.get_size ();
else
size += u.format2.get_size ();
return size;
}
inline bool get_sid (hb_codepoint_t glyph, unsigned int &sid) const
{
// XXX: TODO
return false;
}
HBUINT8 format;
union {
Charset0 format0;
Charset1_2<HBUINT8> format1;
Charset1_2<HBUINT16> format2;
} u;
DEFINE_SIZE_MIN (1);
};
struct CFFTopDictValues : TopDictValues
{
inline void init (void)
{
TopDictValues::init ();
ros[0] = ros[1] = ros[2] = 0;
cidCount = 8720;
EncodingOffset = 0;
CharsetOffset = 0;
FDSelectOffset = 0;
privateDictInfo.init ();
}
inline void fini (void)
{
TopDictValues::fini ();
}
inline bool is_CID (void) const
{ return ros[0] != 0; }
inline unsigned int calculate_serialized_size (void) const
{
unsigned int size = 0;
for (unsigned int i = 0; i < values.len; i++)
{
OpCode op = values[i].op;
switch (op)
{
case OpCode_FDSelect:
size += OpCode_Size (OpCode_longint) + 4 + OpCode_Size (op);
break;
default:
size += TopDictValues::calculate_serialized_op_size (values[i]);
break;
}
}
return size;
}
unsigned int ros[3]; /* registry, ordering, supplement */
unsigned int cidCount;
enum EncodingID { StandardEncoding = 0, ExpertEncoding = 1 };
unsigned int EncodingOffset;
unsigned int CharsetOffset;
unsigned int FDSelectOffset;
TableInfo privateDictInfo;
};
struct CFFTopDictOpSet : TopDictOpSet
{
static inline bool process_op (const ByteStr& str, unsigned int& offset,
OpCode op, Stack& stack, CFFTopDictValues& dictval)
{
switch (op) {
case OpCode_version:
case OpCode_Notice:
case OpCode_Copyright:
case OpCode_FullName:
case OpCode_FamilyName:
case OpCode_Weight:
case OpCode_isFixedPitch:
case OpCode_ItalicAngle:
case OpCode_UnderlinePosition:
case OpCode_UnderlineThickness:
case OpCode_PaintType:
case OpCode_CharstringType:
case OpCode_UniqueID:
case OpCode_StrokeWidth:
case OpCode_SyntheticBase:
case OpCode_PostScript:
case OpCode_BaseFontName:
case OpCode_CIDFontVersion:
case OpCode_CIDFontRevision:
case OpCode_CIDFontType:
case OpCode_UIDBase:
case OpCode_FontBBox:
case OpCode_XUID:
case OpCode_BaseFontBlend:
stack.clear ();
break;
case OpCode_CIDCount:
if (unlikely (!stack.check_pop_uint (dictval.cidCount)))
return false;
stack.clear ();
break;
case OpCode_ROS:
if (unlikely (!stack.check_pop_uint (dictval.ros[2]) ||
!stack.check_pop_uint (dictval.ros[1]) ||
!stack.check_pop_uint (dictval.ros[0])))
return false;
stack.clear ();
break;
case OpCode_Encoding:
if (unlikely (!stack.check_pop_uint (dictval.EncodingOffset)))
return false;
stack.clear ();
break;
case OpCode_charset:
if (unlikely (!stack.check_pop_uint (dictval.CharsetOffset)))
return false;
stack.clear ();
break;
case OpCode_FDSelect:
if (unlikely (!stack.check_pop_uint (dictval.FDSelectOffset)))
return false;
stack.clear ();
break;
case OpCode_Private:
if (unlikely (!stack.check_pop_uint (dictval.privateDictInfo.offset)))
return false;
if (unlikely (!stack.check_pop_uint (dictval.privateDictInfo.size)))
return false;
stack.clear ();
break;
default:
if (unlikely (!TopDictOpSet::process_op (str, offset, op, stack, dictval)))
return false;
/* Record this operand below if stack is empty, otherwise done */
if (!stack.is_empty ()) return true;
break;
}
dictval.pushVal (op, str, offset + 1);
return true;
}
};
struct CFFFontDictValues : DictValues<OpStr>
{
inline void init (void)
{
DictValues<OpStr>::init ();
privateDictInfo.init ();
}
inline void fini (void)
{
DictValues<OpStr>::fini ();
}
TableInfo privateDictInfo;
};
struct CFFFontDictOpSet
{
static inline bool process_op (const ByteStr& str, unsigned int& offset,
OpCode op, Stack& stack, CFFFontDictValues& dictval)
{
switch (op) {
case OpCode_FontName:
case OpCode_FontMatrix:
case OpCode_PaintType:
stack.clear ();
break;
case OpCode_Private:
if (unlikely (!stack.check_pop_uint (dictval.privateDictInfo.offset)))
return false;
if (unlikely (!stack.check_pop_uint (dictval.privateDictInfo.size)))
return false;
stack.clear ();
break;
case OpCode_longint: /* 5-byte integer */
return stack.push_longint_from_str (str, offset);
case OpCode_BCD: /* real number */
float v;
if (unlikely (stack.check_overflow (1) || !parse_bcd (str, offset, v)))
return false;
stack.push_real (v);
return true;
default:
/* XXX: invalid */
stack.clear ();
return false;
}
dictval.pushVal (op, str, offset + 1);
return true;
}
};
template <typename VAL>
struct CFFPrivateDictValues_Base : DictValues<VAL>
{
inline void init (void)
{
DictValues<VAL>::init ();
subrsOffset = 0;
localSubrs = &Null(CFFSubrs);
}
inline void fini (void)
{
DictValues<VAL>::fini ();
}
inline unsigned int calculate_serialized_size (void) const
{
unsigned int size = 0;
for (unsigned int i = 0; i < DictValues<VAL>::values.len; i++)
if (DictValues<VAL>::values[i].op == OpCode_Subrs)
size += OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Subrs);
else
size += DictValues<VAL>::values[i].str.len;
return size;
}
unsigned int subrsOffset;
const CFFSubrs *localSubrs;
};
typedef CFFPrivateDictValues_Base<OpStr> CFFPrivateDictValues_Subset;
typedef CFFPrivateDictValues_Base<DictVal> CFFPrivateDictValues;
struct CFFPrivateDictOpSet
{
static inline bool process_op (const ByteStr& str, unsigned int& offset,
OpCode op, Stack& stack, CFFPrivateDictValues& dictval)
{
DictVal val;
val.init ();
switch (op) {
case OpCode_BlueValues:
case OpCode_OtherBlues:
case OpCode_FamilyBlues:
case OpCode_FamilyOtherBlues:
case OpCode_StemSnapH:
case OpCode_StemSnapV:
if (unlikely (!stack.check_pop_delta (val.multi_val)))
return false;
break;
case OpCode_StdHW:
case OpCode_StdVW:
case OpCode_BlueScale:
case OpCode_BlueShift:
case OpCode_BlueFuzz:
case OpCode_ForceBold:
case OpCode_LanguageGroup:
case OpCode_ExpansionFactor:
case OpCode_initialRandomSeed:
case OpCode_defaultWidthX:
case OpCode_nominalWidthX:
if (unlikely (!stack.check_pop_num (val.single_val)))
return false;
stack.clear ();
break;
case OpCode_Subrs:
if (unlikely (!stack.check_pop_uint (dictval.subrsOffset)))
return false;
stack.clear ();
break;
case OpCode_longint: /* 5-byte integer */
return stack.push_longint_from_str (str, offset);
case OpCode_BCD: /* real number */
float v;
if (unlikely (!stack.check_overflow (1) || !parse_bcd (str, offset, v)))
return false;
stack.push_real (v);
return true;
default:
return false;
}
dictval.pushVal (op, str, offset + 1, val);
return true;
}
};
struct CFFPrivateDictOpSet_Subset
{
static inline bool process_op (const ByteStr& str, unsigned int& offset,
OpCode op, Stack& stack, CFFPrivateDictValues_Subset& dictval)
{
switch (op) {
case OpCode_BlueValues:
case OpCode_OtherBlues:
case OpCode_FamilyBlues:
case OpCode_FamilyOtherBlues:
case OpCode_StemSnapH:
case OpCode_StemSnapV:
case OpCode_StdHW:
case OpCode_StdVW:
case OpCode_BlueScale:
case OpCode_BlueShift:
case OpCode_BlueFuzz:
case OpCode_ForceBold:
case OpCode_LanguageGroup:
case OpCode_ExpansionFactor:
case OpCode_initialRandomSeed:
case OpCode_defaultWidthX:
case OpCode_nominalWidthX:
stack.clear ();
break;
case OpCode_BCD:
{
float v;
return parse_bcd (str, offset, v);
}
case OpCode_Subrs:
if (unlikely (!stack.check_pop_uint (dictval.subrsOffset)))
return false;
stack.clear ();
break;
case OpCode_longint: /* 5-byte integer */
return stack.push_longint_from_str (str, offset);
default:
return false;
}
dictval.pushVal (op, str, offset + 1);
return true;
}
};
typedef Interpreter<CFFTopDictOpSet, CFFTopDictValues> CFFTopDict_Interpreter;
typedef Interpreter<CFFFontDictOpSet, CFFFontDictValues> CFFFontDict_Interpreter;
typedef Interpreter<CFFPrivateDictOpSet, CFFPrivateDictValues> CFFPrivateDict_Interpreter;
typedef CFFIndex NameIndex;
typedef CFFIndexOf<TopDict> TopDictIndex;
typedef CFFIndex StringIndex;
}; /* namespace CFF */
namespace OT {
using namespace CFF;
struct cff
{
static const hb_tag_t tableTag = HB_OT_TAG_cff;
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this) &&
likely (version.major == 1));
}
template <typename PrivOpSet, typename PrivDictVal>
struct accelerator_templ_t
{
inline void init (hb_face_t *face)
{
topDicts.init ();
topDicts.resize (1);
topDicts[0].init ();
fontDicts.init ();
privateDicts.init ();
this->blob = sc.reference_table<cff> (face);
/* setup for run-time santization */
sc.init (this->blob);
sc.start_processing ();
const OT::cff *cff = this->blob->template as<OT::cff> ();
if (cff == &Null(OT::cff))
{ fini (); return; }
nameIndex = &cff->nameIndex (cff);
if ((nameIndex == &Null (NameIndex)) || !nameIndex->sanitize (&sc))
{ fini (); return; }
topDictIndex = &StructAtOffset<TopDictIndex> (nameIndex, nameIndex->get_size ());
if ((topDictIndex == &Null (TopDictIndex)) || !topDictIndex->sanitize (&sc) || (topDictIndex->count == 0))
{ fini (); return; }
{ /* parse top dict */
const ByteStr topDictStr = (*topDictIndex)[0];
CFFTopDict_Interpreter top_interp;
if (unlikely (!topDictStr.sanitize (&sc) ||
!top_interp.interpret (topDictStr, topDicts[0])))
{ fini (); return; }
}
encoding = &Null(Encoding);
charset = &StructAtOffsetOrNull<Charset> (cff, topDicts[0].CharsetOffset);
fdCount = 1;
if (is_CID ())
{
fdArray = &StructAtOffsetOrNull<CFFFDArray> (cff, topDicts[0].FDArrayOffset);
fdSelect = &StructAtOffsetOrNull<CFFFDSelect> (cff, topDicts[0].FDSelectOffset);
if (unlikely ((fdArray == &Null(CFFFDArray)) || !fdArray->sanitize (&sc) ||
(fdSelect == &Null(CFFFDSelect)) || !fdSelect->sanitize (&sc, fdArray->count)))
{ fini (); return; }
fdCount = fdArray->count;
}
else
{
fdArray = &Null(CFFFDArray);
fdSelect = &Null(CFFFDSelect);
if (topDicts[0].EncodingOffset != CFFTopDictValues::StandardEncoding &&
topDicts[0].EncodingOffset != CFFTopDictValues::ExpertEncoding)
{
encoding = &StructAtOffsetOrNull<Encoding> (cff, topDicts[0].EncodingOffset);
if ((encoding == &Null (Encoding)) || !encoding->sanitize (&sc))
{ fini (); return; }
}
}
stringIndex = &StructAtOffset<StringIndex> (topDictIndex, topDictIndex->get_size ());
if ((stringIndex == &Null (StringIndex)) || !stringIndex->sanitize (&sc))
{ fini (); return; }
globalSubrs = &StructAtOffset<CFFSubrs> (stringIndex, stringIndex->get_size ());
if ((globalSubrs != &Null (CFFSubrs)) && !stringIndex->sanitize (&sc))
{ fini (); return; }
charStrings = &StructAtOffsetOrNull<CFFCharStrings> (cff, topDicts[0].charStringsOffset);
if ((charStrings == &Null(CFFCharStrings)) || unlikely (!charStrings->sanitize (&sc)))
{ fini (); return; }
num_glyphs = charStrings->count;
if (num_glyphs != sc.get_num_glyphs ())
{ fini (); return; }
privateDicts.resize (fdCount);
for (unsigned int i = 0; i < fdCount; i++)
privateDicts[i].init ();
// parse CID font dicts and gather private dicts
if (is_CID ())
{
for (unsigned int i = 0; i < fdCount; i++)
{
ByteStr fontDictStr = (*fdArray)[i];
CFFFontDictValues *font;
CFFFontDict_Interpreter font_interp;
font = fontDicts.push ();
if (unlikely (!fontDictStr.sanitize (&sc) ||
!font_interp.interpret (fontDictStr, *font)))
{ fini (); return; }
PrivDictVal *priv = &privateDicts[i];
const ByteStr privDictStr (StructAtOffset<UnsizedByteStr> (cff, font->privateDictInfo.offset), font->privateDictInfo.size);
Interpreter<PrivOpSet, PrivDictVal> priv_interp;
if (unlikely (!privDictStr.sanitize (&sc) ||
!priv_interp.interpret (privDictStr, *priv)))
{ fini (); return; }
priv->localSubrs = &StructAtOffsetOrNull<CFFSubrs> (privDictStr.str, priv->subrsOffset);
if (priv->localSubrs != &Null(CFFSubrs) &&
unlikely (!priv->localSubrs->sanitize (&sc)))
{ fini (); return; }
}
}
else /* non-CID */
{
CFFTopDictValues *font = &topDicts[0];
PrivDictVal *priv = &privateDicts[0];
const ByteStr privDictStr (StructAtOffset<UnsizedByteStr> (cff, font->privateDictInfo.offset), font->privateDictInfo.size);
Interpreter<PrivOpSet, PrivDictVal> priv_interp;
if (unlikely (!privDictStr.sanitize (&sc) ||
!priv_interp.interpret (privDictStr, *priv)))
{ fini (); return; }
priv->localSubrs = &StructAtOffsetOrNull<CFFSubrs> (privDictStr.str, priv->subrsOffset);
if (priv->localSubrs != &Null(CFFSubrs) &&
unlikely (!priv->localSubrs->sanitize (&sc)))
{ fini (); return; }
}
}
inline void fini (void)
{
sc.end_processing ();
topDicts[0].fini ();
topDicts.fini ();
fontDicts.fini ();
privateDicts.fini ();
hb_blob_destroy (blob);
blob = nullptr;
}
inline bool is_valid (void) const { return blob != nullptr; }
inline bool is_CID (void) const { return topDicts[0].is_CID (); }
inline bool get_extents (hb_codepoint_t glyph,
hb_glyph_extents_t *extents) const
{
// XXX: TODO
if (glyph >= num_glyphs)
return false;
return true;
}
protected:
hb_blob_t *blob;
hb_sanitize_context_t sc;
public:
const NameIndex *nameIndex;
const TopDictIndex *topDictIndex;
const StringIndex *stringIndex;
const Encoding *encoding;
const Charset *charset;
const CFFSubrs *globalSubrs;
const CFFCharStrings *charStrings;
const CFFFDArray *fdArray;
const CFFFDSelect *fdSelect;
unsigned int fdCount;
hb_vector_t<CFFTopDictValues> topDicts;
hb_vector_t<CFFFontDictValues> fontDicts;
hb_vector_t<PrivDictVal> privateDicts;
unsigned int num_glyphs;
};
typedef accelerator_templ_t<CFFPrivateDictOpSet, CFFPrivateDictValues> accelerator_t;
typedef accelerator_templ_t<CFFPrivateDictOpSet_Subset, CFFPrivateDictValues_Subset> accelerator_subset_t;
inline bool subset (hb_subset_plan_t *plan) const
{
hb_blob_t *cff_prime = nullptr;
bool success = true;
if (hb_subset_cff (plan, &cff_prime)) {
success = success && plan->add_table (HB_OT_TAG_cff, cff_prime);
} else {
success = false;
}
hb_blob_destroy (cff_prime);
return success;
}
public:
FixedVersion<HBUINT8> version; /* Version of CFF table. set to 0x0100u */
OffsetTo<NameIndex, HBUINT8> nameIndex; /* headerSize = Offset to Name INDEX. */
HBUINT8 offSize; /* offset size (unused?) */
public:
DEFINE_SIZE_STATIC (4);
};
} /* namespace OT */
#endif /* HB_OT_CFF_TABLE_HH */

@ -38,6 +38,13 @@ namespace CFF {
*/
#define HB_OT_TAG_cff2 HB_TAG('C','F','F','2')
typedef Index<HBUINT32> CFF2Index;
template <typename Type> struct CFF2IndexOf : IndexOf<HBUINT32, Type> {};
typedef CFF2Index CFF2CharStrings;
typedef FDArray<HBUINT32> CFF2FDArray;
typedef CFF2Index CFF2Subrs;
typedef FDSelect3_4<HBUINT32, HBUINT16> FDSelect4;
typedef FDSelect3_4_Range<HBUINT32, HBUINT16> FDSelect4_Range;
@ -126,22 +133,18 @@ struct CFF2VariationStore
DEFINE_SIZE_MIN (2 + VariationStore::min_size);
};
struct CFF2TopDictValues : DictValues<OpStr>
struct CFF2TopDictValues : TopDictValues
{
inline void init (void)
{
DictValues<OpStr>::init ();
charStringsOffset.set (0);
vstoreOffset.set (0);
FDArrayOffset.set (0);
FDSelectOffset.set (0);
TopDictValues::init ();
vstoreOffset = 0;
FDSelectOffset = 0;
}
inline void fini (void)
{
DictValues<OpStr>::fini ();
FontMatrix[0] = FontMatrix[3] = 0.001f;
FontMatrix[1] = FontMatrix[2] = FontMatrix[4] = FontMatrix[5] = 0.0f;
TopDictValues::fini ();
}
inline unsigned int calculate_serialized_size (void) const
@ -150,66 +153,55 @@ struct CFF2TopDictValues : DictValues<OpStr>
for (unsigned int i = 0; i < values.len; i++)
{
OpCode op = values[i].op;
if (op == OpCode_FontMatrix)
size += values[i].str.len;
else
switch (op)
{
case OpCode_vstore:
case OpCode_FDSelect:
size += OpCode_Size (OpCode_longint) + 4 + OpCode_Size (op);
break;
default:
size += TopDictValues::calculate_serialized_op_size (values[i]);
break;
}
}
return size;
}
float FontMatrix[6];
LOffsetTo<CharStrings> charStringsOffset;
LOffsetTo<CFF2VariationStore> vstoreOffset;
LOffsetTo<FDArray> FDArrayOffset;
LOffsetTo<CFF2FDSelect> FDSelectOffset;
unsigned int vstoreOffset;
unsigned int FDSelectOffset;
};
struct CFF2TopDictOpSet
struct CFF2TopDictOpSet : TopDictOpSet
{
static inline bool process_op (const ByteStr& str, unsigned int& offset, OpCode op, Stack& stack, CFF2TopDictValues& dictval)
static inline bool process_op (const ByteStr& str, unsigned int& offset,
OpCode op, Stack& stack, CFF2TopDictValues& dictval)
{
switch (op) {
case OpCode_CharStrings:
if (unlikely (!check_pop_offset (stack, dictval.charStringsOffset)))
return false;
case OpCode_FontMatrix:
{
DictVal val;
val.init ();
dictval.pushVal (op, str, offset + 1);
stack.clear ();
}
break;
case OpCode_vstore:
if (unlikely (!check_pop_offset (stack, dictval.vstoreOffset)))
return false;
break;
case OpCode_FDArray:
if (unlikely (!check_pop_offset (stack, dictval.FDArrayOffset)))
if (unlikely (!stack.check_pop_uint (dictval.vstoreOffset)))
return false;
stack.clear ();
break;
case OpCode_FDSelect:
if (unlikely (!check_pop_offset (stack, dictval.FDSelectOffset)))
return false;
break;
case OpCode_FontMatrix:
if (unlikely (!stack.check_underflow (6)))
if (unlikely (!stack.check_pop_uint (dictval.FDSelectOffset)))
return false;
for (int i = 0; i < 6; i++)
dictval.FontMatrix[i] = stack.pop ().to_real ();
stack.clear ();
break;
case OpCode_longint: /* 5-byte integer */
if (unlikely (!str.check_limit (offset, 5) || !stack.check_overflow (1)))
return false;
stack.push_int ((int32_t)*(const HBUINT32*)&str[offset + 1]);
offset += 4;
return true;
case OpCode_BCD: /* real number */
float v;
if (unlikely (stack.check_overflow (1) || !parse_bcd (str, offset, v)))
return false;
stack.push_real (v);
return true;
default:
/* XXX: invalid */
stack.clear ();
if (unlikely (!TopDictOpSet::process_op (str, offset, op, stack, dictval)))
return false;
/* Record this operand below if stack is empty, otherwise done */
if (!stack.is_empty ()) return true;
}
dictval.pushVal (op, str, offset + 1);
@ -222,8 +214,7 @@ struct CFF2FontDictValues : DictValues<OpStr>
inline void init (void)
{
DictValues<OpStr>::init ();
privateDictSize = 0;
privateDictOffset.set (0);
privateDictInfo.init ();
}
inline void fini (void)
@ -231,27 +222,24 @@ struct CFF2FontDictValues : DictValues<OpStr>
DictValues<OpStr>::fini ();
}
unsigned int privateDictSize;
LOffsetTo<PrivateDict> privateDictOffset;
TableInfo privateDictInfo;
};
struct CFF2FontDictOpSet
{
static inline bool process_op (const ByteStr& str, unsigned int& offset, OpCode op, Stack& stack, CFF2FontDictValues& dictval)
static inline bool process_op (const ByteStr& str, unsigned int& offset,
OpCode op, Stack& stack, CFF2FontDictValues& dictval)
{
switch (op) {
case OpCode_Private:
if (unlikely (!check_pop_offset (stack, dictval.privateDictOffset)))
if (unlikely (!stack.check_pop_uint (dictval.privateDictInfo.offset)))
return false;
if (unlikely (!stack.check_pop_uint (dictval.privateDictSize)))
if (unlikely (!stack.check_pop_uint (dictval.privateDictInfo.size)))
return false;
stack.clear ();
break;
case OpCode_longint: /* 5-byte integer */
if (unlikely (!str.check_limit (offset, 5) || !stack.check_overflow (1)))
return false;
stack.push_int ((int32_t)((str[offset + 1] << 24) | ((uint32_t)str[offset + 2] << 16) | ((uint32_t)str[offset + 3] << 8) | str[offset + 4]));
offset += 4;
return true;
return stack.push_longint_from_str (str, offset);
case OpCode_BCD: /* real number */
float v;
if (unlikely (stack.check_overflow (1) || !parse_bcd (str, offset, v)))
@ -276,8 +264,8 @@ struct CFF2PrivateDictValues_Base : DictValues<VAL>
inline void init (void)
{
DictValues<VAL>::init ();
subrsOffset.set (0);
localSubrs = &Null(Subrs);
subrsOffset = 0;
localSubrs = &Null(CFF2Subrs);
}
inline void fini (void)
@ -296,8 +284,8 @@ struct CFF2PrivateDictValues_Base : DictValues<VAL>
return size;
}
LOffsetTo<Subrs> subrsOffset;
const Subrs *localSubrs;
unsigned int subrsOffset;
const CFF2Subrs *localSubrs;
};
typedef CFF2PrivateDictValues_Base<OpStr> CFF2PrivateDictValues_Subset;
@ -305,47 +293,44 @@ typedef CFF2PrivateDictValues_Base<DictVal> CFF2PrivateDictValues;
struct CFF2PrivateDictOpSet
{
static inline bool process_op (const ByteStr& str, unsigned int& offset, OpCode op, Stack& stack, CFF2PrivateDictValues& dictval)
static inline bool process_op (const ByteStr& str, unsigned int& offset,
OpCode op, Stack& stack, CFF2PrivateDictValues& dictval)
{
DictVal val;
val.init ();
switch (op) {
case OpCode_BlueValues:
case OpCode_OtherBlues:
case OpCode_FamilyBlues:
case OpCode_FamilyOtherBlues:
case OpCode_StemSnapH:
case OpCode_StemSnapV:
if (unlikely (!stack.check_pop_delta (val.multi_val)))
return false;
break;
case OpCode_StdHW:
case OpCode_StdVW:
case OpCode_BlueScale:
case OpCode_BlueShift:
case OpCode_BlueFuzz:
case OpCode_ExpansionFactor:
case OpCode_LanguageGroup:
if (unlikely (!stack.check_pop_num (val.single_val)))
return false;
stack.clear ();
break;
case OpCode_LanguageGroup:
if (unlikely (!stack.check_pop_num (val.single_val)))
case OpCode_BlueValues:
case OpCode_OtherBlues:
case OpCode_FamilyBlues:
case OpCode_FamilyOtherBlues:
case OpCode_StemSnapH:
case OpCode_StemSnapV:
if (unlikely (!stack.check_pop_delta (val.multi_val)))
return false;
stack.clear ();
break;
case OpCode_Subrs:
if (unlikely (!check_pop_offset (stack, dictval.subrsOffset)))
if (unlikely (!stack.check_pop_uint (dictval.subrsOffset)))
return false;
stack.clear ();
break;
case OpCode_blend:
// XXX: TODO
return true;
case OpCode_longint: /* 5-byte integer */
if (unlikely (!str.check_limit (offset, 5) || !stack.check_overflow (1)))
return false;
stack.push_int ((int32_t)((str[offset + 1] << 24) | (str[offset + 2] << 16) || (str[offset + 3] << 8) || str[offset + 4]));
offset += 4;
return true;
return stack.push_longint_from_str (str, offset);
case OpCode_BCD: /* real number */
float v;
if (unlikely (!stack.check_overflow (1) || !parse_bcd (str, offset, v)))
@ -364,7 +349,8 @@ struct CFF2PrivateDictOpSet
struct CFF2PrivateDictOpSet_Subset
{
static inline bool process_op (const ByteStr& str, unsigned int& offset, OpCode op, Stack& stack, CFF2PrivateDictValues_Subset& dictval)
static inline bool process_op (const ByteStr& str, unsigned int& offset,
OpCode op, Stack& stack, CFF2PrivateDictValues_Subset& dictval)
{
switch (op) {
case OpCode_BlueValues:
@ -394,15 +380,12 @@ struct CFF2PrivateDictOpSet_Subset
}
case OpCode_Subrs:
if (unlikely (!check_pop_offset (stack, dictval.subrsOffset)))
if (unlikely (!stack.check_pop_uint (dictval.subrsOffset)))
return false;
stack.clear ();
break;
case OpCode_longint: /* 5-byte integer */
if (unlikely (!str.check_limit (offset, 5) || !stack.check_overflow (1)))
return false;
stack.push_int ((int32_t)((str[offset + 1] << 24) | (str[offset + 2] << 16) || (str[offset + 3] << 8) || str[offset + 4]));
offset += 4;
return true;
return stack.push_longint_from_str (str, offset);
default:
return false;
@ -439,6 +422,7 @@ struct cff2
{
inline void init (hb_face_t *face)
{
topDict.init ();
fontDicts.init ();
privateDicts.init ();
@ -451,43 +435,31 @@ struct cff2
const OT::cff2 *cff2 = this->blob->template as<OT::cff2> ();
if (cff2 == &Null(OT::cff2))
{
fini ();
return;
}
{ fini (); return; }
{ /* parse top dict */
ByteStr topDictStr (cff2 + cff2->topDict, cff2->topDictSize);
CFF2TopDict_Interpreter top_interp;
if (unlikely (!topDictStr.sanitize (&sc) ||
!top_interp.interpret (topDictStr, top)))
{
fini ();
return;
}
!top_interp.interpret (topDictStr, topDict)))
{ fini (); return; }
}
globalSubrs = &StructAtOffset<Subrs> (cff2, cff2->topDict + cff2->topDictSize);
varStore = &top.vstoreOffset (cff2);
charStrings = &top.charStringsOffset (cff2);
fdArray = &top.FDArrayOffset (cff2);
fdSelect = &top.FDSelectOffset (cff2);
globalSubrs = &StructAtOffset<CFF2Subrs> (cff2, cff2->topDict + cff2->topDictSize);
varStore = &StructAtOffsetOrNull<CFF2VariationStore> (cff2, topDict.vstoreOffset);
charStrings = &StructAtOffsetOrNull<CFF2CharStrings> (cff2, topDict.charStringsOffset);
fdArray = &StructAtOffsetOrNull<CFF2FDArray> (cff2, topDict.FDArrayOffset);
fdSelect = &StructAtOffsetOrNull<CFF2FDSelect> (cff2, topDict.FDSelectOffset);
if (((varStore != &Null(CFF2VariationStore)) && unlikely (!varStore->sanitize (&sc))) ||
((charStrings == &Null(CharStrings)) || unlikely (!charStrings->sanitize (&sc))) ||
((fdArray == &Null(FDArray)) || unlikely (!fdArray->sanitize (&sc))) ||
((fdSelect != &Null(CFF2FDSelect)) && unlikely (!fdSelect->sanitize (&sc, fdArray->count))))
{
fini ();
return;
}
(charStrings == &Null(CFF2CharStrings)) || unlikely (!charStrings->sanitize (&sc)) ||
(fdArray == &Null(CFF2FDArray)) || unlikely (!fdArray->sanitize (&sc)) ||
(((fdSelect != &Null(CFF2FDSelect)) && unlikely (!fdSelect->sanitize (&sc, fdArray->count)))))
{ fini (); return; }
num_glyphs = charStrings->count;
if (num_glyphs != sc.get_num_glyphs ())
{
fini ();
return;
}
{ fini (); return; }
privateDicts.resize (fdArray->count);
@ -500,27 +472,18 @@ struct cff2
font = fontDicts.push ();
if (unlikely (!fontDictStr.sanitize (&sc) ||
!font_interp.interpret (fontDictStr, *font)))
{
fini ();
return;
}
{ fini (); return; }
const ByteStr privDictStr (font->privateDictOffset (cff2), font->privateDictSize);
const ByteStr privDictStr (StructAtOffsetOrNull<UnsizedByteStr> (cff2, font->privateDictInfo.offset), font->privateDictInfo.size);
Interpreter<PrivOpSet, PrivDictVal> priv_interp;
if (unlikely (!privDictStr.sanitize (&sc) ||
!priv_interp.interpret (privDictStr, privateDicts[i])))
{
fini ();
return;
}
{ fini (); return; }
privateDicts[i].localSubrs = &privateDicts[i].subrsOffset (privDictStr.str);
if (privateDicts[i].localSubrs != &Null(Subrs) &&
privateDicts[i].localSubrs = &StructAtOffsetOrNull<CFF2Subrs> (privDictStr.str, privateDicts[i].subrsOffset);
if (privateDicts[i].localSubrs != &Null(CFF2Subrs) &&
unlikely (!privateDicts[i].localSubrs->sanitize (&sc)))
{
fini ();
return;
}
{ fini (); return; }
}
}
@ -550,11 +513,11 @@ struct cff2
hb_sanitize_context_t sc;
public:
CFF2TopDictValues top;
const Subrs *globalSubrs;
CFF2TopDictValues topDict;
const CFF2Subrs *globalSubrs;
const CFF2VariationStore *varStore;
const CharStrings *charStrings;
const FDArray *fdArray;
const CFF2CharStrings *charStrings;
const CFF2FDArray *fdArray;
const CFF2FDSelect *fdSelect;
hb_vector_t<CFF2FontDictValues> fontDicts;

@ -32,6 +32,7 @@
#include "hb-ot-cmap-table.hh"
#include "hb-ot-glyf-table.hh"
#include "hb-ot-cff-table.hh"
#include "hb-ot-cff2-table.hh"
#include "hb-ot-hmtx-table.hh"
#include "hb-ot-kern-table.hh"

@ -41,7 +41,7 @@ using namespace CFF;
bool
hb_plan_subset_cff_fdselect (const hb_vector_t<hb_codepoint_t> &glyphs,
unsigned int fdCount,
const CFF2FDSelect &src, /* IN */
const FDSelect &src, /* IN */
unsigned int &subset_fd_count /* OUT */,
unsigned int &subst_fdselect_size /* OUT */,
unsigned int &subst_fdselect_format /* OUT */,
@ -128,7 +128,7 @@ template <typename FDSELECT3_4>
static inline bool
serialize_fdselect_3_4 (hb_serialize_context_t *c,
unsigned int num_glyphs,
const CFF2FDSelect &src,
const FDSelect &src,
unsigned int size,
const hb_vector_t<hb_codepoint_t> &first_glyphs,
const hb_vector_t<hb_codepoint_t> &fdmap)
@ -154,7 +154,7 @@ serialize_fdselect_3_4 (hb_serialize_context_t *c,
bool
hb_serialize_cff_fdselect (hb_serialize_context_t *c,
const hb_vector_t<hb_codepoint_t> &glyphs,
const CFF2FDSelect &src,
const FDSelect &src,
unsigned int fd_count,
unsigned int fdselect_format,
unsigned int size,

@ -34,7 +34,7 @@
HB_INTERNAL bool
hb_plan_subset_cff_fdselect (const hb_vector_t<hb_codepoint_t> &glyphs,
unsigned int fdCount,
const CFF::CFF2FDSelect &src, /* IN */
const CFF::FDSelect &src, /* IN */
unsigned int &subset_fd_count /* OUT */,
unsigned int &subst_fdselect_size /* OUT */,
unsigned int &subst_fdselect_format /* OUT */,
@ -44,7 +44,7 @@ hb_plan_subset_cff_fdselect (const hb_vector_t<hb_codepoint_t> &glyphs,
HB_INTERNAL bool
hb_serialize_cff_fdselect (hb_serialize_context_t *c,
const hb_vector_t<hb_codepoint_t> &glyphs,
const CFF::CFF2FDSelect &src,
const CFF::FDSelect &src,
unsigned int fd_count,
unsigned int fdselect_format,
unsigned int size,

@ -0,0 +1,560 @@
/*
* Copyright © 2018 Adobe Systems Incorporated.
*
* 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.
*
* Adobe Author(s): Michiharu Ariza
*/
#include "hb-open-type-private.hh"
#include "hb-ot-cff-table.hh"
#include "hb-set.h"
#include "hb-subset-cff.hh"
#include "hb-subset-plan.hh"
#include "hb-subset-cff-common-private.hh"
using namespace CFF;
struct CFFSubTableOffsets {
inline CFFSubTableOffsets (void)
{
memset (this, 0, sizeof(*this));
}
unsigned int nameIndexOffset;
unsigned int topDictOffset;
unsigned int topDictOffSize;
unsigned int stringIndexOffset;
unsigned int globalSubrsOffset;
unsigned int encodingOffset;
unsigned int charsetOffset;
TableInfo FDSelectInfo;
unsigned int FDArrayOffset;
unsigned int FDArrayOffSize;
unsigned int charStringsOffset;
unsigned int charStringsOffSize;
TableInfo privateDictInfo;
};
struct CFFTopDict_OpSerializer : OpSerializer
{
inline bool serialize (hb_serialize_context_t *c,
const OpStr &opstr,
const CFFSubTableOffsets &offsets) const
{
TRACE_SERIALIZE (this);
switch (opstr.op)
{
case OpCode_charset:
return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.charsetOffset));
case OpCode_Encoding:
return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.encodingOffset));
case OpCode_CharStrings:
return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.charStringsOffset));
case OpCode_FDArray:
return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.FDArrayOffset));
case OpCode_FDSelect:
return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.FDSelectInfo.offset));
case OpCode_Private:
{
if (unlikely (!UnsizedByteStr::serialize_int2 (c, offsets.privateDictInfo.size)))
return_trace (false);
if (unlikely (!UnsizedByteStr::serialize_int4 (c, offsets.privateDictInfo.offset)))
return_trace (false);
HBUINT8 *p = c->allocate_size<HBUINT8> (1);
if (unlikely (p == nullptr)) return_trace (false);
p->set (OpCode_Private);
}
break;
default:
return_trace (copy_opstr (c, opstr));
}
return_trace (true);
}
inline unsigned int calculate_serialized_size (const OpStr &opstr) const
{
switch (opstr.op)
{
case OpCode_charset:
case OpCode_Encoding:
case OpCode_CharStrings:
case OpCode_FDArray:
case OpCode_FDSelect:
return OpCode_Size (OpCode_longint) + 4 + OpCode_Size (opstr.op);
case OpCode_Private:
return OpCode_Size (OpCode_longint) + 4 + OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Private);
default:
return opstr.str.len;
}
}
};
struct CFFFontDict_OpSerializer : OpSerializer
{
inline bool serialize (hb_serialize_context_t *c,
const OpStr &opstr,
const TableInfo &privateDictInfo) const
{
TRACE_SERIALIZE (this);
if (opstr.op == OpCode_Private)
{
/* serialize the private dict size as a 2-byte integer */
if (unlikely (!UnsizedByteStr::serialize_int2 (c, privateDictInfo.size)))
return_trace (false);
/* serialize the private dict offset as a 4-byte integer */
if (unlikely (!UnsizedByteStr::serialize_int4 (c, privateDictInfo.offset)))
return_trace (false);
/* serialize the opcode */
HBUINT8 *p = c->allocate_size<HBUINT8> (1);
if (unlikely (p == nullptr)) return_trace (false);
p->set (OpCode_Private);
return_trace (true);
}
else
{
HBUINT8 *d = c->allocate_size<HBUINT8> (opstr.str.len);
if (unlikely (d == nullptr)) return_trace (false);
memcpy (d, &opstr.str.str[0], opstr.str.len);
}
return_trace (true);
}
inline unsigned int calculate_serialized_size (const OpStr &opstr) const
{
if (opstr.op == OpCode_Private)
return OpCode_Size (OpCode_longint) + 4 + OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Private);
else
return opstr.str.len;
}
};
struct CFFPrivateDict_OpSerializer : OpSerializer
{
inline bool serialize (hb_serialize_context_t *c,
const OpStr &opstr,
const unsigned int subrsOffset) const
{
TRACE_SERIALIZE (this);
if (opstr.op == OpCode_Subrs)
return_trace (FontDict::serialize_offset2_op(c, OpCode_Subrs, subrsOffset));
else
return_trace (copy_opstr (c, opstr));
}
inline unsigned int calculate_serialized_size (const OpStr &opstr) const
{
if (opstr.op == OpCode_Subrs)
return OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Subrs);
else
return opstr.str.len;
}
};
struct cff_subset_plan {
inline cff_subset_plan (void)
: final_size (0),
orig_fdcount (0),
subst_fdcount(1),
subst_fdselect_format (0)
{
topdict_sizes.init ();
topdict_sizes.resize (1);
subst_fdselect_first_glyphs.init ();
fdmap.init ();
subset_charstrings.init ();
privateDictInfos.init ();
}
inline ~cff_subset_plan (void)
{
topdict_sizes.fini ();
subst_fdselect_first_glyphs.fini ();
fdmap.fini ();
subset_charstrings.fini ();
privateDictInfos.fini ();
}
inline bool create (const OT::cff::accelerator_subset_t &acc,
hb_subset_plan_t *plan)
{
final_size = 0;
orig_fdcount = acc.fdCount;
/* CFF header */
final_size += OT::cff::static_size;
/* Name INDEX */
offsets.nameIndexOffset = final_size;
final_size += acc.nameIndex->get_size ();
/* top dict INDEX */
{
offsets.topDictOffset = final_size;
CFFTopDict_OpSerializer topSzr;
unsigned int topDictSize = TopDict::calculate_serialized_size (acc.topDicts[0], topSzr);
offsets.topDictOffSize = calcOffSize(topDictSize);
final_size += CFFIndexOf<TopDict>::calculate_serialized_size<CFFTopDictValues> (offsets.topDictOffSize, acc.topDicts, topdict_sizes, topSzr);
}
/* String INDEX */
offsets.stringIndexOffset = final_size;
final_size += acc.stringIndex->get_size ();
/* global subrs */
offsets.globalSubrsOffset = final_size;
final_size += acc.globalSubrs->get_size ();
/* Encoding */
offsets.encodingOffset = final_size;
if (acc.encoding != &Null(Encoding))
final_size += acc.encoding->get_size ();
/* Charset */
offsets.charsetOffset = final_size;
if (acc.charset != &Null(Charset))
final_size += acc.charset->get_size (acc.num_glyphs);
/* FDSelect */
if (acc.fdSelect != &Null(CFFFDSelect))
{
offsets.FDSelectInfo.offset = final_size;
if (unlikely (!hb_plan_subset_cff_fdselect (plan->glyphs,
orig_fdcount,
*acc.fdSelect,
subst_fdcount,
offsets.FDSelectInfo.size,
subst_fdselect_format,
subst_fdselect_first_glyphs,
fdmap)))
return false;
if (!is_fds_subsetted ())
offsets.FDSelectInfo.size = acc.fdSelect->calculate_serialized_size (acc.num_glyphs);
final_size += offsets.FDSelectInfo.size;
}
/* FDArray (FDIndex) */
if (acc.fdArray != &Null(CFFFDArray)) {
offsets.FDArrayOffset = final_size;
CFFFontDict_OpSerializer fontSzr;
final_size += CFFFDArray::calculate_serialized_size(offsets.FDArrayOffSize/*OUT*/, acc.fontDicts, subst_fdcount, fdmap, fontSzr);
}
/* CharStrings */
{
offsets.charStringsOffset = final_size;
unsigned int dataSize = 0;
for (unsigned int i = 0; i < plan->glyphs.len; i++)
{
const ByteStr str = (*acc.charStrings)[plan->glyphs[i]];
subset_charstrings.push (str);
dataSize += str.len;
}
offsets.charStringsOffSize = calcOffSize (dataSize + 1);
final_size += CFFCharStrings::calculate_serialized_size (offsets.charStringsOffSize, plan->glyphs.len, dataSize);
}
/* private dicts & local subrs */
offsets.privateDictInfo.offset = final_size;
for (unsigned int i = 0; i < orig_fdcount; i++)
{
CFFPrivateDict_OpSerializer privSzr;
TableInfo privInfo = { final_size, PrivateDict::calculate_serialized_size (acc.privateDicts[i], privSzr) };
privateDictInfos.push (privInfo);
final_size += privInfo.size + acc.privateDicts[i].localSubrs->get_size ();
}
if (!acc.is_CID ())
offsets.privateDictInfo = privateDictInfos[0];
return true;
}
inline unsigned int get_final_size (void) const { return final_size; }
unsigned int final_size;
hb_vector_t<unsigned int> topdict_sizes;
CFFSubTableOffsets offsets;
unsigned int orig_fdcount;
unsigned int subst_fdcount;
inline bool is_fds_subsetted (void) const { return subst_fdcount < orig_fdcount; }
unsigned int subst_fdselect_format;
hb_vector_t<hb_codepoint_t> subst_fdselect_first_glyphs;
/* font dict index remap table from fullset FDArray to subset FDArray.
* set to HB_SET_VALUE_INVALID if excluded from subset */
hb_vector_t<hb_codepoint_t> fdmap;
hb_vector_t<ByteStr> subset_charstrings;
hb_vector_t<TableInfo> privateDictInfos;
};
static inline bool _write_cff (const cff_subset_plan &plan,
const OT::cff::accelerator_subset_t &acc,
const hb_vector_t<hb_codepoint_t>& glyphs,
unsigned int dest_sz,
void *dest)
{
hb_serialize_context_t c (dest, dest_sz);
OT::cff *cff = c.start_serialize<OT::cff> ();
if (unlikely (!c.extend_min (*cff)))
return false;
/* header */
cff->version.major.set (0x01);
cff->version.minor.set (0x00);
cff->nameIndex.set (cff->min_size);
cff->offSize.set (4); /* unused? */
/* name INDEX */
{
assert (cff->nameIndex == c.head - c.start);
NameIndex *dest = c.start_embed<NameIndex> ();
if (unlikely (dest == nullptr)) return false;
if (unlikely (!dest->serialize (&c, *acc.nameIndex)))
{
DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF name INDEX");
return false;
}
}
/* top dict INDEX */
{
assert (plan.offsets.topDictOffset == c.head - c.start);
CFFIndexOf<TopDict> *dest = c.start_embed< CFFIndexOf<TopDict> > ();
if (dest == nullptr) return false;
CFFTopDict_OpSerializer topSzr;
if (unlikely (!dest->serialize (&c, plan.offsets.topDictOffSize, acc.topDicts, plan.topdict_sizes, topSzr, plan.offsets)))
{
DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF top dict");
return false;
}
}
/* String INDEX */
{
assert (plan.offsets.stringIndexOffset == c.head - c.start);
StringIndex *dest = c.start_embed<StringIndex> ();
if (unlikely (dest == nullptr)) return false;
if (unlikely (!dest->serialize (&c, *acc.stringIndex)))
{
DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF string INDEX");
return false;
}
}
/* global subrs */
{
assert (plan.offsets.globalSubrsOffset == c.head - c.start);
CFFSubrs *dest = c.start_embed<CFFSubrs> ();
if (unlikely (dest == nullptr)) return false;
if (unlikely (!dest->serialize (&c, *acc.globalSubrs)))
{
DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF global subrs");
return false;
}
}
/* Encoding */
if (acc.encoding != &Null(Encoding)){
assert (plan.offsets.encodingOffset == c.head - c.start);
Encoding *dest = c.start_embed<Encoding> ();
if (unlikely (dest == nullptr)) return false;
if (unlikely (!dest->serialize (&c, *acc.encoding, acc.num_glyphs))) // XXX: TODO
{
DEBUG_MSG (SUBSET, nullptr, "failed to serialize Encoding");
return false;
}
}
/* Charset */
if (acc.charset != &Null(Charset))
{
assert (plan.offsets.charsetOffset == c.head - c.start);
Charset *dest = c.start_embed<Charset> ();
if (unlikely (dest == nullptr)) return false;
if (unlikely (!dest->serialize (&c, *acc.charset, acc.num_glyphs))) // XXX: TODO
{
DEBUG_MSG (SUBSET, nullptr, "failed to serialize Charset");
return false;
}
}
/* FDSelect */
if (acc.fdSelect != &Null(CFFFDSelect))
{
assert (plan.offsets.FDSelectInfo.offset == c.head - c.start);
if (plan.is_fds_subsetted ())
{
if (unlikely (!hb_serialize_cff_fdselect (&c, glyphs, *acc.fdSelect, acc.fdCount,
plan.subst_fdselect_format, plan.offsets.FDSelectInfo.size,
plan.subst_fdselect_first_glyphs,
plan.fdmap)))
{
DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF subset FDSelect");
return false;
}
}
else
{
CFFFDSelect *dest = c.start_embed<CFFFDSelect> ();
if (unlikely (!dest->serialize (&c, *acc.fdSelect, acc.num_glyphs)))
{
DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF FDSelect");
return false;
}
}
}
/* FDArray (FD Index) */
if (acc.fdArray != &Null(CFFFDArray))
{
assert (plan.offsets.FDArrayOffset == c.head - c.start);
CFFFDArray *fda = c.start_embed<CFFFDArray> ();
if (unlikely (fda == nullptr)) return false;
CFFFontDict_OpSerializer fontSzr;
if (unlikely (!fda->serialize (&c, plan.offsets.FDArrayOffSize,
acc.fontDicts, plan.subst_fdcount, plan.fdmap,
fontSzr, plan.privateDictInfos)))
{
DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF FDArray");
return false;
}
}
/* CharStrings */
{
assert (plan.offsets.charStringsOffset == c.head - c.start);
CFFCharStrings *cs = c.start_embed<CFFCharStrings> ();
if (unlikely (cs == nullptr)) return false;
if (unlikely (!cs->serialize (&c, plan.offsets.charStringsOffSize, plan.subset_charstrings)))
{
DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF CharStrings");
return false;
}
}
/* private dicts & local subrs */
assert (plan.offsets.privateDictInfo.offset == c.head - c.start);
for (unsigned int i = 0; i < acc.privateDicts.len; i++)
{
PrivateDict *pd = c.start_embed<PrivateDict> ();
if (unlikely (pd == nullptr)) return false;
CFFPrivateDict_OpSerializer privSzr;
/* N.B. local subrs immediately follows its corresponding private dict. i.e., subr offset == private dict size */
if (unlikely (!pd->serialize (&c, acc.privateDicts[i], privSzr, plan.privateDictInfos[i].size)))
{
DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF Private Dict[%d]", i);
return false;
}
if (acc.privateDicts[i].subrsOffset != 0)
{
CFFSubrs *subrs = c.start_embed<CFFSubrs> ();
if (unlikely (subrs == nullptr) || acc.privateDicts[i].localSubrs == &Null(CFFSubrs))
{
DEBUG_MSG (SUBSET, nullptr, "CFF subset: local subrs unexpectedly null [%d]", i);
return false;
}
if (unlikely (!subrs->serialize (&c, *acc.privateDicts[i].localSubrs)))
{
DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF local subrs [%d]", i);
return false;
}
}
}
c.end_serialize ();
return true;
}
static bool
_hb_subset_cff (const OT::cff::accelerator_subset_t &acc,
const char *data,
hb_subset_plan_t *plan,
hb_blob_t **prime /* OUT */)
{
cff_subset_plan cff_plan;
if (unlikely (!cff_plan.create (acc, plan)))
{
DEBUG_MSG(SUBSET, nullptr, "Failed to generate a cff subsetting plan.");
return false;
}
unsigned int cff_prime_size = cff_plan.get_final_size ();
char *cff_prime_data = (char *) calloc (1, cff_prime_size);
if (unlikely (!_write_cff (cff_plan, acc, plan->glyphs,
cff_prime_size, cff_prime_data))) {
DEBUG_MSG(SUBSET, nullptr, "Failed to write a subset cff.");
free (cff_prime_data);
return false;
}
*prime = hb_blob_create (cff_prime_data,
cff_prime_size,
HB_MEMORY_MODE_READONLY,
cff_prime_data,
free);
return true;
}
/**
* hb_subset_cff:
* Subsets the CFF table according to a provided plan.
*
* Return value: subsetted cff table.
**/
bool
hb_subset_cff (hb_subset_plan_t *plan,
hb_blob_t **prime /* OUT */)
{
hb_blob_t *cff_blob = hb_sanitize_context_t().reference_table<CFF::cff> (plan->source);
const char *data = hb_blob_get_data(cff_blob, nullptr);
OT::cff::accelerator_subset_t acc;
acc.init(plan->source);
bool result = likely (acc.is_valid ()) &&
_hb_subset_cff (acc, data, plan, prime);
hb_blob_destroy (cff_blob);
acc.fini ();
return result;
}

@ -0,0 +1,38 @@
/*
* Copyright © 2018 Adobe Systems Incorporated.
*
* 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.
*
* Adobe Author(s): Michiharu Ariza
*/
#ifndef HB_SUBSET_CFF_HH
#define HB_SUBSET_CFF_HH
#include "hb-private.hh"
#include "hb-subset-plan.hh"
HB_INTERNAL bool
hb_subset_cff (hb_subset_plan_t *plan,
hb_blob_t **cff_prime /* OUT */);
#endif /* HB_SUBSET_CFF_HH */

@ -33,11 +33,27 @@
using namespace CFF;
struct CFF2SubTableOffsets {
inline CFF2SubTableOffsets (void)
{
memset (this, 0, sizeof(*this));
}
unsigned int topDictSize;
unsigned int varStoreOffset;
TableInfo FDSelectInfo;
unsigned int FDArrayOffset;
unsigned int FDArrayOffSize;
unsigned int charStringsOffset;
unsigned int charStringsOffSize;
unsigned int privateDictsOffset;
};
struct CFF2TopDict_OpSerializer : OpSerializer
{
inline bool serialize (hb_serialize_context_t *c,
const OpStr &opstr,
const SubTableOffsets &offsets) const
const CFF2SubTableOffsets &offsets) const
{
TRACE_SERIALIZE (this);
@ -53,7 +69,7 @@ struct CFF2TopDict_OpSerializer : OpSerializer
return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.FDArrayOffset));
case OpCode_FDSelect:
return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.FDSelectOffset));
return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.FDSelectInfo.offset));
default:
return_trace (copy_opstr (c, opstr));
@ -81,18 +97,18 @@ struct CFF2FontDict_OpSerializer : OpSerializer
{
inline bool serialize (hb_serialize_context_t *c,
const OpStr &opstr,
const offset_size_pair& privOffSize) const
const TableInfo& privDictInfo) const
{
TRACE_SERIALIZE (this);
if (opstr.op == OpCode_Private)
{
/* serialize the private dict size as a 2-byte integer */
if (unlikely (!UnsizedByteStr::serialize_int2 (c, privOffSize.size)))
if (unlikely (!UnsizedByteStr::serialize_int2 (c, privDictInfo.size)))
return_trace (false);
/* serialize the private dict offset as a 4-byte integer */
if (unlikely (!UnsizedByteStr::serialize_int4 (c, privOffSize.offset)))
if (unlikely (!UnsizedByteStr::serialize_int4 (c, privDictInfo.offset)))
return_trace (false);
/* serialize the opcode */
@ -143,8 +159,8 @@ struct CFF2PrivateDict_OpSerializer : OpSerializer
}
};
struct subset_plan {
inline subset_plan (void)
struct cff2_subset_plan {
inline cff2_subset_plan (void)
: final_size (0),
orig_fdcount (0),
subst_fdcount(1),
@ -153,15 +169,15 @@ struct subset_plan {
subst_fdselect_first_glyphs.init ();
fdmap.init ();
subset_charstrings.init ();
private_off_and_size_pairs.init ();
privateDictInfos.init ();
}
inline ~subset_plan (void)
inline ~cff2_subset_plan (void)
{
subst_fdselect_first_glyphs.fini ();
fdmap.fini ();
subset_charstrings.fini ();
private_off_and_size_pairs.fini ();
privateDictInfos.fini ();
}
inline bool create (const OT::cff2::accelerator_subset_t &acc,
@ -176,7 +192,7 @@ struct subset_plan {
/* top dict */
{
CFF2TopDict_OpSerializer topSzr;
offsets.topDictSize = TopDict::calculate_serialized_size (acc.top, topSzr);
offsets.topDictSize = TopDict::calculate_serialized_size (acc.topDict, topSzr);
final_size += offsets.topDictSize;
}
@ -193,27 +209,27 @@ struct subset_plan {
/* FDSelect */
if (acc.fdSelect != &Null(CFF2FDSelect))
{
offsets.FDSelectOffset = final_size;
offsets.FDSelectInfo.offset = final_size;
if (unlikely (!hb_plan_subset_cff_fdselect (plan->glyphs,
orig_fdcount,
*acc.fdSelect,
*(const FDSelect *)acc.fdSelect,
subst_fdcount,
offsets.FDSelectSize,
offsets.FDSelectInfo.size,
subst_fdselect_format,
subst_fdselect_first_glyphs,
fdmap)))
return false;
if (!is_fds_subsetted ())
offsets.FDSelectSize = acc.fdSelect->calculate_serialized_size (acc.num_glyphs);
final_size += offsets.FDSelectSize;
offsets.FDSelectInfo.size = acc.fdSelect->calculate_serialized_size (acc.num_glyphs);
final_size += offsets.FDSelectInfo.size;
}
/* FDArray (FDIndex) */
{
offsets.FDArrayOffset = final_size;
CFF2FontDict_OpSerializer fontSzr;
final_size += FDArray::calculate_serialized_size(offsets.FDArrayOffSize/*OUT*/, acc.fontDicts, subst_fdcount, fdmap, fontSzr);
final_size += CFF2FDArray::calculate_serialized_size(offsets.FDArrayOffSize/*OUT*/, acc.fontDicts, subst_fdcount, fdmap, fontSzr);
}
/* CharStrings */
@ -227,7 +243,7 @@ struct subset_plan {
dataSize += str.len;
}
offsets.charStringsOffSize = calcOffSize (dataSize + 1);
final_size += CharStrings::calculate_serialized_size (offsets.charStringsOffSize, plan->glyphs.len, dataSize);
final_size += CFF2CharStrings::calculate_serialized_size (offsets.charStringsOffSize, plan->glyphs.len, dataSize);
}
/* private dicts & local subrs */
@ -235,10 +251,9 @@ struct subset_plan {
for (unsigned int i = 0; i < orig_fdcount; i++)
{
CFF2PrivateDict_OpSerializer privSzr;
unsigned int private_size = PrivateDict::calculate_serialized_size (acc.privateDicts[i], privSzr);
offset_size_pair pair = { final_size, private_size };
private_off_and_size_pairs.push (pair);
final_size += private_size + acc.privateDicts[i].localSubrs->get_size ();
TableInfo privInfo = { final_size, PrivateDict::calculate_serialized_size (acc.privateDicts[i], privSzr) };
privateDictInfos.push (privInfo);
final_size += privInfo.size + acc.privateDicts[i].localSubrs->get_size ();
}
return true;
@ -247,7 +262,7 @@ struct subset_plan {
inline unsigned int get_final_size (void) const { return final_size; }
unsigned int final_size;
SubTableOffsets offsets;
CFF2SubTableOffsets offsets;
unsigned int orig_fdcount;
unsigned int subst_fdcount;
@ -260,10 +275,10 @@ struct subset_plan {
hb_vector_t<hb_codepoint_t> fdmap;
hb_vector_t<ByteStr> subset_charstrings;
hb_vector_t<offset_size_pair> private_off_and_size_pairs;
hb_vector_t<TableInfo> privateDictInfos;
};
static inline bool _write_cff2 (const subset_plan &plan,
static inline bool _write_cff2 (const cff2_subset_plan &plan,
const OT::cff2::accelerator_subset_t &acc,
const hb_vector_t<hb_codepoint_t>& glyphs,
unsigned int dest_sz,
@ -286,7 +301,7 @@ static inline bool _write_cff2 (const subset_plan &plan,
cff2->topDictSize.set (plan.offsets.topDictSize);
TopDict &dict = cff2 + cff2->topDict;
CFF2TopDict_OpSerializer topSzr;
if (unlikely (!dict.serialize (&c, acc.top, topSzr, plan.offsets)))
if (unlikely (!dict.serialize (&c, acc.topDict, topSzr, plan.offsets)))
{
DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 top dict");
return false;
@ -296,7 +311,7 @@ static inline bool _write_cff2 (const subset_plan &plan,
/* global subrs */
{
assert (cff2->topDict + plan.offsets.topDictSize == c.head - c.start);
Subrs *dest = c.start_embed<Subrs> ();
CFF2Subrs *dest = c.start_embed<CFF2Subrs> ();
if (unlikely (dest == nullptr)) return false;
if (unlikely (!dest->serialize (&c, *acc.globalSubrs)))
{
@ -320,12 +335,12 @@ static inline bool _write_cff2 (const subset_plan &plan,
/* FDSelect */
if (acc.fdSelect != &Null(CFF2FDSelect))
{
assert (plan.offsets.FDSelectOffset == c.head - c.start);
assert (plan.offsets.FDSelectInfo.offset == c.head - c.start);
if (plan.is_fds_subsetted ())
{
if (unlikely (!hb_serialize_cff_fdselect (&c, glyphs, *acc.fdSelect, acc.fdArray->count,
plan.subst_fdselect_format, plan.offsets.FDSelectSize,
if (unlikely (!hb_serialize_cff_fdselect (&c, glyphs, *(const FDSelect *)acc.fdSelect, acc.fdArray->count,
plan.subst_fdselect_format, plan.offsets.FDSelectInfo.size,
plan.subst_fdselect_first_glyphs,
plan.fdmap)))
{
@ -347,12 +362,12 @@ static inline bool _write_cff2 (const subset_plan &plan,
/* FDArray (FD Index) */
{
assert (plan.offsets.FDArrayOffset == c.head - c.start);
FDArray *fda = c.start_embed<FDArray> ();
CFF2FDArray *fda = c.start_embed<CFF2FDArray> ();
if (unlikely (fda == nullptr)) return false;
CFF2FontDict_OpSerializer fontSzr;
if (unlikely (!fda->serialize (&c, plan.offsets.FDArrayOffSize,
acc.fontDicts, plan.subst_fdcount, plan.fdmap,
fontSzr, plan.private_off_and_size_pairs)))
fontSzr, plan.privateDictInfos)))
{
DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 FDArray");
return false;
@ -362,7 +377,7 @@ static inline bool _write_cff2 (const subset_plan &plan,
/* CharStrings */
{
assert (plan.offsets.charStringsOffset == c.head - c.start);
CharStrings *cs = c.start_embed<CharStrings> ();
CFF2CharStrings *cs = c.start_embed<CFF2CharStrings> ();
if (unlikely (cs == nullptr)) return false;
if (unlikely (!cs->serialize (&c, plan.offsets.charStringsOffSize, plan.subset_charstrings)))
{
@ -379,15 +394,15 @@ static inline bool _write_cff2 (const subset_plan &plan,
if (unlikely (pd == nullptr)) return false;
CFF2PrivateDict_OpSerializer privSzr;
/* N.B. local subrs immediately follows its corresponding private dict. i.e., subr offset == private dict size */
if (unlikely (!pd->serialize (&c, acc.privateDicts[i], privSzr, plan.private_off_and_size_pairs[i].size)))
if (unlikely (!pd->serialize (&c, acc.privateDicts[i], privSzr, plan.privateDictInfos[i].size)))
{
DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 Private Dict[%d]", i);
return false;
}
if (acc.privateDicts[i].subrsOffset != 0)
{
Subrs *subrs = c.start_embed<Subrs> ();
if (unlikely (subrs == nullptr) || acc.privateDicts[i].localSubrs == &Null(Subrs))
CFF2Subrs *subrs = c.start_embed<CFF2Subrs> ();
if (unlikely (subrs == nullptr) || acc.privateDicts[i].localSubrs == &Null(CFF2Subrs))
{
DEBUG_MSG (SUBSET, nullptr, "CFF2 subset: local subrs unexpectedly null [%d]", i);
return false;
@ -411,18 +426,18 @@ _hb_subset_cff2 (const OT::cff2::accelerator_subset_t &acc,
hb_subset_plan_t *plan,
hb_blob_t **prime /* OUT */)
{
subset_plan cff2_subset_plan;
cff2_subset_plan cff2_plan;
if (unlikely (!cff2_subset_plan.create (acc, plan)))
if (unlikely (!cff2_plan.create (acc, plan)))
{
DEBUG_MSG(SUBSET, nullptr, "Failed to generate a cff2 subsetting plan.");
return false;
}
unsigned int cff2_prime_size = cff2_subset_plan.get_final_size ();
unsigned int cff2_prime_size = cff2_plan.get_final_size ();
char *cff2_prime_data = (char *) calloc (1, cff2_prime_size);
if (unlikely (!_write_cff2 (cff2_subset_plan, acc, plan->glyphs,
if (unlikely (!_write_cff2 (cff2_plan, acc, plan->glyphs,
cff2_prime_size, cff2_prime_data))) {
DEBUG_MSG(SUBSET, nullptr, "Failed to write a subset cff2.");
free (cff2_prime_data);
@ -453,10 +468,7 @@ hb_subset_cff2 (hb_subset_plan_t *plan,
OT::cff2::accelerator_subset_t acc;
acc.init(plan->source);
bool result = likely (acc.is_valid ()) &&
_hb_subset_cff2 (acc,
data,
plan,
prime);
_hb_subset_cff2 (acc, data, plan, prime);
hb_blob_destroy (cff2_blob);
acc.fini ();

@ -41,6 +41,7 @@
#include "hb-ot-maxp-table.hh"
#include "hb-ot-os2-table.hh"
#include "hb-ot-post-table.hh"
#include "hb-ot-cff-table.hh"
#include "hb-ot-cff2-table.hh"
@ -271,6 +272,9 @@ _subset_table (hb_subset_plan_t *plan,
case HB_OT_TAG_post:
result = _subset<const OT::post> (plan);
break;
case HB_OT_TAG_cff:
result = _subset<const OT::cff> (plan);
break;
case HB_OT_TAG_cff2:
result = _subset<const OT::cff2> (plan);
break;

Loading…
Cancel
Save