harfbuzz/src/hb-ot-cff1-table.hh

1162 lines
33 KiB
C++

/*
* 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_CFF1_TABLE_HH
#define HB_OT_CFF1_TABLE_HH
#include "hb-ot-cff-common.hh"
#include "hb-subset-cff1.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_cff1 HB_TAG('C','F','F',' ')
#define CFF_UNDEF_CODE 0xFFFFFFFF
enum EncodingID { StandardEncoding = 0, ExpertEncoding = 1 };
enum CharsetID { ISOAdobeCharset = 0, ExpertCharset = 1, ExpertSubsetCharset = 2 };
typedef CFFIndex<HBUINT16> CFF1Index;
template <typename Type> struct CFF1IndexOf : CFFIndexOf<HBUINT16, Type> {};
typedef CFFIndex<HBUINT16> CFF1Index;
typedef CFF1Index CFF1CharStrings;
typedef FDArray<HBUINT16> CFF1FDArray;
typedef Subrs<HBUINT16> CFF1Subrs;
struct CFF1FDSelect : 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 hb_codepoint_t get_code (hb_codepoint_t glyph) const
{
if (glyph < nCodes)
{
return (hb_codepoint_t)codes[glyph];
}
else
return CFF_UNDEF_CODE;
}
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 hb_codepoint_t get_code (hb_codepoint_t glyph) const
{
for (unsigned int i = 0; i < nRanges; i++)
{
if (glyph <= ranges[i].nLeft)
{
return (hb_codepoint_t)ranges[i].first + glyph;
}
glyph -= (ranges[i].nLeft + 1);
}
return CFF_UNDEF_CODE;
}
HBUINT8 nRanges;
Encoding1_Range ranges[VAR];
DEFINE_SIZE_ARRAY (1, ranges);
};
struct SuppEncoding {
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 CFF1SuppEncData {
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 void get_codes (hb_codepoint_t sid, hb_vector_t<hb_codepoint_t> &codes) const
{
for (unsigned int i = 0; i < nSups; i++)
if (sid == supps[i].glyph)
codes.push (supps[i].code);
}
inline unsigned int get_size (void) const
{ return HBUINT8::static_size + SuppEncoding::static_size * nSups; }
HBUINT8 nSups;
SuppEncoding supps[VAR];
DEFINE_SIZE_ARRAY (1, supps);
};
struct code_pair
{
hb_codepoint_t code;
hb_codepoint_t glyph;
};
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));
}
/* serialize a fullset Encoding */
inline bool serialize (hb_serialize_context_t *c, const Encoding &src)
{
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);
}
/* serialize a subset Encoding */
inline bool serialize (hb_serialize_context_t *c,
uint8_t format,
unsigned int enc_count,
const hb_vector_t<code_pair>& code_ranges,
const hb_vector_t<code_pair>& supp_codes)
{
TRACE_SERIALIZE (this);
Encoding *dest = c->extend_min (*this);
if (unlikely (dest == nullptr)) return_trace (false);
dest->format.set (format | ((supp_codes.len > 0)? 0x80: 0));
if (format == 0)
{
Encoding0 *fmt0 = c->allocate_size<Encoding0> (Encoding0::min_size + HBUINT8::static_size * enc_count);
if (unlikely (fmt0 == nullptr)) return_trace (false);
fmt0->nCodes.set (enc_count);
unsigned int glyph = 0;
for (unsigned int i = 0; i < code_ranges.len; i++)
{
hb_codepoint_t code = code_ranges[i].code;
for (int left = (int)code_ranges[i].glyph; left >= 0; left--)
fmt0->codes[glyph++].set (code++);
assert ((glyph <= 0x100) && (code <= 0x100));
}
}
else
{
Encoding1 *fmt1 = c->allocate_size<Encoding1> (Encoding1::min_size + Encoding1_Range::static_size * code_ranges.len);
if (unlikely (fmt1 == nullptr)) return_trace (false);
fmt1->nRanges.set (code_ranges.len);
for (unsigned int i = 0; i < code_ranges.len; i++)
{
assert ((code_ranges[i].code <= 0xFF) && (code_ranges[i].glyph <= 0xFF));
fmt1->ranges[i].first.set (code_ranges[i].code);
fmt1->ranges[i].nLeft.set (code_ranges[i].glyph);
}
}
if (supp_codes.len > 0)
{
CFF1SuppEncData *suppData = c->allocate_size<CFF1SuppEncData> (CFF1SuppEncData::min_size + SuppEncoding::static_size * supp_codes.len);
if (unlikely (suppData == nullptr)) return_trace (false);
suppData->nSups.set (supp_codes.len);
for (unsigned int i = 0; i < supp_codes.len; i++)
{
suppData->supps[i].code.set (supp_codes[i].code);
suppData->supps[i].glyph.set (supp_codes[i].glyph); /* actually SID */
}
}
return_trace (true);
}
/* parallel to above: calculate the size of a subset Encoding */
static inline unsigned int calculate_serialized_size (
uint8_t format,
unsigned int enc_count,
unsigned int supp_count)
{
unsigned int size = min_size;
if (format == 0)
size += Encoding0::min_size + HBUINT8::static_size * enc_count;
else
size += Encoding1::min_size + Encoding1_Range::static_size * enc_count;
if (supp_count > 0)
size += CFF1SuppEncData::min_size + SuppEncoding::static_size * supp_count;
return size;
}
inline unsigned int get_size (void) const
{
unsigned int size = min_size;
if (table_format () == 0)
size += u.format0.get_size ();
else
size += u.format1.get_size ();
if (has_supplement ())
size += suppEncData ().get_size ();
return size;
}
inline hb_codepoint_t get_code (hb_codepoint_t glyph) const
{
if (table_format () == 0)
return u.format0.get_code (glyph);
else
return u.format1.get_code (glyph);
}
inline uint8_t table_format (void) const { return (format & 0x7F); }
inline bool has_supplement (void) const { return (format & 0x80) != 0; }
inline void get_supplement_codes (hb_codepoint_t sid, hb_vector_t<hb_codepoint_t> &codes) const
{
codes.resize (0);
if (has_supplement ())
suppEncData().get_codes (sid, codes);
}
protected:
inline const CFF1SuppEncData &suppEncData (void) const
{
if ((format & 0x7F) == 0)
return StructAfter<CFF1SuppEncData> (u.format0.codes[u.format0.nCodes-1]);
else
return StructAfter<CFF1SuppEncData> (u.format1.ranges[u.format1.nRanges-1]);
}
public:
HBUINT8 format;
union {
Encoding0 format0;
Encoding1 format1;
} u;
/* CFF1SuppEncData 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 hb_codepoint_t get_sid (hb_codepoint_t glyph) const
{
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));
}
HBUINT16 first;
TYPE nLeft;
DEFINE_SIZE_STATIC (HBUINT16::static_size + TYPE::static_size);
};
template <typename TYPE>
struct Charset1_2 {
inline bool sanitize (hb_sanitize_context_t *c, unsigned int num_glyphs) const
{
TRACE_SANITIZE (this);
if (unlikely (!c->check_struct (this)))
return_trace (false);
num_glyphs--;
for (unsigned int i = 0; num_glyphs > 0; i++)
{
if (unlikely (!ranges[i].sanitize (c) || (num_glyphs < ranges[i].nLeft + 1)))
return_trace (false);
num_glyphs -= (ranges[i].nLeft + 1);
}
return_trace (true);
}
inline hb_codepoint_t get_sid (hb_codepoint_t glyph) const
{
if (glyph == 0) return 0;
glyph--;
for (unsigned int i = 0;; i++)
{
if (glyph <= ranges[i].nLeft)
return (hb_codepoint_t)ranges[i].first + glyph;
glyph -= (ranges[i].nLeft + 1);
}
return 0;
}
inline unsigned int get_size (unsigned int num_glyphs) const
{
unsigned int size = HBUINT8::static_size;
int glyph = (int)num_glyphs;
assert (glyph > 0);
glyph--;
for (unsigned int i = 0; glyph > 0; i++)
{
glyph -= (ranges[i].nLeft + 1);
size += Charset_Range<TYPE>::static_size;
}
return size;
}
Charset_Range<TYPE> ranges[VAR];
DEFINE_SIZE_ARRAY (0, ranges);
};
typedef Charset1_2<HBUINT8> Charset1;
typedef Charset1_2<HBUINT16> Charset2;
typedef Charset_Range<HBUINT8> Charset1_Range;
typedef Charset_Range<HBUINT16> Charset2_Range;
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, c->get_num_glyphs ()));
else if (likely (format == 2))
return_trace (u.format2.sanitize (c, c->get_num_glyphs ()));
else
return_trace (false);
}
/* serialize a fullset Charset */
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);
}
/* serialize a subset Charset */
inline bool serialize (hb_serialize_context_t *c,
uint8_t format,
unsigned int num_glyphs,
const hb_vector_t<code_pair>& sid_ranges)
{
TRACE_SERIALIZE (this);
Charset *dest = c->extend_min (*this);
if (unlikely (dest == nullptr)) return_trace (false);
dest->format.set (format);
if (format == 0)
{
Charset0 *fmt0 = c->allocate_size<Charset0> (Charset0::min_size + HBUINT8::static_size * (num_glyphs - 1));
if (unlikely (fmt0 == nullptr)) return_trace (false);
unsigned int glyph = 0;
for (unsigned int i = 0; i < sid_ranges.len; i++)
{
hb_codepoint_t sid = sid_ranges[i].code;
for (int left = (int)sid_ranges[i].glyph; left >= 0; left--)
fmt0->sids[glyph++].set (sid++);
}
}
else if (format == 1)
{
Charset1 *fmt1 = c->allocate_size<Charset1> (Charset1::min_size + Charset1_Range::static_size * sid_ranges.len);
if (unlikely (fmt1 == nullptr)) return_trace (false);
for (unsigned int i = 0; i < sid_ranges.len; i++)
{
assert (sid_ranges[i].glyph <= 0xFF);
fmt1->ranges[i].first.set (sid_ranges[i].code);
fmt1->ranges[i].nLeft.set (sid_ranges[i].glyph);
}
}
else /* format 2 */
{
Charset2 *fmt2 = c->allocate_size<Charset2> (Charset2::min_size + Charset2_Range::static_size * sid_ranges.len);
if (unlikely (fmt2 == nullptr)) return_trace (false);
for (unsigned int i = 0; i < sid_ranges.len; i++)
{
assert (sid_ranges[i].glyph <= 0xFFFF);
fmt2->ranges[i].first.set (sid_ranges[i].code);
fmt2->ranges[i].nLeft.set (sid_ranges[i].glyph);
}
}
return_trace (true);
}
/* parallel to above: calculate the size of a subset Charset */
static inline unsigned int calculate_serialized_size (
uint8_t format,
unsigned int count)
{
unsigned int size = min_size;
if (format == 0)
size += Charset0::min_size + HBUINT8::static_size * (count - 1);
else if (format == 1)
size += Charset1::min_size + Charset1_Range::static_size * count;
else
size += Charset2::min_size + Charset2_Range::static_size * count;
return size;
}
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 (num_glyphs);
else
size += u.format2.get_size (num_glyphs);
return size;
}
inline hb_codepoint_t get_sid (hb_codepoint_t glyph) const
{
if (format == 0)
return u.format0.get_sid (glyph);
else if (format == 1)
return u.format1.get_sid (glyph);
else
return u.format2.get_sid (glyph);
}
HBUINT8 format;
union {
Charset0 format0;
Charset1 format1;
Charset2 format2;
} u;
DEFINE_SIZE_MIN (1);
};
struct CFF1TopDictValues : 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 < getNumValues (); i++)
{
OpCode op = getValue (i).op;
switch (op)
{
case OpCode_FDSelect:
size += OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (op);
break;
default:
size += TopDictValues::calculate_serialized_op_size (getValue (i));
break;
}
}
return size;
}
unsigned int ros[3]; /* registry, ordering, supplement */
unsigned int cidCount;
unsigned int EncodingOffset;
unsigned int CharsetOffset;
unsigned int FDSelectOffset;
TableInfo privateDictInfo;
};
/* a copy of a parsed out CFF1TopDictValues augmented with additional operators */
struct CFF1TopDictValuesMod : CFF1TopDictValues
{
inline void init (const CFF1TopDictValues *base_= &Null(CFF1TopDictValues))
{
SUPER::init ();
base = base_;
}
inline void fini (void)
{
SUPER::fini ();
}
inline unsigned getNumValues (void) const
{
return base->getNumValues () + SUPER::getNumValues ();
}
inline const OpStr &getValue (unsigned int i) const
{
if (i < base->getNumValues ())
return (*base)[i];
else
return SUPER::values[i - base->getNumValues ()];
}
inline const OpStr &operator [] (unsigned int i) const { return getValue (i); }
protected:
typedef CFF1TopDictValues SUPER;
const CFF1TopDictValues *base;
};
struct CFF1TopDictOpSet : TopDictOpSet
{
static inline bool process_op (OpCode op, NumInterpEnv& env, CFF1TopDictValues& 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:
env.clear_args ();
break;
case OpCode_CIDCount:
if (unlikely (!env.argStack.check_pop_uint (dictval.cidCount)))
return false;
env.clear_args ();
break;
case OpCode_ROS:
if (unlikely (!env.argStack.check_pop_uint (dictval.ros[2]) ||
!env.argStack.check_pop_uint (dictval.ros[1]) ||
!env.argStack.check_pop_uint (dictval.ros[0])))
return false;
env.clear_args ();
break;
case OpCode_Encoding:
if (unlikely (!env.argStack.check_pop_uint (dictval.EncodingOffset)))
return false;
env.clear_args ();
break;
case OpCode_charset:
if (unlikely (!env.argStack.check_pop_uint (dictval.CharsetOffset)))
return false;
env.clear_args ();
break;
case OpCode_FDSelect:
if (unlikely (!env.argStack.check_pop_uint (dictval.FDSelectOffset)))
return false;
env.clear_args ();
break;
case OpCode_Private:
if (unlikely (!env.argStack.check_pop_uint (dictval.privateDictInfo.offset)))
return false;
if (unlikely (!env.argStack.check_pop_uint (dictval.privateDictInfo.size)))
return false;
env.clear_args ();
break;
default:
if (unlikely (!TopDictOpSet::process_op (op, env, dictval)))
return false;
/* Record this operand below if stack is empty, otherwise done */
if (!env.argStack.is_empty ()) return true;
break;
}
dictval.addOp (op, env.substr);
return true;
}
};
struct CFF1FontDictValues : DictValues<OpStr>
{
inline void init (void)
{
DictValues<OpStr>::init ();
privateDictInfo.init ();
}
inline void fini (void)
{
DictValues<OpStr>::fini ();
}
TableInfo privateDictInfo;
};
struct CFF1FontDictOpSet : DictOpSet
{
static inline bool process_op (OpCode op, NumInterpEnv& env, CFF1FontDictValues& dictval)
{
switch (op) {
case OpCode_FontName:
case OpCode_FontMatrix:
case OpCode_PaintType:
env.clear_args ();
break;
case OpCode_Private:
if (unlikely (!env.argStack.check_pop_uint (dictval.privateDictInfo.offset)))
return false;
if (unlikely (!env.argStack.check_pop_uint (dictval.privateDictInfo.size)))
return false;
env.clear_args ();
break;
default:
if (unlikely (!DictOpSet::process_op (op, env)))
return false;
if (!env.argStack.is_empty ()) return true;
break;
}
dictval.addOp (op, env.substr);
return true;
}
};
template <typename VAL>
struct CFF1PrivateDictValues_Base : DictValues<VAL>
{
inline void init (void)
{
DictValues<VAL>::init ();
subrsOffset = 0;
localSubrs = &Null(CFF1Subrs);
}
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>::getNumValues; i++)
if (DictValues<VAL>::getValue (i).op == OpCode_Subrs)
size += OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Subrs);
else
size += DictValues<VAL>::getValue (i).str.len;
return size;
}
unsigned int subrsOffset;
const CFF1Subrs *localSubrs;
};
typedef CFF1PrivateDictValues_Base<OpStr> CFF1PrivateDictValues_Subset;
typedef CFF1PrivateDictValues_Base<NumDictVal> CFF1PrivateDictValues;
struct CFF1PrivateDictOpSet : DictOpSet
{
static inline bool process_op (OpCode op, NumInterpEnv& env, CFF1PrivateDictValues& dictval)
{
NumDictVal 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 (!env.argStack.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 (!env.argStack.check_pop_num (val.single_val)))
return false;
env.clear_args ();
break;
case OpCode_Subrs:
if (unlikely (!env.argStack.check_pop_uint (dictval.subrsOffset)))
return false;
env.clear_args ();
break;
default:
if (unlikely (!DictOpSet::process_op (op, env)))
return false;
if (!env.argStack.is_empty ()) return true;
break;
}
dictval.addOp (op, env.substr, val);
return true;
}
};
struct CFF1PrivateDictOpSet_Subset : DictOpSet
{
static inline bool process_op (OpCode op, NumInterpEnv& env, CFF1PrivateDictValues_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:
env.clear_args ();
break;
case OpCode_Subrs:
if (unlikely (!env.argStack.check_pop_uint (dictval.subrsOffset)))
return false;
env.clear_args ();
break;
default:
if (unlikely (!DictOpSet::process_op (op, env)))
return false;
if (!env.argStack.is_empty ()) return true;
break;
}
dictval.addOp (op, env.substr);
return true;
}
};
typedef DictInterpreter<CFF1TopDictOpSet, CFF1TopDictValues> CFF1TopDict_Interpreter;
typedef DictInterpreter<CFF1FontDictOpSet, CFF1FontDictValues> CFF1FontDict_Interpreter;
typedef DictInterpreter<CFF1PrivateDictOpSet, CFF1PrivateDictValues> CFF1PrivateDict_Interpreter;
typedef CFF1Index CFF1NameIndex;
typedef CFF1IndexOf<TopDict> CFF1TopDictIndex;
typedef CFF1Index CFF1StringIndex;
}; /* namespace CFF */
namespace OT {
using namespace CFF;
struct cff1
{
static const hb_tag_t tableTag = HB_OT_TAG_cff1;
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<cff1> (face);
/* setup for run-time santization */
sc.init (this->blob);
sc.start_processing ();
const OT::cff1 *cff = this->blob->template as<OT::cff1> ();
if (cff == &Null(OT::cff1))
{ fini (); return; }
nameIndex = &cff->nameIndex (cff);
if ((nameIndex == &Null (CFF1NameIndex)) || !nameIndex->sanitize (&sc))
{ fini (); return; }
topDictIndex = &StructAtOffset<CFF1TopDictIndex> (nameIndex, nameIndex->get_size ());
if ((topDictIndex == &Null (CFF1TopDictIndex)) || !topDictIndex->sanitize (&sc) || (topDictIndex->count == 0))
{ fini (); return; }
{ /* parse top dict */
const ByteStr topDictStr = (*topDictIndex)[0];
if (unlikely (!topDictStr.sanitize (&sc))) { fini (); return; }
CFF1TopDict_Interpreter top_interp;
top_interp.env.init (topDictStr);
if (unlikely (!top_interp.interpret (topDicts[0]))) { fini (); return; }
}
encoding = &Null(Encoding);
charset = &StructAtOffsetOrNull<Charset> (cff, topDicts[0].CharsetOffset);
if (unlikely (is_CID () && (charset == &Null(Charset))))
{ fini (); return; }
fdCount = 1;
if (is_CID ())
{
fdArray = &StructAtOffsetOrNull<CFF1FDArray> (cff, topDicts[0].FDArrayOffset);
fdSelect = &StructAtOffsetOrNull<CFF1FDSelect> (cff, topDicts[0].FDSelectOffset);
if (unlikely ((fdArray == &Null(CFF1FDArray)) || !fdArray->sanitize (&sc) ||
(fdSelect == &Null(CFF1FDSelect)) || !fdSelect->sanitize (&sc, fdArray->count)))
{ fini (); return; }
fdCount = fdArray->count;
}
else
{
fdArray = &Null(CFF1FDArray);
fdSelect = &Null(CFF1FDSelect);
if (!is_predef_encoding ())
{
encoding = &StructAtOffsetOrNull<Encoding> (cff, topDicts[0].EncodingOffset);
if ((encoding == &Null (Encoding)) || !encoding->sanitize (&sc))
{ fini (); return; }
}
}
stringIndex = &StructAtOffset<CFF1StringIndex> (topDictIndex, topDictIndex->get_size ());
if ((stringIndex == &Null (CFF1StringIndex)) || !stringIndex->sanitize (&sc))
{ fini (); return; }
globalSubrs = &StructAtOffset<CFF1Subrs> (stringIndex, stringIndex->get_size ());
if ((globalSubrs != &Null (CFF1Subrs)) && !stringIndex->sanitize (&sc))
{ fini (); return; }
charStrings = &StructAtOffsetOrNull<CFF1CharStrings> (cff, topDicts[0].charStringsOffset);
if ((charStrings == &Null(CFF1CharStrings)) || 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];
if (unlikely (!fontDictStr.sanitize (&sc))) { fini (); return; }
CFF1FontDictValues *font;
CFF1FontDict_Interpreter font_interp;
font_interp.env.init (fontDictStr);
font = fontDicts.push ();
if (unlikely (!font_interp.interpret (*font))) { fini (); return; }
PRIVDICTVAL *priv = &privateDicts[i];
const ByteStr privDictStr (StructAtOffset<UnsizedByteStr> (cff, font->privateDictInfo.offset), font->privateDictInfo.size);
if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; }
DictInterpreter<PRIVOPSET, PRIVDICTVAL> priv_interp;
priv_interp.env.init (privDictStr);
if (unlikely (!priv_interp.interpret (*priv))) { fini (); return; }
priv->localSubrs = &StructAtOffsetOrNull<CFF1Subrs> (privDictStr.str, priv->subrsOffset);
if (priv->localSubrs != &Null(CFF1Subrs) &&
unlikely (!priv->localSubrs->sanitize (&sc)))
{ fini (); return; }
}
}
else /* non-CID */
{
CFF1TopDictValues *font = &topDicts[0];
PRIVDICTVAL *priv = &privateDicts[0];
const ByteStr privDictStr (StructAtOffset<UnsizedByteStr> (cff, font->privateDictInfo.offset), font->privateDictInfo.size);
if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; }
DictInterpreter<PRIVOPSET, PRIVDICTVAL> priv_interp;
priv_interp.env.init (privDictStr);
if (unlikely (!priv_interp.interpret (*priv))) { fini (); return; }
priv->localSubrs = &StructAtOffsetOrNull<CFF1Subrs> (privDictStr.str, priv->subrsOffset);
if (priv->localSubrs != &Null(CFF1Subrs) &&
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 is_predef_encoding (void) const { return topDicts[0].EncodingOffset <= ExpertEncoding; }
inline bool is_predef_charset (void) const { return topDicts[0].CharsetOffset <= ExpertSubsetCharset; }
inline hb_codepoint_t glyph_to_code (hb_codepoint_t glyph) const
{
if (encoding != &Null(Encoding))
return encoding->get_code (glyph);
else
{
hb_codepoint_t sid = glyph_to_sid (glyph);
if (sid == 0) return 0;
hb_codepoint_t code = 0;
switch (topDicts[0].EncodingOffset)
{
case StandardEncoding:
code = lookup_standard_encoding (sid);
break;
case ExpertEncoding:
code = lookup_expert_encoding (sid);
break;
default:
break;
}
return code;
}
}
inline hb_codepoint_t glyph_to_sid (hb_codepoint_t glyph) const
{
if (charset != &Null(Charset))
return charset->get_sid (glyph);
else
{
hb_codepoint_t sid = 0;
switch (topDicts[0].CharsetOffset)
{
case ISOAdobeCharset:
if (glyph <= 228 /*zcaron*/) sid = glyph;
break;
case ExpertCharset:
sid = lookup_expert_charset (glyph);
break;
case ExpertSubsetCharset:
sid = lookup_expert_subset_charset (glyph);
break;
default:
break;
}
return sid;
}
}
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 CFF1NameIndex *nameIndex;
const CFF1TopDictIndex *topDictIndex;
const CFF1StringIndex *stringIndex;
const Encoding *encoding;
const Charset *charset;
const CFF1Subrs *globalSubrs;
const CFF1CharStrings *charStrings;
const CFF1FDArray *fdArray;
const CFF1FDSelect *fdSelect;
unsigned int fdCount;
hb_vector_t<CFF1TopDictValues> topDicts;
hb_vector_t<CFF1FontDictValues> fontDicts;
hb_vector_t<PRIVDICTVAL> privateDicts;
unsigned int num_glyphs;
};
typedef accelerator_templ_t<CFF1PrivateDictOpSet, CFF1PrivateDictValues> accelerator_t;
typedef accelerator_templ_t<CFF1PrivateDictOpSet_Subset, CFF1PrivateDictValues_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_cff1 (plan, &cff_prime)) {
success = success && plan->add_table (HB_OT_TAG_cff1, cff_prime);
} else {
success = false;
}
hb_blob_destroy (cff_prime);
return success;
}
protected:
static hb_codepoint_t lookup_standard_encoding (hb_codepoint_t sid);
static hb_codepoint_t lookup_expert_encoding (hb_codepoint_t sid);
static hb_codepoint_t lookup_expert_charset (hb_codepoint_t glyph);
static hb_codepoint_t lookup_expert_subset_charset (hb_codepoint_t glyph);
public:
FixedVersion<HBUINT8> version; /* Version of CFF table. set to 0x0100u */
OffsetTo<CFF1NameIndex, HBUINT8> nameIndex; /* headerSize = Offset to Name INDEX. */
HBUINT8 offSize; /* offset size (unused?) */
public:
DEFINE_SIZE_STATIC (4);
};
} /* namespace OT */
#endif /* HB_OT_CFF1_TABLE_HH */