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
40namespace OT {
41
42
43struct InstanceRecord
44{
45 friend struct fvar;
46
47 hb_array_t<const F16DOT16> get_coordinates (unsigned int axis_count) const
48 { return coordinatesZ.as_array (len: axis_count); }
49
50 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 (key: i, vp: &axis_tag)) continue;
65
66 if (axes_location->has (key: *axis_tag) &&
67 fabsf (x: axes_location->get (key: *axis_tag) - coords[i].to_float ()) > 0.001f)
68 return_trace (false);
69
70 if (!c->plan->axes_index_map.has (key: i))
71 continue;
72
73 if (!c->serializer->embed (obj: coords[i]))
74 return_trace (false);
75 }
76
77 if (has_postscript_nameid)
78 {
79 NameID name_id;
80 name_id = StructAfter<NameID> (X: coords);
81 if (!c->serializer->embed (obj: name_id))
82 return_trace (false);
83 }
84
85 return_trace (true);
86 }
87
88 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
109struct AxisRecord
110{
111 int cmp (hb_tag_t key) const { return axisTag.cmp (a: key); }
112
113 enum
114 {
115 AXIS_FLAG_HIDDEN = 0x0001,
116 };
117
118#ifndef HB_DISABLE_DEPRECATED
119 void get_axis_deprecated (hb_ot_var_axis_t *info) const
120 {
121 info->tag = axisTag;
122 info->name_id = axisNameID;
123 get_coordinates (min&: info->min_value, default_&: info->default_value, max&: info->max_value);
124 }
125#endif
126
127 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 (min&: info->min_value, default_&: info->default_value, max&: info->max_value);
134 info->reserved = 0;
135 }
136
137 hb_tag_t get_axis_tag () const { return axisTag; }
138
139 int normalize_axis_value (float v) const
140 {
141 float min_value, default_value, max_value;
142 get_coordinates (min&: min_value, default_&: default_value, max&: 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
155 float unnormalize_axis_value (int v) const
156 {
157 float min_value, default_value, max_value;
158 get_coordinates (min&: min_value, default_&: default_value, max&: 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
168 hb_ot_name_id_t get_name_id () const { return axisNameID; }
169
170 bool sanitize (hb_sanitize_context_t *c) const
171 {
172 TRACE_SANITIZE (this);
173 return_trace (c->check_struct (this));
174 }
175
176 void get_coordinates (float &min, float &default_, float &max) const
177 {
178 default_ = defaultValue.to_float ();
179 /* Ensure order, to simplify client math. */
180 min = hb_min (default_, minValue.to_float ());
181 max = hb_max (default_, maxValue.to_float ());
182 }
183
184 float get_default () const
185 {
186 return defaultValue.to_float ();
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
204struct fvar
205{
206 static constexpr hb_tag_t tableTag = HB_OT_TAG_fvar;
207
208 bool has_data () const { return version.to_int (); }
209
210 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
222 unsigned int get_axis_count () const { return axisCount; }
223
224#ifndef HB_DISABLE_DEPRECATED
225 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, seg_count: axes_count);
232 for (unsigned i = 0; i < arr.length; ++i)
233 arr[i].get_axis_deprecated (info: &axes_array[i]);
234 }
235 return axisCount;
236 }
237#endif
238
239 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, seg_count: axes_count);
246 for (unsigned i = 0; i < arr.length; ++i)
247 arr[i].get_axis_info (axis_index: start_offset + i, info: &axes_array[i]);
248 }
249 return axisCount;
250 }
251
252#ifndef HB_DISABLE_DEPRECATED
253 bool
254 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 (x: tag, pos: axis_index) && ((void) axes[*axis_index].get_axis_deprecated (info), true);
261 }
262#endif
263 bool
264 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 (x: tag, pos: &i) && ((void) axes[i].get_axis_info (axis_index: i, info), true);
269 }
270
271 int normalize_axis_value (unsigned int axis_index, float v) const
272 { return get_axes ()[axis_index].normalize_axis_value (v); }
273
274 float unnormalize_axis_value (unsigned int axis_index, int v) const
275 { return get_axes ()[axis_index].unnormalize_axis_value (v); }
276
277 unsigned int get_instance_count () const { return instanceCount; }
278
279 hb_ot_name_id_t get_instance_subfamily_name_id (unsigned int instance_index) const
280 {
281 const InstanceRecord *instance = get_instance (i: instance_index);
282 if (unlikely (!instance)) return HB_OT_NAME_ID_INVALID;
283 return instance->subfamilyNameID;
284 }
285
286 hb_ot_name_id_t get_instance_postscript_name_id (unsigned int instance_index) const
287 {
288 const InstanceRecord *instance = get_instance (i: instance_index);
289 if (unlikely (!instance)) return HB_OT_NAME_ID_INVALID;
290 if (instanceSize >= axisCount * 4 + 6)
291 return StructAfter<NameID> (X: instance->get_coordinates (axis_count: axisCount));
292 return HB_OT_NAME_ID_INVALID;
293 }
294
295 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 (i: 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 (axis_count: axisCount)
310 .sub_array (start_offset: 0, seg_count: 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
317 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 (key: axis_tag))
328 {
329 pinned_axes.set (key: i, value&: axis_tag);
330 continue;
331 }
332
333 nameids->add (g: 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 (axis_count: 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 (key: _.first);
345 float location = user_axes_location->get (key: axis_tag);
346 if (fabs (x: (double)location - (double)_.second.to_float ()) > 0.001) return true;
347 return false;
348 })
349 ))
350 continue;
351
352 nameids->add (g: instance->subfamilyNameID);
353
354 if (instanceSize >= axisCount * 4 + 6)
355 {
356 unsigned post_script_name_id = StructAfter<NameID> (X: instance->get_coordinates (axis_count: axisCount));
357 if (post_script_name_id != HB_OT_NAME_ID_INVALID) nameids->add (g: post_script_name_id);
358 }
359 }
360 }
361
362 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 (obj: this);
370 if (unlikely (!out)) return_trace (false);
371
372 if (!c->serializer->check_assign (v1&: out->axisCount, v2&: retained_axis_count, err_type: 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 (v1&: out->instanceSize, v2: retained_axis_count * 4 + (has_postscript_nameid ? 6 : 4),
380 err_type: 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 (key: i)) continue;
387 if (unlikely (!c->serializer->embed (axes_records[i])))
388 return_trace (false);
389 }
390
391 if (!c->serializer->check_assign (v1&: out->firstAxis, v2: get_size (), err_type: 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, axis_count: axisCount, has_postscript_nameid))
399 c->serializer->revert (snap);
400 }
401 return_trace (true);
402 }
403
404 public:
405 hb_array_t<const AxisRecord> get_axes () const
406 { return hb_array (array: &(this+firstAxis), length: axisCount); }
407
408 const InstanceRecord *get_instance (unsigned int i) const
409 {
410 if (unlikely (i >= instanceCount)) return nullptr;
411 return &StructAtOffset<InstanceRecord> (P: &StructAfter<InstanceRecord> (X: get_axes ()),
412 offset: 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

source code of flutter_engine/third_party/harfbuzz/src/hb-ot-var-fvar-table.hh