• 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 
42 
43 struct InstanceRecord
44 {
45   friend struct fvar;
46 
get_coordinatesOT::InstanceRecord47   hb_array_t<const F16DOT16> get_coordinates (unsigned int axis_count) const
48   { return coordinatesZ.as_array (axis_count); }
49 
subsetOT::InstanceRecord50   bool subset (hb_subset_context_t *c,
51                unsigned axis_count,
52                bool has_postscript_nameid) const
53   {
54     TRACE_SUBSET (this);
55     if (unlikely (!c->serializer->embed (subfamilyNameID))) return_trace (false);
56     if (unlikely (!c->serializer->embed (flags))) return_trace (false);
57 
58     const hb_array_t<const F16DOT16> coords = get_coordinates (axis_count);
59     const hb_hashmap_t<hb_tag_t, float> *axes_location = c->plan->user_axes_location;
60     for (unsigned i = 0 ; i < axis_count; i++)
61     {
62       uint32_t *axis_tag;
63       // only keep instances whose coordinates == pinned axis location
64       if (!c->plan->axes_old_index_tag_map->has (i, &axis_tag)) continue;
65 
66       if (axes_location->has (*axis_tag) &&
67           fabsf (axes_location->get (*axis_tag) - coords[i].to_float ()) > 0.001f)
68         return_trace (false);
69 
70       if (!c->plan->axes_index_map->has (i))
71         continue;
72 
73       if (!c->serializer->embed (coords[i]))
74         return_trace (false);
75     }
76 
77     if (has_postscript_nameid)
78     {
79       NameID name_id;
80       name_id = StructAfter<NameID> (coords);
81       if (!c->serializer->embed (name_id))
82         return_trace (false);
83     }
84 
85     return_trace (true);
86   }
87 
sanitizeOT::InstanceRecord88   bool sanitize (hb_sanitize_context_t *c, unsigned int axis_count) const
89   {
90     TRACE_SANITIZE (this);
91     return_trace (c->check_struct (this) &&
92 		  c->check_array (coordinatesZ.arrayZ, axis_count));
93   }
94 
95   protected:
96   NameID	subfamilyNameID;/* The name ID for entries in the 'name' table
97 				 * that provide subfamily names for this instance. */
98   HBUINT16	flags;		/* Reserved for future use — set to 0. */
99   UnsizedArrayOf<F16DOT16>
100 		coordinatesZ;	/* The coordinates array for this instance. */
101   //NameID	postScriptNameIDX;/*Optional. The name ID for entries in the 'name'
102   //				  * table that provide PostScript names for this
103   //				  * instance. */
104 
105   public:
106   DEFINE_SIZE_UNBOUNDED (4);
107 };
108 
109 struct AxisRecord
110 {
cmpOT::AxisRecord111   int cmp (hb_tag_t key) const { return axisTag.cmp (key); }
112 
113   enum
114   {
115     AXIS_FLAG_HIDDEN	= 0x0001,
116   };
117 
118 #ifndef HB_DISABLE_DEPRECATED
get_axis_deprecatedOT::AxisRecord119   void get_axis_deprecated (hb_ot_var_axis_t *info) const
120   {
121     info->tag = axisTag;
122     info->name_id = axisNameID;
123     get_coordinates (info->min_value, info->default_value, info->max_value);
124   }
125 #endif
126 
get_axis_infoOT::AxisRecord127   void get_axis_info (unsigned axis_index, hb_ot_var_axis_info_t *info) const
128   {
129     info->axis_index = axis_index;
130     info->tag = axisTag;
131     info->name_id = axisNameID;
132     info->flags = (hb_ot_var_axis_flags_t) (unsigned int) flags;
133     get_coordinates (info->min_value, info->default_value, info->max_value);
134     info->reserved = 0;
135   }
136 
get_axis_tagOT::AxisRecord137   hb_tag_t get_axis_tag () const { return axisTag; }
138 
normalize_axis_valueOT::AxisRecord139   int normalize_axis_value (float v) const
140   {
141     float min_value, default_value, max_value;
142     get_coordinates (min_value, default_value, max_value);
143 
144     v = hb_clamp (v, min_value, max_value);
145 
146     if (v == default_value)
147       return 0;
148     else if (v < default_value)
149       v = (v - default_value) / (default_value - min_value);
150     else
151       v = (v - default_value) / (max_value - default_value);
152     return roundf (v * 16384.f);
153   }
154 
unnormalize_axis_valueOT::AxisRecord155   float unnormalize_axis_value (int v) const
156   {
157     float min_value, default_value, max_value;
158     get_coordinates (min_value, default_value, max_value);
159 
160     if (v == 0)
161       return default_value;
162     else if (v < 0)
163       return v * (default_value - min_value) / 16384.f + default_value;
164     else
165       return v * (max_value - default_value) / 16384.f + default_value;
166   }
167 
get_name_idOT::AxisRecord168   hb_ot_name_id_t get_name_id () const { return axisNameID; }
169 
sanitizeOT::AxisRecord170   bool sanitize (hb_sanitize_context_t *c) const
171   {
172     TRACE_SANITIZE (this);
173     return_trace (c->check_struct (this));
174   }
175 
get_coordinatesOT::AxisRecord176   void get_coordinates (float &min, float &default_, float &max) const
177   {
178     default_ = defaultValue / 65536.f;
179     /* Ensure order, to simplify client math. */
180     min = hb_min (default_, minValue / 65536.f);
181     max = hb_max (default_, maxValue / 65536.f);
182   }
183 
get_defaultOT::AxisRecord184   float get_default () const
185   {
186     return defaultValue / 65536.f;
187   }
188 
189   public:
190   Tag		axisTag;	/* Tag identifying the design variation for the axis. */
191   protected:
192   F16DOT16	minValue;	/* The minimum coordinate value for the axis. */
193   F16DOT16	defaultValue;	/* The default coordinate value for the axis. */
194   F16DOT16	maxValue;	/* The maximum coordinate value for the axis. */
195   public:
196   HBUINT16	flags;		/* Axis flags. */
197   NameID	axisNameID;	/* The name ID for entries in the 'name' table that
198 				 * provide a display name for this axis. */
199 
200   public:
201   DEFINE_SIZE_STATIC (20);
202 };
203 
204 struct fvar
205 {
206   static constexpr hb_tag_t tableTag = HB_OT_TAG_fvar;
207 
has_dataOT::fvar208   bool has_data () const { return version.to_int (); }
209 
sanitizeOT::fvar210   bool sanitize (hb_sanitize_context_t *c) const
211   {
212     TRACE_SANITIZE (this);
213     return_trace (version.sanitize (c) &&
214 		  likely (version.major == 1) &&
215 		  c->check_struct (this) &&
216 		  axisSize == 20 && /* Assumed in our code. */
217 		  instanceSize >= axisCount * 4 + 4 &&
218 		  get_axes ().sanitize (c) &&
219 		  c->check_range (get_instance (0), instanceCount, instanceSize));
220   }
221 
get_axis_countOT::fvar222   unsigned int get_axis_count () const { return axisCount; }
223 
224 #ifndef HB_DISABLE_DEPRECATED
get_axes_deprecatedOT::fvar225   unsigned int get_axes_deprecated (unsigned int      start_offset,
226 				    unsigned int     *axes_count /* IN/OUT */,
227 				    hb_ot_var_axis_t *axes_array /* OUT */) const
228   {
229     if (axes_count)
230     {
231       hb_array_t<const AxisRecord> arr = get_axes ().sub_array (start_offset, axes_count);
232       for (unsigned i = 0; i < arr.length; ++i)
233 	arr[i].get_axis_deprecated (&axes_array[i]);
234     }
235     return axisCount;
236   }
237 #endif
238 
get_axis_infosOT::fvar239   unsigned int get_axis_infos (unsigned int           start_offset,
240 			       unsigned int          *axes_count /* IN/OUT */,
241 			       hb_ot_var_axis_info_t *axes_array /* OUT */) const
242   {
243     if (axes_count)
244     {
245       hb_array_t<const AxisRecord> arr = get_axes ().sub_array (start_offset, axes_count);
246       for (unsigned i = 0; i < arr.length; ++i)
247 	arr[i].get_axis_info (start_offset + i, &axes_array[i]);
248     }
249     return axisCount;
250   }
251 
252 #ifndef HB_DISABLE_DEPRECATED
253   bool
find_axis_deprecatedOT::fvar254   find_axis_deprecated (hb_tag_t tag, unsigned *axis_index, hb_ot_var_axis_t *info) const
255   {
256     unsigned i;
257     if (!axis_index) axis_index = &i;
258     *axis_index = HB_OT_VAR_NO_AXIS_INDEX;
259     auto axes = get_axes ();
260     return axes.lfind (tag, axis_index) && ((void) axes[*axis_index].get_axis_deprecated (info), true);
261   }
262 #endif
263   bool
find_axis_infoOT::fvar264   find_axis_info (hb_tag_t tag, hb_ot_var_axis_info_t *info) const
265   {
266     unsigned i;
267     auto axes = get_axes ();
268     return axes.lfind (tag, &i) && ((void) axes[i].get_axis_info (i, info), true);
269   }
270 
normalize_axis_valueOT::fvar271   int normalize_axis_value (unsigned int axis_index, float v) const
272   { return get_axes ()[axis_index].normalize_axis_value (v); }
273 
unnormalize_axis_valueOT::fvar274   float unnormalize_axis_value (unsigned int axis_index, int v) const
275   { return get_axes ()[axis_index].unnormalize_axis_value (v); }
276 
get_instance_countOT::fvar277   unsigned int get_instance_count () const { return instanceCount; }
278 
get_instance_subfamily_name_idOT::fvar279   hb_ot_name_id_t get_instance_subfamily_name_id (unsigned int instance_index) const
280   {
281     const InstanceRecord *instance = get_instance (instance_index);
282     if (unlikely (!instance)) return HB_OT_NAME_ID_INVALID;
283     return instance->subfamilyNameID;
284   }
285 
get_instance_postscript_name_idOT::fvar286   hb_ot_name_id_t get_instance_postscript_name_id (unsigned int instance_index) const
287   {
288     const InstanceRecord *instance = get_instance (instance_index);
289     if (unlikely (!instance)) return HB_OT_NAME_ID_INVALID;
290     if (instanceSize >= axisCount * 4 + 6)
291       return StructAfter<NameID> (instance->get_coordinates (axisCount));
292     return HB_OT_NAME_ID_INVALID;
293   }
294 
get_instance_coordsOT::fvar295   unsigned int get_instance_coords (unsigned int  instance_index,
296 				    unsigned int *coords_length, /* IN/OUT */
297 				    float        *coords         /* OUT */) const
298   {
299     const InstanceRecord *instance = get_instance (instance_index);
300     if (unlikely (!instance))
301     {
302       if (coords_length)
303 	*coords_length = 0;
304       return 0;
305     }
306 
307     if (coords_length && *coords_length)
308     {
309       hb_array_t<const F16DOT16> instanceCoords = instance->get_coordinates (axisCount)
310 							 .sub_array (0, coords_length);
311       for (unsigned int i = 0; i < instanceCoords.length; i++)
312 	coords[i] = instanceCoords.arrayZ[i].to_float ();
313     }
314     return axisCount;
315   }
316 
collect_name_idsOT::fvar317   void collect_name_ids (hb_hashmap_t<hb_tag_t, float> *user_axes_location,
318 			 hb_set_t *nameids  /* IN/OUT */) const
319   {
320     if (!has_data ()) return;
321     hb_map_t pinned_axes;
322 
323     auto axis_records = get_axes ();
324     for (unsigned i = 0 ; i < (unsigned)axisCount; i++)
325     {
326       hb_tag_t axis_tag = axis_records[i].get_axis_tag ();
327       if (user_axes_location->has (axis_tag))
328       {
329         pinned_axes.set (i, axis_tag);
330         continue;
331       }
332 
333       nameids->add (axis_records[i].get_name_id ());
334     }
335 
336     for (unsigned i = 0 ; i < (unsigned)instanceCount; i++)
337     {
338       const InstanceRecord *instance = get_instance (i);
339 
340       if (hb_any (+ hb_enumerate (instance->get_coordinates (axisCount))
341                   | hb_filter (pinned_axes, hb_first)
342                   | hb_map ([&] (const hb_pair_t<unsigned, const F16DOT16&>& _)
343                             {
344                               hb_tag_t axis_tag = pinned_axes.get (_.first);
345                               float location = user_axes_location->get (axis_tag);
346                               if (fabs ((double)location - (double)_.second.to_float ()) > 0.001) return true;
347                               return false;
348                             })
349                   ))
350         continue;
351 
352       nameids->add (instance->subfamilyNameID);
353 
354       if (instanceSize >= axisCount * 4 + 6)
355       {
356         unsigned post_script_name_id = StructAfter<NameID> (instance->get_coordinates (axisCount));
357         if (post_script_name_id != HB_OT_NAME_ID_INVALID) nameids->add (post_script_name_id);
358       }
359     }
360   }
361 
subsetOT::fvar362   bool subset (hb_subset_context_t *c) const
363   {
364     TRACE_SUBSET (this);
365     unsigned retained_axis_count = c->plan->axes_index_map->get_population ();
366     if (!retained_axis_count) //all axes are pinned
367       return_trace (false);
368 
369     fvar *out = c->serializer->embed (this);
370     if (unlikely (!out)) return_trace (false);
371 
372     if (!c->serializer->check_assign (out->axisCount, retained_axis_count, HB_SERIALIZE_ERROR_INT_OVERFLOW))
373       return_trace (false);
374 
375     bool has_postscript_nameid = false;
376     if (instanceSize >= axisCount * 4 + 6)
377       has_postscript_nameid = true;
378 
379     if (!c->serializer->check_assign (out->instanceSize, retained_axis_count * 4 + (has_postscript_nameid ? 6 : 4),
380                                       HB_SERIALIZE_ERROR_INT_OVERFLOW))
381       return_trace (false);
382 
383     auto axes_records = get_axes ();
384     for (unsigned i = 0 ; i < (unsigned)axisCount; i++)
385     {
386       if (!c->plan->axes_index_map->has (i)) continue;
387       if (unlikely (!c->serializer->embed (axes_records[i])))
388         return_trace (false);
389     }
390 
391     if (!c->serializer->check_assign (out->firstAxis, get_size (), HB_SERIALIZE_ERROR_INT_OVERFLOW))
392       return_trace (false);
393 
394     for (unsigned i = 0 ; i < (unsigned)instanceCount; i++)
395     {
396       const InstanceRecord *instance = get_instance (i);
397       auto snap = c->serializer->snapshot ();
398       if (!instance->subset (c, axisCount, has_postscript_nameid))
399         c->serializer->revert (snap);
400     }
401     return_trace (true);
402   }
403 
404   public:
get_axesOT::fvar405   hb_array_t<const AxisRecord> get_axes () const
406   { return hb_array (&(this+firstAxis), axisCount); }
407 
get_instanceOT::fvar408   const InstanceRecord *get_instance (unsigned int i) const
409   {
410     if (unlikely (i >= instanceCount)) return nullptr;
411    return &StructAtOffset<InstanceRecord> (&StructAfter<InstanceRecord> (get_axes ()),
412 					   i * instanceSize);
413   }
414 
415   protected:
416   FixedVersion<>version;	/* Version of the fvar table
417 				 * initially set to 0x00010000u */
418   Offset16To<AxisRecord>
419 		firstAxis;	/* Offset in bytes from the beginning of the table
420 				 * to the start of the AxisRecord array. */
421   HBUINT16	reserved;	/* This field is permanently reserved. Set to 2. */
422   HBUINT16	axisCount;	/* The number of variation axes in the font (the
423 				 * number of records in the axes array). */
424   HBUINT16	axisSize;	/* The size in bytes of each VariationAxisRecord —
425 				 * set to 20 (0x0014) for this version. */
426   HBUINT16	instanceCount;	/* The number of named instances defined in the font
427 				 * (the number of records in the instances array). */
428   HBUINT16	instanceSize;	/* The size in bytes of each InstanceRecord — set
429 				 * to either axisCount * sizeof(F16DOT16) + 4, or to
430 				 * axisCount * sizeof(F16DOT16) + 6. */
431 
432   public:
433   DEFINE_SIZE_STATIC (16);
434 };
435 
436 } /* namespace OT */
437 
438 
439 #endif /* HB_OT_VAR_FVAR_TABLE_HH */
440