implented no-desubroutinize with CFF2 along with API test

replaced AdobeVFPrototype.abc.otf with a hinted (maually) & subroutinized copy
replaced expected results as well
This commit is contained in:
Michiharu Ariza 2018-11-07 14:48:37 -08:00
parent 43ee0e4d00
commit 0996c0ff62
12 changed files with 271 additions and 66 deletions

View File

@ -262,12 +262,12 @@ struct DictInterpreter : Interpreter<ENV>
inline bool interpret (PARAM& param)
{
param.init ();
do
while (SUPER::env.substr.avail ())
{
OPSET::process_op (SUPER::env.fetch_op (), SUPER::env, param);
if (unlikely (SUPER::env.in_error ()))
return false;
} while (SUPER::env.substr.avail ());
}
return true;
}

View File

@ -88,7 +88,7 @@ struct CFF2CSInterpEnv : CSInterpEnv<BlendArg, CFF2Subrs>
num_coords = num_coords_;
varStore = acc.varStore;
seen_blend = false;
seen_vsindex = false;
seen_vsindex_ = false;
scalars.init ();
do_blend = (coords != nullptr) && num_coords && (varStore != &Null(CFF2VariationStore));
set_ivs (acc.privateDicts[fd].ivs);
@ -145,18 +145,22 @@ struct CFF2CSInterpEnv : CSInterpEnv<BlendArg, CFF2Subrs>
inline void process_vsindex (void)
{
unsigned int index = argStack.pop_uint ();
if (do_blend)
if (unlikely (seen_vsindex () || seen_blend))
{
if (likely (!seen_vsindex && !seen_blend))
set_ivs (index);
set_error ();
}
seen_vsindex = true;
else
{
set_ivs (index);
}
seen_vsindex_ = true;
}
inline unsigned int get_region_count (void) const { return region_count; }
inline void set_region_count (unsigned int region_count_) { region_count = region_count_; }
inline unsigned int get_ivs (void) const { return ivs; }
inline void set_ivs (unsigned int ivs_) { ivs = ivs_; }
inline bool seen_vsindex (void) const { return seen_vsindex_; }
protected:
inline void blend_arg (BlendArg &arg)
@ -184,7 +188,7 @@ struct CFF2CSInterpEnv : CSInterpEnv<BlendArg, CFF2Subrs>
unsigned int ivs;
hb_vector_t<float> scalars;
bool do_blend;
bool seen_vsindex;
bool seen_vsindex_;
bool seen_blend;
typedef CSInterpEnv<BlendArg, CFF2Subrs> SUPER;

View File

