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 fixespull/1113/head
parent
161ece4c30
commit
64c5412264
11 changed files with 1991 additions and 366 deletions
@ -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 */ |
@ -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 */ |
Loading…
Reference in new issue