• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright © 2017  Google, Inc.
3  *
4  *  This is part of HarfBuzz, a text shaping library.
5  *
6  * Permission is hereby granted, without written agreement and without
7  * license or royalty fees, to use, copy, modify, and distribute this
8  * software and its documentation for any purpose, provided that the
9  * above copyright notice and the following two paragraphs appear in
10  * all copies of this software.
11  *
12  * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14  * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15  * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
16  * DAMAGE.
17  *
18  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
21  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
23  *
24  * Google Author(s): Behdad Esfahbod
25  */
26 
27 #ifndef HB_OT_VAR_FVAR_TABLE_HH
28 #define HB_OT_VAR_FVAR_TABLE_HH
29 
30 #include "hb-open-type.hh"
31 
32 /*
33  * fvar -- Font Variations
34  * https://docs.microsoft.com/en-us/typography/opentype/spec/fvar
35  */
36 
37 #define HB_OT_TAG_fvar HB_TAG('f','v','a','r')
38 
39 
40 namespace OT {
41 
axis_coord_pinned_or_within_axis_range(const hb_array_t<const F16DOT16> coords,unsigned axis_index,Triple axis_limit)42 static bool axis_coord_pinned_or_within_axis_range (const hb_array_t<const F16DOT16> coords,
43                                                     unsigned axis_index,
44                                                     Triple axis_limit)
45 {
46   float axis_coord = coords[axis_index].to_float ();
47   if (axis_limit.is_point ())
48   {
49     if (axis_limit.minimum != axis_coord)
50       return false;
51   }
52   else
53   {
54     if (axis_coord < axis_limit.minimum ||
55         axis_coord > axis_limit.maximum)
56       return false;
57   }
58   return true;
59 }
60 
61 struct InstanceRecord
62 {
63   friend struct fvar;
64 
get_coordinatesOT::InstanceRecord65   hb_array_t<const F16DOT16> get_coordinates (unsigned int axis_count) const
66   { return coordinatesZ.as_array (axis_count); }
67 
keep_instanceOT::InstanceRecord68   bool keep_instance (unsigned axis_count,
69                       const hb_map_t *axes_index_tag_map,
70                       const hb_hashmap_t<hb_tag_t, Triple> *axes_location) const
71   {
72     if (axes_location->is_empty ()) return true;
73     const hb_array_t<const F16DOT16> coords = get_coordinates (axis_count);
74     for (unsigned i = 0 ; i < axis_count; i++)
75     {
76       uint32_t *axis_tag;
77       if (!axes_index_tag_map->has (i, &axis_tag))
78         return false;
79       if (!axes_location->has (*axis_tag))
80         continue;
81 
82       Triple axis_limit = axes_location->get (*axis_tag);
83       if (!axis_coord_pinned_or_within_axis_range (coords, i, axis_limit))
84         return false;
85     }
86     return true;
87   }
88 
subsetOT::InstanceRecord89   bool subset (hb_subset_context_t *c,
90                unsigned axis_count,
91                bool has_postscript_nameid) const
92   {
93     TRACE_SUBSET (this);
94     if (unlikely (!c->serializer->embed (subfamilyNameID))) return_trace (false);
95     if (unlikely (!c->serializer->embed (flags))) return_trace (false);
96 
97     const hb_array_t<const F16DOT16> coords = get_coordinates (axis_count);
98     const hb_hashmap_t<hb_tag_t, Triple> *axes_location = &c->plan->user_axes_location;
99     for (unsigned i = 0 ; i < axis_count; i++)
100     {
101       uint32_t *axis_tag;
102       Triple *axis_limit;
103       // only keep instances whose coordinates == pinned axis location
104       if (!c->plan->axes_old_index_tag_map.has (i, &axis_tag)) return_trace (false);
105       if (axes_location->has (*axis_tag, &axis_limit))
106       {
107         if (!axis_coord_pinned_or_within_axis_range (coords, i, *axis_limit))
108           return_trace (false);
109 
110         //skip pinned axis
111         if (axis_limit->is_point ())
112           continue;
113       }
114 
115       if (!c->serializer->embed (coords[i]))
116         return_trace (false);
117     }
118 
119     if (has_postscript_nameid)
120     {
121       NameID name_id;
122       name_id = StructAfter<NameID> (coords);
123       if (!c->serializer->embed (name_id))
124         return_trace (false);
125     }
126 
127     return_trace (true);
128   }
129 
sanitizeOT::InstanceRecord130   bool sanitize (hb_sanitize_context_t *c, unsigned int axis_count) const
131   {
132     TRACE_SANITIZE (this);
133     return_trace (c->check_struct (this) &&
134 		  c->check_array (coordinatesZ.arrayZ, axis_count));
135   }
136 
137   protected:
138   NameID	subfamilyNameID;/* The name ID for entries in the 'name' table
139 				 * that provide subfamily names for this instance. */
140   HBUINT16	flags;		/* Reserved for future use — set to 0. */
141   UnsizedArrayOf<F16DOT16>
142 		coordinatesZ;	/* The coordinates array for this instance. */
143   //NameID	postScriptNameIDX;/*Optional. The name ID for entries in the 'name'
144   //				  * table that provide PostScript names for this
145   //				  * instance. */
146 
147   public:
148   DEFINE_SIZE_UNBOUNDED (4);
149 };
150 
151 struct AxisRecord
152 {
cmpOT::AxisRecord153   int cmp (hb_tag_t key) const { return axisTag.cmp (key); }
154 
155   enum
156   {
157     AXIS_FLAG_HIDDEN	= 0x0001,
158   };
159 
160 #ifndef HB_DISABLE_DEPRECATED
get_axis_deprecatedOT::AxisRecord161   void get_axis_deprecated (hb_ot_var_axis_t *info) const
162   {
163     info->tag = axisTag;
164     info->name_id = axisNameID;
165     get_coordinates (info->min_value, info->default_value, info->max_value);
166   }
167 #endif
168 
get_axis_infoOT::AxisRecord169   void get_axis_info (unsigned axis_index, hb_ot_var_axis_info_t *info) const
170   {
171     info->axis_index = axis_index;
172     info->tag = axisTag;
173     info->name_id = axisNameID;
174     info->flags = (hb_ot_var_axis_flags_t) (unsigned int) flags;
175     get_coordinates (info->min_value, info->default_value, info->max_value);
176     info->reserved = 0;
177   }
178 
get_axis_tagOT::AxisRecord179   hb_tag_t get_axis_tag () const { return axisTag; }
180 
normalize_axis_valueOT::AxisRecord181   int normalize_axis_value (float v) const
182   {
183     float min_value, default_value, max_value;
184     get_coordinates (min_value, default_value, max_value);
185 
186     v = hb_clamp (v, min_value, max_value);
187 
188     if (v == default_value)
189       return 0;
190     else if (v < default_value)
191       v = (v - default_value) / (default_value - min_value);
192     else
193       v = (v - default_value) / (max_value - default_value);
194     return roundf (v * 16384.f);
195   }
196 
unnormalize_axis_valueOT::AxisRecord197   float unnormalize_axis_value (int v) const
198   {
199     float min_value, default_value, max_value;
200     get_coordinates (min_value, default_value, max_value);
201 
202     if (v == 0)
203       return default_value;
204     else if (v < 0)
205       return v * (default_value - min_value) / 16384.f + default_value;
206     else
207       return v * (max_value - default_value) / 16384.f + default_value;
208   }
209 
get_name_idOT::AxisRecord210   hb_ot_name_id_t get_name_id () const { return axisNameID; }
211 
sanitizeOT::AxisRecord212   bool sanitize (hb_sanitize_context_t *c) const
213   {
214     TRACE_SANITIZE (this);
215     return_trace (c->check_struct (this));
216   }
217 
get_coordinatesOT::AxisRecord218   void get_coordinates (float &min, float &default_, float &max) const
219   {
220     default_ = defaultValue.to_float ();
221     /* Ensure order, to simplify client math. */
222     min = hb_min (default_, minValue.to_float ());
223     max = hb_max (default_, maxValue.to_float ());
224   }
225 
get_defaultOT::AxisRecord226   float get_default () const
227   {
228     return defaultValue.to_float ();
229   }
230 
get_triple_distancesOT::AxisRecord231   TripleDistances get_triple_distances () const
232   {
233     float min, default_, max;
234     get_coordinates (min, default_, max);
235     return TripleDistances (min, default_, max);
236   }
237 
subsetOT::AxisRecord238   bool subset (hb_subset_context_t *c) const
239   {
240     TRACE_SUBSET (this);
241     auto *out = c->serializer->embed (this);
242     if (unlikely (!out)) return_trace (false);
243 
244     const hb_hashmap_t<hb_tag_t, Triple>& user_axes_location = c->plan->user_axes_location;
245     Triple *axis_limit;
246     if (user_axes_location.has (axisTag, &axis_limit))
247     {
248       out->minValue.set_float (axis_limit->minimum);
249       out->defaultValue.set_float (axis_limit->middle);
250       out->maxValue.set_float (axis_limit->maximum);
251     }
252     return_trace (true);
253   }
254 
255   public:
256   Tag		axisTag;	/* Tag identifying the design variation for the axis. */
257   protected:
258   F16DOT16	minValue;	/* The minimum coordinate value for the axis. */
259   F16DOT16	defaultValue;	/* The default coordinate value for the axis. */
260   F16DOT16	maxValue;	/* The maximum coordinate value for the axis. */
261   public:
262   HBUINT16	flags;		/* Axis flags. */
263   NameID	axisNameID;	/* The name ID for entries in the 'name' table that
264 				 * provide a display name for this axis. */
265 
266   public:
267   DEFINE_SIZE_STATIC (20);
268 };
269 
270 struct fvar
271 {
272   static constexpr hb_tag_t tableTag = HB_OT_TAG_fvar;
273 
has_dataOT::fvar274   bool has_data () const { return version.to_int (); }
275 
sanitizeOT::fvar276   bool sanitize (hb_sanitize_context_t *c) const
277   {
278     TRACE_SANITIZE (this);
279     return_trace (version.sanitize (c) &&
280 		  likely (version.major == 1) &&
281 		  c->check_struct (this) &&
282 		  axisSize == 20 && /* Assumed in our code. */
283 		  instanceSize >= axisCount * 4 + 4 &&
284 		  get_axes ().sanitize (c) &&
285 		  c->check_range (&StructAfter<InstanceRecord> (get_axes ()),
286 				  instanceCount, instanceSize));
287   }
288 
get_axis_countOT::fvar289   unsigned int get_axis_count () const { return axisCount; }
290 
291 #ifndef HB_DISABLE_DEPRECATED
get_axes_deprecatedOT::fvar292   unsigned int get_axes_deprecated (unsigned int      start_offset,
293 				    unsigned int     *axes_count /* IN/OUT */,
294 				    hb_ot_var_axis_t *axes_array /* OUT */) const
295   {
296     if (axes_count)
297     {
298       hb_array_t<const AxisRecord> arr = get_axes ().sub_array (start_offset, axes_count);
299       for (unsigned i = 0; i < arr.length; ++i)
300 	arr[i].get_axis_deprecated (&axes_array[i]);
301     }
302     return axisCount;
303   }
304 #endif
305 
get_axis_infosOT::fvar306   unsigned int get_axis_infos (unsigned int           start_offset,
307 			       unsigned int          *axes_count /* IN/OUT */,
308 			       hb_ot_var_axis_info_t *axes_array /* OUT */) const
309   {
310     if (axes_count)
311     {
312       hb_array_t<const AxisRecord> arr = get_axes ().sub_array (start_offset, axes_count);
313       for (unsigned i = 0; i < arr.length; ++i)
314 	arr[i].get_axis_info (start_offset + i, &axes_array[i]);
315     }
316     return axisCount;
317   }
318 
319 #ifndef HB_DISABLE_DEPRECATED
320   bool
find_axis_deprecatedOT::fvar321   find_axis_deprecated (hb_tag_t tag, unsigned *axis_index, hb_ot_var_axis_t *info) const
322   {
323     unsigned i;
324     if (!axis_index) axis_index = &i;
325     *axis_index = HB_OT_VAR_NO_AXIS_INDEX;
326     auto axes = get_axes ();
327     return axes.lfind (tag, axis_index) && ((void) axes[*axis_index].get_axis_deprecated (info), true);
328   }
329 #endif
330   bool
find_axis_infoOT::fvar331   find_axis_info (hb_tag_t tag, hb_ot_var_axis_info_t *info) const
332   {
333     unsigned i;
334     auto axes = get_axes ();
335     return axes.lfind (tag, &i) && ((void) axes[i].get_axis_info (i, info), true);
336   }
337 
normalize_axis_valueOT::fvar338   int normalize_axis_value (unsigned int axis_index, float v) const
339   { return get_axes ()[axis_index].normalize_axis_value (v); }
340 
unnormalize_axis_valueOT::fvar341   float unnormalize_axis_value (unsigned int axis_index, int v) const
342   { return get_axes ()[axis_index].unnormalize_axis_value (v); }
343 
get_instance_countOT::fvar344   unsigned int get_instance_count () const { return instanceCount; }
345 
get_instance_subfamily_name_idOT::fvar346   hb_ot_name_id_t get_instance_subfamily_name_id (unsigned int instance_index) const
347   {
348     const InstanceRecord *instance = get_instance (instance_index);
349     if (unlikely (!instance)) return HB_OT_NAME_ID_INVALID;
350     return instance->subfamilyNameID;
351   }
352 
get_instance_postscript_name_idOT::fvar353   hb_ot_name_id_t get_instance_postscript_name_id (unsigned int instance_index) const
354   {
355     const InstanceRecord *instance = get_instance (instance_index);
356     if (unlikely (!instance)) return HB_OT_NAME_ID_INVALID;
357     if (instanceSize >= axisCount * 4 + 6)
358       return StructAfter<NameID> (instance->get_coordinates (axisCount));
359     return HB_OT_NAME_ID_INVALID;
360   }
361 
get_instance_coordsOT::fvar362   unsigned int get_instance_coords (unsigned int  instance_index,
363 				    unsigned int *coords_length, /* IN/OUT */
364 				    float        *coords         /* OUT */) const
365   {
366     const InstanceRecord *instance = get_instance (instance_index);
367     if (unlikely (!instance))
368     {
369       if (coords_length)
370 	*coords_length = 0;
371       return 0;
372     }
373 
374     if (coords_length && *coords_length)
375     {
376       hb_array_t<const F16DOT16> instanceCoords = instance->get_coordinates (axisCount)
377 							 .sub_array (0, coords_length);
378       for (unsigned int i = 0; i < instanceCoords.length; i++)
379 	coords[i] = instanceCoords.arrayZ[i].to_float ();
380     }
381     return axisCount;
382   }
383 
collect_name_idsOT::fvar384   void collect_name_ids (hb_hashmap_t<hb_tag_t, Triple> *user_axes_location,
385 			 hb_map_t *axes_old_index_tag_map,
386 			 hb_set_t *nameids  /* IN/OUT */) const
387   {
388     if (!has_data ()) return;
389 
390     auto axis_records = get_axes ();
391     for (unsigned i = 0 ; i < (unsigned)axisCount; i++)
392     {
393       hb_tag_t axis_tag = axis_records[i].get_axis_tag ();
394       if (user_axes_location->has (axis_tag) &&
395           user_axes_location->get (axis_tag).is_point ())
396         continue;
397 
398       nameids->add (axis_records[i].get_name_id ());
399     }
400 
401     for (unsigned i = 0 ; i < (unsigned)instanceCount; i++)
402     {
403       const InstanceRecord *instance = get_instance (i);
404 
405       if (!instance->keep_instance (axisCount, axes_old_index_tag_map, user_axes_location))
406         continue;
407 
408       nameids->add (instance->subfamilyNameID);
409 
410       if (instanceSize >= axisCount * 4 + 6)
411       {
412         unsigned post_script_name_id = StructAfter<NameID> (instance->get_coordinates (axisCount));
413         if (post_script_name_id != HB_OT_NAME_ID_INVALID) nameids->add (post_script_name_id);
414       }
415     }
416   }
417 
subsetOT::fvar418   bool subset (hb_subset_context_t *c) const
419   {
420     TRACE_SUBSET (this);
421     unsigned retained_axis_count = c->plan->axes_index_map.get_population ();
422     if (!retained_axis_count) //all axes are pinned
423       return_trace (false);
424 
425     fvar *out = c->serializer->embed (this);
426     if (unlikely (!out)) return_trace (false);
427 
428     if (!c->serializer->check_assign (out->axisCount, retained_axis_count, HB_SERIALIZE_ERROR_INT_OVERFLOW))
429       return_trace (false);
430 
431     bool has_postscript_nameid = false;
432     if (instanceSize >= axisCount * 4 + 6)
433       has_postscript_nameid = true;
434 
435     if (!c->serializer->check_assign (out->instanceSize, retained_axis_count * 4 + (has_postscript_nameid ? 6 : 4),
436                                       HB_SERIALIZE_ERROR_INT_OVERFLOW))
437       return_trace (false);
438 
439     auto axes_records = get_axes ();
440     for (unsigned i = 0 ; i < (unsigned)axisCount; i++)
441     {
442       if (!c->plan->axes_index_map.has (i)) continue;
443       if (unlikely (!axes_records[i].subset (c)))
444         return_trace (false);
445     }
446 
447     if (!c->serializer->check_assign (out->firstAxis, get_size (), HB_SERIALIZE_ERROR_INT_OVERFLOW))
448       return_trace (false);
449 
450     unsigned num_retained_instances = 0;
451     for (unsigned i = 0 ; i < (unsigned)instanceCount; i++)
452     {
453       const InstanceRecord *instance = get_instance (i);
454       auto snap = c->serializer->snapshot ();
455       if (!instance->subset (c, axisCount, has_postscript_nameid))
456         c->serializer->revert (snap);
457       else
458         num_retained_instances++;
459     }
460 
461     return_trace (c->serializer->check_assign (out->instanceCount, num_retained_instances, HB_SERIALIZE_ERROR_INT_OVERFLOW));
462   }
463 
464   public:
get_axesOT::fvar465   hb_array_t<const AxisRecord> get_axes () const
466   { return hb_array (&(this+firstAxis), axisCount); }
467 
get_instanceOT::fvar468   const InstanceRecord *get_instance (unsigned int i) const
469   {
470     if (unlikely (i >= instanceCount)) return nullptr;
471    return &StructAtOffset<InstanceRecord> (&StructAfter<InstanceRecord> (get_axes ()),
472 					   i * instanceSize);
473   }
474 
475   protected:
476   FixedVersion<>version;	/* Version of the fvar table
477 				 * initially set to 0x00010000u */
478   Offset16To<AxisRecord>
479 		firstAxis;	/* Offset in bytes from the beginning of the table
480 				 * to the start of the AxisRecord array. */
481   HBUINT16	reserved;	/* This field is permanently reserved. Set to 2. */
482   HBUINT16	axisCount;	/* The number of variation axes in the font (the
483 				 * number of records in the axes array). */
484   HBUINT16	axisSize;	/* The size in bytes of each VariationAxisRecord —
485 				 * set to 20 (0x0014) for this version. */
486   HBUINT16	instanceCount;	/* The number of named instances defined in the font
487 				 * (the number of records in the instances array). */
488   HBUINT16	instanceSize;	/* The size in bytes of each InstanceRecord — set
489 				 * to either axisCount * sizeof(F16DOT16) + 4, or to
490 				 * axisCount * sizeof(F16DOT16) + 6. */
491 
492   public:
493   DEFINE_SIZE_STATIC (16);
494 };
495 
496 } /* namespace OT */
497 
498 
499 #endif /* HB_OT_VAR_FVAR_TABLE_HH */
500