@ -418,14 +418,14 @@ struct TableInfo
struct Remap : hb_vector_t<hb_codepoint_t>
{
inline void init (void)
{ hb_vector_t<hb_codepoint_t>::init (); }
{ SUPER::init (); }
inline void fini (void)
{ hb_vector_t<hb_codepoint_t>::fini (); }
{ SUPER::fini (); }
inline bool reset (unsigned int size)
{
if (unlikely (!hb_vector_t<hb_codepoint_t>::resize (size)))
if (unlikely (!SUPER::resize (size)))
return false;
for (unsigned int i = 0; i < len; i++)
(*this)[i] = CFF_UNDEF_CODE;
@ -436,7 +436,7 @@ struct Remap : hb_vector_t<hb_codepoint_t>
inline bool fullset (void) const
{
for (unsigned int i = 0; i < len; i++)
if (hb_vector_t<hb_codepoint_t>::operator[] (i) == CFF_UNDEF_CODE)
if (SUPER::operator[] (i) == CFF_UNDEF_CODE)
return false;
return true;
}
@ -452,13 +452,13 @@ struct Remap : hb_vector_t<hb_codepoint_t>
if (fullset ())
return i;
else
return hb_vector_t<hb_codepoint_t>::operator[] (i);
return SUPER::operator[] (i);
}
inline hb_codepoint_t &operator[] (hb_codepoint_t i)
{
assert (i < len);
return hb_vector_t<hb_codepoint_t>::operator[] (i);
return SUPER::operator[] (i);
}
inline unsigned int add (unsigned int i)
@ -473,6 +473,9 @@ struct Remap : hb_vector_t<hb_codepoint_t>
protected:
hb_codepoint_t count;
private:
typedef hb_vector_t<hb_codepoint_t> SUPER;
};
template <typename COUNT>
@ -533,7 +536,7 @@ struct FDArray : CFFIndexOf<COUNT, FontDict>
unsigned int offset = 1;
unsigned int fid = 0;
for (unsigned i = 0; i < fontDicts.len; i++)
if (!fdmap.excludes (i))
if (fdmap.includes (i))
{
CFFIndexOf<COUNT, FontDict>::set_offset_at (fid++, offset);
offset += FontDict::calculate_serialized_size (fontDicts[i], opszr);
@ -542,7 +545,7 @@ struct FDArray : CFFIndexOf<COUNT, FontDict>
/* serialize font dicts */
for (unsigned int i = 0; i < fontDicts.len; i++)
if (!fdmap.excludes (i))
if (fdmap.includes (i))
{
FontDict *dict = c->start_embed<FontDict> ();
if (unlikely (!dict->serialize (c, fontDicts[i], opszr, privateInfos[fdmap[i]])))
@ -561,7 +564,7 @@ struct FDArray : CFFIndexOf<COUNT, FontDict>
{
unsigned int dictsSize = 0;
for (unsigned int i = 0; i < fontDicts.len; i++)
if (!fdmap.excludes (i))
if (fdmap.includes (i))
dictsSize += FontDict::calculate_serialized_size (fontDicts[i], opszr);
offSize_ = calcOffSize (dictsSize);
@ -720,4 +723,3 @@ struct Subrs : CFFIndex<COUNT>
} /* namespace CFF */
#endif /* HB_OT_CFF_COMMON_HH */

View File

@ -411,7 +411,7 @@ struct ParsedCStr : ParsedValues<ParsedCSOp>
{
SUPER::init ();
parsed = false;
hint_removed = false;
hint_dropped = false;
has_prefix_ = false;
}
@ -444,15 +444,18 @@ struct ParsedCStr : ParsedValues<ParsedCSOp>
inline bool is_parsed (void) const { return parsed; }
inline void set_parsed (void) { parsed = true; }
inline bool is_hint_removed (void) const { return hint_removed; }
inline void set_hint_removed (void) { hint_removed = true; }
inline bool is_hint_dropped (void) const { return hint_dropped; }
inline void set_hint_dropped (void) { hint_dropped = true; }
inline bool is_vsindex_dropped (void) const { return vsindex_dropped; }
inline void set_vsindex_dropped (void) { vsindex_dropped = true; }
inline bool has_prefix (void) const { return has_prefix_; }
inline OpCode prefix_op (void) const { return prefix_op_; }
inline const Number &prefix_num (void) const { return prefix_num_; }
protected:
bool parsed;
bool hint_removed;
bool hint_dropped;
bool vsindex_dropped;
bool has_prefix_;
OpCode prefix_op_;
Number prefix_num_;
@ -697,10 +700,13 @@ struct SubrSubsetter
closures.global_closure, closures.local_closures[fd],
drop_hints);
bool seen_moveto = false;
bool ends_in_hint = false;
if (drop_hints_in_str (parsed_charstrings[i], param, seen_moveto, ends_in_hint))
parsed_charstrings[i].set_hint_removed ();
DropHintsParam drop;
if (drop_hints_in_str (parsed_charstrings[i], param, drop))
{
parsed_charstrings[i].set_hint_dropped ();
if (drop.vsindex_dropped)
parsed_charstrings[i].set_vsindex_dropped ();
}
}
/* after dropping hints recreate closures of actually used subrs */
@ -764,24 +770,35 @@ struct SubrSubsetter
}
protected:
struct DropHintsParam
{
inline DropHintsParam (void)
: seen_moveto (false),
ends_in_hint (false),
vsindex_dropped (false) {}
bool seen_moveto;
bool ends_in_hint;
bool vsindex_dropped;
};
inline bool drop_hints_in_subr (ParsedCStr &str, unsigned int pos,
ParsedCStrs &subrs, unsigned int subr_num,
const SubrSubsetParam &param, bool &seen_moveto)
const SubrSubsetParam &param, DropHintsParam &drop)
{
bool ends_in_hint = false;
bool has_hint = drop_hints_in_str (subrs[subr_num], param, seen_moveto, ends_in_hint);
drop.ends_in_hint = false;
bool has_hint = drop_hints_in_str (subrs[subr_num], param, drop);
/* if this subr ends with a stem hint (i.e., not a number a potential argument for moveto),
* then this entire subroutine must be a hint. drop its call. */
if (ends_in_hint)
if (drop.ends_in_hint)
str.values[pos].set_drop ();
return has_hint;
}
/* returns true if it sees a hint op before the first moveto */
inline bool drop_hints_in_str (ParsedCStr &str, const SubrSubsetParam &param,
bool &seen_moveto, bool &ends_in_hint)
inline bool drop_hints_in_str (ParsedCStr &str, const SubrSubsetParam &param, DropHintsParam &drop)
{
bool seen_hint = false;
@ -793,25 +810,25 @@ struct SubrSubsetter
case OpCode_callsubr:
has_hint = drop_hints_in_subr (str, pos,
*param.parsed_local_subrs, str.values[pos].subr_num,
param, seen_moveto);
param, drop);
break;
case OpCode_callgsubr:
has_hint = drop_hints_in_subr (str, pos,
*param.parsed_global_subrs, str.values[pos].subr_num,
param, seen_moveto);
param, drop);
break;
case OpCode_rmoveto:
case OpCode_hmoveto:
case OpCode_vmoveto:
seen_moveto = true;
drop.seen_moveto = true;
break;
case OpCode_hintmask:
case OpCode_cntrmask:
if (seen_moveto)
if (drop.seen_moveto)
{
str.values[pos].set_drop ();
break;
@ -826,7 +843,7 @@ struct SubrSubsetter
str.values[pos].set_drop ();
if ((pos + 1 >= str.values.len) /* CFF2 */
|| (str.values[pos + 1].op == OpCode_return))
ends_in_hint = true;
drop.ends_in_hint = true;
break;
default:
@ -837,9 +854,12 @@ struct SubrSubsetter
{
for (int i = pos - 1; i >= 0; i--)
{
if (str.values[i].for_drop ())
ParsedCSOp &csop = str.values[i];
if (csop.for_drop ())
break;
str.values[i].set_drop ();
csop.set_drop ();
if (csop.op == OpCode_vsindexcs)
drop.vsindex_dropped = true;
}
seen_hint |= has_hint;
}
@ -890,7 +910,7 @@ struct SubrSubsetter
encoder.reset ();
/* if a prefix (CFF1 width or CFF2 vsindex) has been removed along with hints,
* re-insert it at the beginning of charstreing */
if (str.has_prefix () && str.is_hint_removed ())
if (str.has_prefix () && str.is_hint_dropped ())
{
encoder.encode_num (str.prefix_num ());
if (str.prefix_op () != OpCode_Invalid)

View File

@ -167,6 +167,68 @@ struct CFF2CSOpSet_Flatten : CFF2CSOpSet<CFF2CSOpSet_Flatten, FlattenParam>
typedef CSOpSet<BlendArg, CFF2CSOpSet_Flatten, CFF2CSOpSet_Flatten, CFF2CSInterpEnv, FlattenParam> CSOPSET;
};
struct CFF2CSOpSet_SubrSubset : CFF2CSOpSet<CFF2CSOpSet_SubrSubset, SubrSubsetParam>
{
static inline void process_op (OpCode op, CFF2CSInterpEnv &env, SubrSubsetParam& param)
{
switch (op) {
case OpCode_return:
param.current_parsed_str->set_parsed ();
env.returnFromSubr ();
param.set_current_str (env);
break;
case OpCode_endchar:
param.current_parsed_str->set_parsed ();
SUPER::process_op (op, env, param);
break;
case OpCode_callsubr:
process_call_subr (op, CSType_LocalSubr, env, param, env.localSubrs, param.local_closure);
break;
case OpCode_callgsubr:
process_call_subr (op, CSType_GlobalSubr, env, param, env.globalSubrs, param.global_closure);
break;
default:
SUPER::process_op (op, env, param);
param.current_parsed_str->add_op (op, env.substr);
break;
}
}
protected:
static inline void process_call_subr (OpCode op, CSType type,
CFF2CSInterpEnv &env, SubrSubsetParam& param,
CFF2BiasedSubrs& subrs, hb_set_t *closure)
{
SubByteStr substr = env.substr;
env.callSubr (subrs, type);
param.current_parsed_str->add_call_op (op, substr, env.context.subr_num);
hb_set_add (closure, env.context.subr_num);
param.set_current_str (env);
}
private:
typedef CFF2CSOpSet<CFF2CSOpSet_SubrSubset, SubrSubsetParam> SUPER;
};
struct CFF2SubrSubsetter : SubrSubsetter<CFF2SubrSubsetter, CFF2Subrs, const OT::cff2::accelerator_subset_t, CFF2CSInterpEnv, CFF2CSOpSet_SubrSubset>
{
static inline void finalize_parsed_str (CFF2CSInterpEnv &env, SubrSubsetParam& param, ParsedCStr &charstring)
{
/* vsindex is inserted at the beginning of the charstring as necessary */
if (env.seen_vsindex ())
{
Number ivs;
ivs.set_int ((int)env.get_ivs ());
charstring.set_prefix (ivs, OpCode_vsindexcs);
}
}
};
struct cff2_subset_plan {
inline cff2_subset_plan (void)
: final_size (0),
@ -179,7 +241,8 @@ struct cff2_subset_plan {
subset_fdselect_ranges.init ();
fdmap.init ();
subset_charstrings.init ();
flat_charstrings.init ();
subset_globalsubrs.init ();
subset_localsubrs.init ();
privateDictInfos.init ();
}
@ -187,8 +250,9 @@ struct cff2_subset_plan {
{
subset_fdselect_ranges.fini ();
fdmap.fini ();
subset_charstrings.fini ();
flat_charstrings.fini_deep ();
subset_charstrings.fini_deep ();
subset_globalsubrs.fini_deep ();
subset_localsubrs.fini_deep ();
privateDictInfos.fini ();
}
@ -211,17 +275,60 @@ struct cff2_subset_plan {
final_size += offsets.topDictInfo.size;
}
if (desubroutinize)
{
/* Flatten global & local subrs */
SubrFlattener<const OT::cff2::accelerator_subset_t, CFF2CSInterpEnv, CFF2CSOpSet_Flatten>
flattener(acc, plan->glyphs, plan->drop_hints);
if (!flattener.flatten (flat_charstrings))
if (!flattener.flatten (subset_charstrings))
return false;
/* no global/local subroutines */
offsets.globalSubrsInfo.size = HBUINT32::static_size; /* count 0 only */
offsets.globalSubrsInfo.size = CFF2Subrs::calculate_serialized_size (1, 0, 0);
}
else
{
/* Subset subrs: collect used subroutines, leaving all unused ones behind */
if (!subr_subsetter.subset (acc, plan->glyphs, plan->drop_hints))
return false;
/* encode charstrings, global subrs, local subrs with new subroutine numbers */
if (!subr_subsetter.encode_charstrings (acc, plan->glyphs, subset_charstrings))
return false;
if (!subr_subsetter.encode_globalsubrs (subset_globalsubrs))
return false;
/* global subrs */
unsigned int dataSize = subset_globalsubrs.total_size ();
offsets.globalSubrsInfo.offSize = calcOffSize (dataSize);
offsets.globalSubrsInfo.size = CFF2Subrs::calculate_serialized_size (offsets.globalSubrsInfo.offSize, subset_globalsubrs.len, dataSize);
/* local subrs */
if (!offsets.localSubrsInfos.resize (orig_fdcount))
return false;
if (!subset_localsubrs.resize (orig_fdcount))
return false;
for (unsigned int fd = 0; fd < orig_fdcount; fd++)
{
subset_localsubrs[fd].init ();
offsets.localSubrsInfos[fd].init ();
if (fdmap.includes (fd))
{
if (!subr_subsetter.encode_localsubrs (fd, subset_localsubrs[fd]))
return false;
unsigned int dataSize = subset_localsubrs[fd].total_size ();
if (dataSize > 0)
{
offsets.localSubrsInfos[fd].offset = final_size;
offsets.localSubrsInfos[fd].offSize = calcOffSize (dataSize);
offsets.localSubrsInfos[fd].size = CFF2Subrs::calculate_serialized_size (offsets.localSubrsInfos[fd].offSize, subset_localsubrs[fd].len, dataSize);
}
}
}
}
/* global subrs */
offsets.globalSubrsInfo.offset = final_size;
final_size += offsets.globalSubrsInfo.size;
@ -256,20 +363,19 @@ struct cff2_subset_plan {
{
offsets.FDArrayInfo.offset = final_size;
CFFFontDict_OpSerializer fontSzr;
final_size += CFF2FDArray::calculate_serialized_size(offsets.FDArrayInfo.offSize/*OUT*/, acc.fontDicts, subset_fdcount, fdmap, fontSzr);
unsigned int dictsSize = 0;
for (unsigned int i = 0; i < acc.fontDicts.len; i++)
if (fdmap.includes (i))
dictsSize += FontDict::calculate_serialized_size (acc.fontDicts[i], fontSzr);
offsets.FDArrayInfo.offSize = calcOffSize (dictsSize);
final_size += CFF2Index::calculate_serialized_size (offsets.FDArrayInfo.offSize, subset_fdcount, dictsSize);
}
/* CharStrings */
{
offsets.charStringsInfo.offset = final_size;
unsigned int dataSize = 0;
for (unsigned int i = 0; i < plan->glyphs.len; i++)
{
StrBuff &flatstr = flat_charstrings[i];
ByteStr str (&flatstr[0], flatstr.len);
subset_charstrings.push (str);
dataSize += flatstr.len;
}
unsigned int dataSize = subset_charstrings.total_size ();
offsets.charStringsInfo.offSize = calcOffSize (dataSize);
final_size += CFF2CharStrings::calculate_serialized_size (offsets.charStringsInfo.offSize, plan->glyphs.len, dataSize);
}
@ -278,14 +384,20 @@ struct cff2_subset_plan {
offsets.privateDictsOffset = final_size;
for (unsigned int i = 0; i < orig_fdcount; i++)
{
if (!fdmap.excludes (i))
if (fdmap.includes (i))
{
unsigned int priv_size;
bool has_localsubrs = offsets.localSubrsInfos[i].size > 0;
CFFPrivateDict_OpSerializer privSzr (desubroutinize, drop_hints);
priv_size = PrivateDict::calculate_serialized_size (acc.privateDicts[i], privSzr);
unsigned int priv_size = PrivateDict::calculate_serialized_size (acc.privateDicts[i], privSzr, has_localsubrs);
TableInfo privInfo = { final_size, priv_size, 0 };
privateDictInfos.push (privInfo);
final_size += privInfo.size;
if (!plan->desubroutinize && has_localsubrs)
{
offsets.localSubrsInfos[i].offset = final_size;
final_size += offsets.localSubrsInfos[i].size;
}
}
}
@ -305,12 +417,14 @@ struct cff2_subset_plan {
Remap fdmap;
ByteStrArray subset_charstrings;
StrBuffArray flat_charstrings;
StrBuffArray subset_charstrings;
StrBuffArray subset_globalsubrs;
hb_vector_t<StrBuffArray> subset_localsubrs;
hb_vector_t<TableInfo> privateDictInfos;
bool drop_hints;
bool desubroutinize;
CFF2SubrSubsetter subr_subsetter;
};
static inline bool _write_cff2 (const cff2_subset_plan &plan,
@ -346,9 +460,13 @@ static inline bool _write_cff2 (const cff2_subset_plan &plan,
/* global subrs */
{
assert (cff2->topDict + plan.offsets.topDictInfo.size == c.head - c.start);
CFF2Subrs *dest = c.allocate_size <CFF2Subrs> (HBUINT32::static_size);
CFF2Subrs *dest = c.start_embed <CFF2Subrs> ();
if (unlikely (dest == nullptr)) return false;
dest->count.set (0);
if (unlikely (!dest->serialize (&c, plan.offsets.globalSubrsInfo.offSize, plan.subset_globalsubrs)))
{
DEBUG_MSG (SUBSET, nullptr, "failed to serialize global subroutines");
return false;
}
}
/* variation store */
@ -420,19 +538,31 @@ static inline bool _write_cff2 (const cff2_subset_plan &plan,
assert (plan.offsets.privateDictsOffset == c.head - c.start);
for (unsigned int i = 0; i < acc.privateDicts.len; i++)
{
if (!plan.fdmap.excludes (i))
if (plan.fdmap.includes (i))
{
PrivateDict *pd = c.start_embed<PrivateDict> ();
if (unlikely (pd == nullptr)) return false;
unsigned int priv_size = plan.privateDictInfos[plan.fdmap[i]].size;
bool result;
CFFPrivateDict_OpSerializer privSzr (plan.desubroutinize, plan.drop_hints);
result = pd->serialize (&c, acc.privateDicts[i], privSzr, priv_size);
/* N.B. local subrs immediately follows its corresponding private dict. i.e., subr offset == private dict size */
unsigned int subroffset = (plan.offsets.localSubrsInfos[i].size > 0)? priv_size: 0;
result = pd->serialize (&c, acc.privateDicts[i], privSzr, subroffset);
if (unlikely (!result))
{
DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 Private Dict[%d]", i);
DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF Private Dict[%d]", i);
return false;
}
if (plan.offsets.localSubrsInfos[i].size > 0)
{
CFF2Subrs *dest = c.start_embed <CFF2Subrs> ();
if (unlikely (dest == nullptr)) return false;
if (unlikely (!dest->serialize (&c, plan.offsets.localSubrsInfos[i].offSize, plan.subset_localsubrs[i])))
{
DEBUG_MSG (SUBSET, nullptr, "failed to serialize local subroutines");
return false;
}
}
}
}

Binary file not shown.

Binary file not shown.

View File

@ -84,7 +84,54 @@ test_subset_cff2_strip_hints (void)
face_abc_subset = hb_subset_test_create_subset (face_abc, input);
hb_set_destroy (codepoints);
hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('C', 'F', 'F', ' '));
hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('C', 'F', 'F', '2'));
hb_face_destroy (face_abc_subset);
hb_face_destroy (face_abc);
hb_face_destroy (face_ac);
}
static void
test_subset_cff2_desubr (void)
{
hb_face_t *face_abc = hb_test_open_font_file ("fonts/AdobeVFPrototype.abc.otf");
hb_face_t *face_ac = hb_test_open_font_file ("fonts/AdobeVFPrototype.ac.nosubrs.otf");
hb_set_t *codepoints = hb_set_create ();
hb_subset_input_t *input;
hb_face_t *face_abc_subset;
hb_set_add (codepoints, 'a');
hb_set_add (codepoints, 'c');
input = hb_subset_test_create_input (codepoints);
hb_subset_input_set_desubroutinize (input, true);
face_abc_subset = hb_subset_test_create_subset (face_abc, input);
hb_set_destroy (codepoints);
hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('C', 'F', 'F', '2'));
hb_face_destroy (face_abc_subset);
hb_face_destroy (face_abc);
hb_face_destroy (face_ac);
}
static void
test_subset_cff2_desubr_strip_hints (void)
{
hb_face_t *face_abc = hb_test_open_font_file ("fonts/AdobeVFPrototype.abc.otf");
hb_face_t *face_ac = hb_test_open_font_file ("fonts/AdobeVFPrototype.ac.nosubrs.nohints.otf");
hb_set_t *codepoints = hb_set_create ();
hb_subset_input_t *input;
hb_face_t *face_abc_subset;
hb_set_add (codepoints, 'a');
hb_set_add (codepoints, 'c');
input = hb_subset_test_create_input (codepoints);
hb_subset_input_set_desubroutinize (input, true);
hb_subset_input_set_drop_hints (input, true);
face_abc_subset = hb_subset_test_create_subset (face_abc, input);
hb_set_destroy (codepoints);
hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('C', 'F', 'F', '2'));
hb_face_destroy (face_abc_subset);
hb_face_destroy (face_abc);
@ -99,6 +146,8 @@ main (int argc, char **argv)
hb_test_add (test_subset_cff2_noop);
hb_test_add (test_subset_cff2);
hb_test_add (test_subset_cff2_strip_hints);
hb_test_add (test_subset_cff2_desubr);
hb_test_add (test_subset_cff2_desubr_strip_hints);
return hb_test_run ();
}