xref: /aosp_15_r20/external/harfbuzz_ng/src/hb-ot-layout-base-table.hh (revision 2d1272b857b1f7575e6e246373e1cb218663db8a)
1 /*
2  * Copyright © 2016  Elie Roux <[email protected]>
3  * Copyright © 2018  Google, Inc.
4  * Copyright © 2018-2019  Ebrahim Byagowi
5  *
6  *  This is part of HarfBuzz, a text shaping library.
7  *
8  * Permission is hereby granted, without written agreement and without
9  * license or royalty fees, to use, copy, modify, and distribute this
10  * software and its documentation for any purpose, provided that the
11  * above copyright notice and the following two paragraphs appear in
12  * all copies of this software.
13  *
14  * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
15  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
16  * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
17  * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
18  * DAMAGE.
19  *
20  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
21  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
22  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
23  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
24  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
25  *
26  * Google Author(s): Behdad Esfahbod
27  */
28 
29 #ifndef HB_OT_LAYOUT_BASE_TABLE_HH
30 #define HB_OT_LAYOUT_BASE_TABLE_HH
31 
32 #include "hb-open-type.hh"
33 #include "hb-ot-layout-common.hh"
34 
35 namespace OT {
36 
37 /*
38  * BASE -- Baseline
39  * https://docs.microsoft.com/en-us/typography/opentype/spec/base
40  */
41 
42 struct BaseCoordFormat1
43 {
get_coordOT::BaseCoordFormat144   hb_position_t get_coord (hb_font_t *font, hb_direction_t direction) const
45   {
46     return HB_DIRECTION_IS_HORIZONTAL (direction) ? font->em_scale_y (coordinate) : font->em_scale_x (coordinate);
47   }
48 
subsetOT::BaseCoordFormat149   bool subset (hb_subset_context_t *c) const
50   {
51     TRACE_SUBSET (this);
52     return_trace ((bool) c->serializer->embed (*this));
53   }
54 
sanitizeOT::BaseCoordFormat155   bool sanitize (hb_sanitize_context_t *c) const
56   {
57     TRACE_SANITIZE (this);
58     return_trace (c->check_struct (this));
59   }
60 
61   protected:
62   HBUINT16	format;		/* Format identifier--format = 1 */
63   FWORD		coordinate;	/* X or Y value, in design units */
64   public:
65   DEFINE_SIZE_STATIC (4);
66 };
67 
68 struct BaseCoordFormat2
69 {
get_coordOT::BaseCoordFormat270   hb_position_t get_coord (hb_font_t *font, hb_direction_t direction) const
71   {
72     /* TODO */
73     return HB_DIRECTION_IS_HORIZONTAL (direction) ? font->em_scale_y (coordinate) : font->em_scale_x (coordinate);
74   }
75 
subsetOT::BaseCoordFormat276   bool subset (hb_subset_context_t *c) const
77   {
78     TRACE_SUBSET (this);
79     auto *out = c->serializer->embed (*this);
80     if (unlikely (!out)) return_trace (false);
81 
82     return_trace (c->serializer->check_assign (out->referenceGlyph,
83                                                c->plan->glyph_map->get (referenceGlyph),
84                                                HB_SERIALIZE_ERROR_INT_OVERFLOW));
85   }
86 
sanitizeOT::BaseCoordFormat287   bool sanitize (hb_sanitize_context_t *c) const
88   {
89     TRACE_SANITIZE (this);
90     return_trace (c->check_struct (this));
91   }
92 
93   protected:
94   HBUINT16	format;		/* Format identifier--format = 2 */
95   FWORD		coordinate;	/* X or Y value, in design units */
96   HBGlyphID16	referenceGlyph;	/* Glyph ID of control glyph */
97   HBUINT16	coordPoint;	/* Index of contour point on the
98 				 * reference glyph */
99   public:
100   DEFINE_SIZE_STATIC (8);
101 };
102 
103 struct BaseCoordFormat3
104 {
get_coordOT::BaseCoordFormat3105   hb_position_t get_coord (hb_font_t *font,
106 			   const ItemVariationStore &var_store,
107 			   hb_direction_t direction) const
108   {
109     const Device &device = this+deviceTable;
110 
111     return HB_DIRECTION_IS_HORIZONTAL (direction)
112 	 ? font->em_scale_y (coordinate) + device.get_y_delta (font, var_store)
113 	 : font->em_scale_x (coordinate) + device.get_x_delta (font, var_store);
114   }
115 
collect_variation_indicesOT::BaseCoordFormat3116   void collect_variation_indices (hb_set_t& varidx_set /* OUT */) const
117   {
118     unsigned varidx = (this+deviceTable).get_variation_index ();
119     varidx_set.add (varidx);
120   }
121 
subsetOT::BaseCoordFormat3122   bool subset (hb_subset_context_t *c) const
123   {
124     TRACE_SUBSET (this);
125     auto *out = c->serializer->embed (*this);
126     if (unlikely (!out)) return_trace (false);
127 
128     if (!c->plan->pinned_at_default)
129     {
130       unsigned var_idx = (this+deviceTable).get_variation_index ();
131       if (var_idx != VarIdx::NO_VARIATION)
132       {
133         hb_pair_t<unsigned, int> *v;
134         if (!c->plan->base_variation_idx_map.has (var_idx, &v))
135           return_trace (false);
136 
137         if (unlikely (!c->serializer->check_assign (out->coordinate, coordinate + hb_second (*v),
138                                                     HB_SERIALIZE_ERROR_INT_OVERFLOW)))
139           return_trace (false);
140       }
141     }
142     return_trace (out->deviceTable.serialize_copy (c->serializer, deviceTable,
143                                                    this, 0,
144                                                    hb_serialize_context_t::Head,
145                                                    &c->plan->base_variation_idx_map));
146   }
147 
sanitizeOT::BaseCoordFormat3148   bool sanitize (hb_sanitize_context_t *c) const
149   {
150     TRACE_SANITIZE (this);
151     return_trace (likely (c->check_struct (this) &&
152 			  deviceTable.sanitize (c, this)));
153   }
154 
155   protected:
156   HBUINT16	format;		/* Format identifier--format = 3 */
157   FWORD		coordinate;	/* X or Y value, in design units */
158   Offset16To<Device>
159 		deviceTable;	/* Offset to Device table for X or
160 				 * Y value, from beginning of
161 				 * BaseCoord table (may be NULL). */
162   public:
163   DEFINE_SIZE_STATIC (6);
164 };
165 
166 struct BaseCoord
167 {
has_dataOT::BaseCoord168   bool has_data () const { return u.format; }
169 
get_coordOT::BaseCoord170   hb_position_t get_coord (hb_font_t            *font,
171 			   const ItemVariationStore &var_store,
172 			   hb_direction_t        direction) const
173   {
174     switch (u.format) {
175     case 1: hb_barrier (); return u.format1.get_coord (font, direction);
176     case 2: hb_barrier (); return u.format2.get_coord (font, direction);
177     case 3: hb_barrier (); return u.format3.get_coord (font, var_store, direction);
178     default:return 0;
179     }
180   }
181 
collect_variation_indicesOT::BaseCoord182   void collect_variation_indices (hb_set_t& varidx_set /* OUT */) const
183   {
184     switch (u.format) {
185     case 3: hb_barrier (); u.format3.collect_variation_indices (varidx_set);
186     default:return;
187     }
188   }
189 
190   template <typename context_t, typename ...Ts>
dispatchOT::BaseCoord191   typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
192   {
193     if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value ();
194     TRACE_DISPATCH (this, u.format);
195     switch (u.format) {
196     case 1: hb_barrier (); return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
197     case 2: hb_barrier (); return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...));
198     case 3: hb_barrier (); return_trace (c->dispatch (u.format3, std::forward<Ts> (ds)...));
199     default:return_trace (c->default_return_value ());
200     }
201   }
202 
sanitizeOT::BaseCoord203   bool sanitize (hb_sanitize_context_t *c) const
204   {
205     TRACE_SANITIZE (this);
206     if (unlikely (!u.format.sanitize (c))) return_trace (false);
207     hb_barrier ();
208     switch (u.format) {
209     case 1: hb_barrier (); return_trace (u.format1.sanitize (c));
210     case 2: hb_barrier (); return_trace (u.format2.sanitize (c));
211     case 3: hb_barrier (); return_trace (u.format3.sanitize (c));
212     default:return_trace (false);
213     }
214   }
215 
216   protected:
217   union {
218   HBUINT16		format;
219   BaseCoordFormat1	format1;
220   BaseCoordFormat2	format2;
221   BaseCoordFormat3	format3;
222   } u;
223   public:
224   DEFINE_SIZE_UNION (2, format);
225 };
226 
227 struct FeatMinMaxRecord
228 {
cmpOT::FeatMinMaxRecord229   int cmp (hb_tag_t key) const { return tag.cmp (key); }
230 
has_dataOT::FeatMinMaxRecord231   bool has_data () const { return tag; }
232 
get_feature_tagOT::FeatMinMaxRecord233   hb_tag_t get_feature_tag () const { return tag; }
234 
get_min_maxOT::FeatMinMaxRecord235   void get_min_max (const BaseCoord **min, const BaseCoord **max) const
236   {
237     if (likely (min)) *min = &(this+minCoord);
238     if (likely (max)) *max = &(this+maxCoord);
239   }
240 
collect_variation_indicesOT::FeatMinMaxRecord241   void collect_variation_indices (const hb_subset_plan_t* plan,
242                                   const void *base,
243                                   hb_set_t& varidx_set /* OUT */) const
244   {
245     if (!plan->layout_features.has (tag))
246       return;
247 
248     (base+minCoord).collect_variation_indices (varidx_set);
249     (base+maxCoord).collect_variation_indices (varidx_set);
250   }
251 
subsetOT::FeatMinMaxRecord252   bool subset (hb_subset_context_t *c,
253                const void *base) const
254   {
255     TRACE_SUBSET (this);
256     auto *out = c->serializer->embed (*this);
257     if (unlikely (!out)) return_trace (false);
258     if (!(out->minCoord.serialize_subset (c, minCoord, base)))
259       return_trace (false);
260 
261     return_trace (out->maxCoord.serialize_subset (c, maxCoord, base));
262   }
263 
sanitizeOT::FeatMinMaxRecord264   bool sanitize (hb_sanitize_context_t *c, const void *base) const
265   {
266     TRACE_SANITIZE (this);
267     return_trace (likely (c->check_struct (this) &&
268 			  minCoord.sanitize (c, base) &&
269 			  maxCoord.sanitize (c, base)));
270   }
271 
272   protected:
273   Tag		tag;		/* 4-byte feature identification tag--must
274 				 * match feature tag in FeatureList */
275   Offset16To<BaseCoord>
276 		minCoord;	/* Offset to BaseCoord table that defines
277 				 * the minimum extent value, from beginning
278 				 * of MinMax table (may be NULL) */
279   Offset16To<BaseCoord>
280 		maxCoord;	/* Offset to BaseCoord table that defines
281 				 * the maximum extent value, from beginning
282 				 * of MinMax table (may be NULL) */
283   public:
284   DEFINE_SIZE_STATIC (8);
285 };
286 
287 struct MinMax
288 {
get_min_maxOT::MinMax289   void get_min_max (hb_tag_t          feature_tag,
290 		    const BaseCoord **min,
291 		    const BaseCoord **max) const
292   {
293     const FeatMinMaxRecord &minMaxCoord = featMinMaxRecords.bsearch (feature_tag);
294     if (minMaxCoord.has_data ())
295       minMaxCoord.get_min_max (min, max);
296     else
297     {
298       if (likely (min)) *min = &(this+minCoord);
299       if (likely (max)) *max = &(this+maxCoord);
300     }
301   }
302 
collect_variation_indicesOT::MinMax303   void collect_variation_indices (const hb_subset_plan_t* plan,
304                                   hb_set_t& varidx_set /* OUT */) const
305   {
306     (this+minCoord).collect_variation_indices (varidx_set);
307     (this+maxCoord).collect_variation_indices (varidx_set);
308     for (const FeatMinMaxRecord& record : featMinMaxRecords)
309       record.collect_variation_indices (plan, this, varidx_set);
310   }
311 
subsetOT::MinMax312   bool subset (hb_subset_context_t *c) const
313   {
314     TRACE_SUBSET (this);
315     auto *out = c->serializer->start_embed (*this);
316     if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false);
317 
318     if (!(out->minCoord.serialize_subset (c, minCoord, this)) ||
319         !(out->maxCoord.serialize_subset (c, maxCoord, this)))
320       return_trace (false);
321 
322     unsigned len = 0;
323     for (const FeatMinMaxRecord& _ : featMinMaxRecords)
324     {
325       hb_tag_t feature_tag = _.get_feature_tag ();
326       if (!c->plan->layout_features.has (feature_tag))
327         continue;
328 
329       if (!_.subset (c, this)) return false;
330       len++;
331     }
332     return_trace (c->serializer->check_assign (out->featMinMaxRecords.len, len,
333                                                HB_SERIALIZE_ERROR_INT_OVERFLOW));
334   }
335 
sanitizeOT::MinMax336   bool sanitize (hb_sanitize_context_t *c) const
337   {
338     TRACE_SANITIZE (this);
339     return_trace (likely (c->check_struct (this) &&
340 			  minCoord.sanitize (c, this) &&
341 			  maxCoord.sanitize (c, this) &&
342 			  featMinMaxRecords.sanitize (c, this)));
343   }
344 
345   protected:
346   Offset16To<BaseCoord>
347 		minCoord;	/* Offset to BaseCoord table that defines
348 				 * minimum extent value, from the beginning
349 				 * of MinMax table (may be NULL) */
350   Offset16To<BaseCoord>
351 		maxCoord;	/* Offset to BaseCoord table that defines
352 				 * maximum extent value, from the beginning
353 				 * of MinMax table (may be NULL) */
354   SortedArray16Of<FeatMinMaxRecord>
355 		featMinMaxRecords;
356 				/* Array of FeatMinMaxRecords, in alphabetical
357 				 * order by featureTableTag */
358   public:
359   DEFINE_SIZE_ARRAY (6, featMinMaxRecords);
360 };
361 
362 struct BaseValues
363 {
get_base_coordOT::BaseValues364   const BaseCoord &get_base_coord (int baseline_tag_index) const
365   {
366     if (baseline_tag_index == -1) baseline_tag_index = defaultIndex;
367     return this+baseCoords[baseline_tag_index];
368   }
369 
collect_variation_indicesOT::BaseValues370   void collect_variation_indices (hb_set_t& varidx_set /* OUT */) const
371   {
372     for (const auto& _ : baseCoords)
373       (this+_).collect_variation_indices (varidx_set);
374   }
375 
subsetOT::BaseValues376   bool subset (hb_subset_context_t *c) const
377   {
378     TRACE_SUBSET (this);
379     auto *out = c->serializer->start_embed (*this);
380     if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false);
381     out->defaultIndex = defaultIndex;
382 
383     for (const auto& _ : baseCoords)
384       if (!subset_offset_array (c, out->baseCoords, this) (_))
385         return_trace (false);
386 
387     return_trace (bool (out->baseCoords));
388   }
389 
sanitizeOT::BaseValues390   bool sanitize (hb_sanitize_context_t *c) const
391   {
392     TRACE_SANITIZE (this);
393     return_trace (likely (c->check_struct (this) &&
394 			  baseCoords.sanitize (c, this)));
395   }
396 
397   protected:
398   Index		defaultIndex;	/* Index number of default baseline for this
399 				 * script — equals index position of baseline tag
400 				 * in baselineTags array of the BaseTagList */
401   Array16OfOffset16To<BaseCoord>
402 		baseCoords;	/* Number of BaseCoord tables defined — should equal
403 				 * baseTagCount in the BaseTagList
404 				 *
405 				 * Array of offsets to BaseCoord tables, from beginning of
406 				 * BaseValues table — order matches baselineTags array in
407 				 * the BaseTagList */
408   public:
409   DEFINE_SIZE_ARRAY (4, baseCoords);
410 };
411 
412 struct BaseLangSysRecord
413 {
cmpOT::BaseLangSysRecord414   int cmp (hb_tag_t key) const { return baseLangSysTag.cmp (key); }
415 
has_dataOT::BaseLangSysRecord416   bool has_data () const { return baseLangSysTag; }
417 
get_min_maxOT::BaseLangSysRecord418   const MinMax &get_min_max (const void* base) const { return base+minMax; }
419 
collect_variation_indicesOT::BaseLangSysRecord420   void collect_variation_indices (const void* base,
421                                   const hb_subset_plan_t* plan,
422                                   hb_set_t& varidx_set /* OUT */) const
423   { (base+minMax).collect_variation_indices (plan, varidx_set); }
424 
subsetOT::BaseLangSysRecord425   bool subset (hb_subset_context_t *c,
426                const void *base) const
427   {
428     TRACE_SUBSET (this);
429     auto *out = c->serializer->embed (*this);
430     if (unlikely (!out)) return_trace (false);
431 
432     return_trace (out->minMax.serialize_subset (c, minMax, base));
433   }
434 
sanitizeOT::BaseLangSysRecord435   bool sanitize (hb_sanitize_context_t *c, const void *base) const
436   {
437     TRACE_SANITIZE (this);
438     return_trace (likely (c->check_struct (this) &&
439 			  minMax.sanitize (c, base)));
440   }
441 
442   protected:
443   Tag		baseLangSysTag;	/* 4-byte language system identification tag */
444   Offset16To<MinMax>
445 		minMax;		/* Offset to MinMax table, from beginning
446 				 * of BaseScript table */
447   public:
448   DEFINE_SIZE_STATIC (6);
449 };
450 
451 struct BaseScript
452 {
get_min_maxOT::BaseScript453   const MinMax &get_min_max (hb_tag_t language_tag) const
454   {
455     const BaseLangSysRecord& record = baseLangSysRecords.bsearch (language_tag);
456     return record.has_data () ? record.get_min_max (this) : this+defaultMinMax;
457   }
458 
get_base_coordOT::BaseScript459   const BaseCoord &get_base_coord (int baseline_tag_index) const
460   { return (this+baseValues).get_base_coord (baseline_tag_index); }
461 
has_valuesOT::BaseScript462   bool has_values () const { return baseValues; }
has_min_maxOT::BaseScript463   bool has_min_max () const { return defaultMinMax; /* TODO What if only per-language is present? */ }
464 
collect_variation_indicesOT::BaseScript465   void collect_variation_indices (const hb_subset_plan_t* plan,
466                                   hb_set_t& varidx_set /* OUT */) const
467   {
468     (this+baseValues).collect_variation_indices (varidx_set);
469     (this+defaultMinMax).collect_variation_indices (plan, varidx_set);
470 
471     for (const BaseLangSysRecord& _ : baseLangSysRecords)
472       _.collect_variation_indices (this, plan, varidx_set);
473   }
474 
subsetOT::BaseScript475   bool subset (hb_subset_context_t *c) const
476   {
477     TRACE_SUBSET (this);
478     auto *out = c->serializer->start_embed (*this);
479     if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false);
480 
481     if (baseValues && !out->baseValues.serialize_subset (c, baseValues, this))
482       return_trace (false);
483 
484     if (defaultMinMax && !out->defaultMinMax.serialize_subset (c, defaultMinMax, this))
485       return_trace (false);
486 
487     for (const auto& _ : baseLangSysRecords)
488       if (!_.subset (c, this)) return_trace (false);
489 
490     return_trace (c->serializer->check_assign (out->baseLangSysRecords.len, baseLangSysRecords.len,
491                                                HB_SERIALIZE_ERROR_INT_OVERFLOW));
492   }
493 
sanitizeOT::BaseScript494   bool sanitize (hb_sanitize_context_t *c) const
495   {
496     TRACE_SANITIZE (this);
497     return_trace (likely (c->check_struct (this) &&
498 			  baseValues.sanitize (c, this) &&
499 			  defaultMinMax.sanitize (c, this) &&
500 			  baseLangSysRecords.sanitize (c, this)));
501   }
502 
503   protected:
504   Offset16To<BaseValues>
505 		baseValues;	/* Offset to BaseValues table, from beginning
506 				 * of BaseScript table (may be NULL) */
507   Offset16To<MinMax>
508 		defaultMinMax;	/* Offset to MinMax table, from beginning of
509 				 * BaseScript table (may be NULL) */
510   SortedArray16Of<BaseLangSysRecord>
511 		baseLangSysRecords;
512 				/* Number of BaseLangSysRecords
513 				 * defined — may be zero (0) */
514 
515   public:
516   DEFINE_SIZE_ARRAY (6, baseLangSysRecords);
517 };
518 
519 struct BaseScriptList;
520 struct BaseScriptRecord
521 {
cmpOT::BaseScriptRecord522   int cmp (hb_tag_t key) const { return baseScriptTag.cmp (key); }
523 
has_dataOT::BaseScriptRecord524   bool has_data () const { return baseScriptTag; }
525 
get_script_tagOT::BaseScriptRecord526   hb_tag_t get_script_tag () const { return baseScriptTag; }
527 
get_base_scriptOT::BaseScriptRecord528   const BaseScript &get_base_script (const BaseScriptList *list) const
529   { return list+baseScript; }
530 
collect_variation_indicesOT::BaseScriptRecord531   void collect_variation_indices (const hb_subset_plan_t* plan,
532                                   const void* list,
533                                   hb_set_t& varidx_set /* OUT */) const
534   {
535     if (!plan->layout_scripts.has (baseScriptTag))
536       return;
537 
538     (list+baseScript).collect_variation_indices (plan, varidx_set);
539   }
540 
subsetOT::BaseScriptRecord541   bool subset (hb_subset_context_t *c,
542                const void *base) const
543   {
544     TRACE_SUBSET (this);
545     auto *out = c->serializer->embed (*this);
546     if (unlikely (!out)) return_trace (false);
547 
548     return_trace (out->baseScript.serialize_subset (c, baseScript, base));
549   }
550 
sanitizeOT::BaseScriptRecord551   bool sanitize (hb_sanitize_context_t *c, const void *base) const
552   {
553     TRACE_SANITIZE (this);
554     return_trace (likely (c->check_struct (this) &&
555 			  baseScript.sanitize (c, base)));
556   }
557 
558   protected:
559   Tag		baseScriptTag;	/* 4-byte script identification tag */
560   Offset16To<BaseScript>
561 		baseScript;	/* Offset to BaseScript table, from beginning
562 				 * of BaseScriptList */
563 
564   public:
565   DEFINE_SIZE_STATIC (6);
566 };
567 
568 struct BaseScriptList
569 {
get_base_scriptOT::BaseScriptList570   const BaseScript &get_base_script (hb_tag_t script) const
571   {
572     const BaseScriptRecord *record = &baseScriptRecords.bsearch (script);
573     if (!record->has_data ()) record = &baseScriptRecords.bsearch (HB_TAG ('D','F','L','T'));
574     return record->has_data () ? record->get_base_script (this) : Null (BaseScript);
575   }
576 
collect_variation_indicesOT::BaseScriptList577   void collect_variation_indices (const hb_subset_plan_t* plan,
578                                   hb_set_t& varidx_set /* OUT */) const
579   {
580     for (const BaseScriptRecord& _ : baseScriptRecords)
581       _.collect_variation_indices (plan, this, varidx_set);
582   }
583 
subsetOT::BaseScriptList584   bool subset (hb_subset_context_t *c) const
585   {
586     TRACE_SUBSET (this);
587     auto *out = c->serializer->start_embed (*this);
588     if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false);
589 
590     unsigned len = 0;
591     for (const BaseScriptRecord& _ : baseScriptRecords)
592     {
593       hb_tag_t script_tag = _.get_script_tag ();
594       if (!c->plan->layout_scripts.has (script_tag))
595         continue;
596 
597       if (!_.subset (c, this)) return false;
598       len++;
599     }
600     return_trace (c->serializer->check_assign (out->baseScriptRecords.len, len,
601                                                HB_SERIALIZE_ERROR_INT_OVERFLOW));
602   }
603 
sanitizeOT::BaseScriptList604   bool sanitize (hb_sanitize_context_t *c) const
605   {
606     TRACE_SANITIZE (this);
607     return_trace (c->check_struct (this) &&
608 		  baseScriptRecords.sanitize (c, this));
609   }
610 
611   protected:
612   SortedArray16Of<BaseScriptRecord>
613 			baseScriptRecords;
614 
615   public:
616   DEFINE_SIZE_ARRAY (2, baseScriptRecords);
617 };
618 
619 struct Axis
620 {
get_baselineOT::Axis621   bool get_baseline (hb_tag_t          baseline_tag,
622 		     hb_tag_t          script_tag,
623 		     hb_tag_t          language_tag,
624 		     const BaseCoord **coord) const
625   {
626     const BaseScript &base_script = (this+baseScriptList).get_base_script (script_tag);
627     if (!base_script.has_values ())
628     {
629       *coord = nullptr;
630       return false;
631     }
632 
633     if (likely (coord))
634     {
635       unsigned int tag_index = 0;
636       if (!(this+baseTagList).bfind (baseline_tag, &tag_index))
637       {
638         *coord = nullptr;
639         return false;
640       }
641       *coord = &base_script.get_base_coord (tag_index);
642     }
643 
644     return true;
645   }
646 
get_min_maxOT::Axis647   bool get_min_max (hb_tag_t          script_tag,
648 		    hb_tag_t          language_tag,
649 		    hb_tag_t          feature_tag,
650 		    const BaseCoord **min_coord,
651 		    const BaseCoord **max_coord) const
652   {
653     const BaseScript &base_script = (this+baseScriptList).get_base_script (script_tag);
654     if (!base_script.has_min_max ())
655     {
656       *min_coord = *max_coord = nullptr;
657       return false;
658     }
659 
660     base_script.get_min_max (language_tag).get_min_max (feature_tag, min_coord, max_coord);
661 
662     return true;
663   }
664 
collect_variation_indicesOT::Axis665   void collect_variation_indices (const hb_subset_plan_t* plan,
666                                   hb_set_t& varidx_set /* OUT */) const
667   { (this+baseScriptList).collect_variation_indices (plan, varidx_set); }
668 
subsetOT::Axis669   bool subset (hb_subset_context_t *c) const
670   {
671     TRACE_SUBSET (this);
672     auto *out = c->serializer->embed (*this);
673     if (unlikely (!out)) return_trace (false);
674 
675     out->baseTagList.serialize_copy (c->serializer, baseTagList, this);
676     return_trace (out->baseScriptList.serialize_subset (c, baseScriptList, this));
677   }
678 
sanitizeOT::Axis679   bool sanitize (hb_sanitize_context_t *c) const
680   {
681     TRACE_SANITIZE (this);
682     return_trace (likely (c->check_struct (this) &&
683 			  baseTagList.sanitize (c, this) &&
684 			  baseScriptList.sanitize (c, this)));
685   }
686 
687   protected:
688   Offset16To<SortedArray16Of<Tag>>
689 		baseTagList;	/* Offset to BaseTagList table, from beginning
690 				 * of Axis table (may be NULL)
691 				 * Array of 4-byte baseline identification tags — must
692 				 * be in alphabetical order */
693   Offset16To<BaseScriptList>
694 		baseScriptList;	/* Offset to BaseScriptList table, from beginning
695 				 * of Axis table
696 				 * Array of BaseScriptRecords, in alphabetical order
697 				 * by baseScriptTag */
698 
699   public:
700   DEFINE_SIZE_STATIC (4);
701 };
702 
703 struct BASE
704 {
705   static constexpr hb_tag_t tableTag = HB_OT_TAG_BASE;
706 
get_axisOT::BASE707   const Axis &get_axis (hb_direction_t direction) const
708   { return HB_DIRECTION_IS_VERTICAL (direction) ? this+vAxis : this+hAxis; }
709 
has_var_storeOT::BASE710   bool has_var_store () const
711   { return version.to_int () >= 0x00010001u && varStore != 0; }
712 
get_var_storeOT::BASE713   const ItemVariationStore &get_var_store () const
714   { return version.to_int () < 0x00010001u ? Null (ItemVariationStore) : this+varStore; }
715 
collect_variation_indicesOT::BASE716   void collect_variation_indices (const hb_subset_plan_t* plan,
717                                   hb_set_t& varidx_set /* OUT */) const
718   {
719     (this+hAxis).collect_variation_indices (plan, varidx_set);
720     (this+vAxis).collect_variation_indices (plan, varidx_set);
721   }
722 
subset_varstoreOT::BASE723   bool subset_varstore (hb_subset_context_t *c,
724                         BASE *out /* OUT */) const
725   {
726     TRACE_SUBSET (this);
727     if (!c->serializer->allocate_size<Offset32To<ItemVariationStore>> (Offset32To<ItemVariationStore>::static_size))
728         return_trace (false);
729     if (!c->plan->normalized_coords)
730       return_trace (out->varStore.serialize_subset (c, varStore, this, c->plan->base_varstore_inner_maps.as_array ()));
731 
732     if (c->plan->all_axes_pinned)
733       return_trace (true);
734 
735     item_variations_t item_vars;
736     if (!item_vars.instantiate (this+varStore, c->plan, true, true,
737                                 c->plan->base_varstore_inner_maps.as_array ()))
738       return_trace (false);
739 
740     if (!out->varStore.serialize_serialize (c->serializer,
741                                             item_vars.has_long_word (),
742                                             c->plan->axis_tags,
743                                             item_vars.get_region_list (),
744                                             item_vars.get_vardata_encodings ()))
745       return_trace (false);
746 
747     const hb_map_t &varidx_map = item_vars.get_varidx_map ();
748     /* base_variation_idx_map in the plan is old_varidx->(varidx, delta)
749      * mapping, new varidx is generated for subsetting, we need to remap this
750      * after instancing */
751     for (auto _ : c->plan->base_variation_idx_map.iter_ref ())
752     {
753       uint32_t varidx = _.second.first;
754       uint32_t *new_varidx;
755       if (varidx_map.has (varidx, &new_varidx))
756         _.second.first = *new_varidx;
757       else
758         _.second.first = HB_OT_LAYOUT_NO_VARIATIONS_INDEX;
759     }
760     return_trace (true);
761   }
762 
subsetOT::BASE763   bool subset (hb_subset_context_t *c) const
764   {
765     TRACE_SUBSET (this);
766     auto *out = c->serializer->start_embed (*this);
767     if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false);
768 
769     out->version = version;
770     if (has_var_store () && !subset_varstore (c, out))
771         return_trace (false);
772 
773     if (hAxis && !out->hAxis.serialize_subset (c, hAxis, this))
774       return_trace (false);
775 
776     if (vAxis && !out->vAxis.serialize_subset (c, vAxis, this))
777       return_trace (false);
778 
779     return_trace (true);
780   }
781 
get_baselineOT::BASE782   bool get_baseline (hb_font_t      *font,
783 		     hb_tag_t        baseline_tag,
784 		     hb_direction_t  direction,
785 		     hb_tag_t        script_tag,
786 		     hb_tag_t        language_tag,
787 		     hb_position_t  *base) const
788   {
789     const BaseCoord *base_coord = nullptr;
790     if (unlikely (!get_axis (direction).get_baseline (baseline_tag, script_tag, language_tag, &base_coord) ||
791 		  !base_coord || !base_coord->has_data ()))
792       return false;
793 
794     if (likely (base))
795       *base = base_coord->get_coord (font, get_var_store (), direction);
796 
797     return true;
798   }
799 
get_min_maxOT::BASE800   bool get_min_max (hb_font_t      *font,
801 		    hb_direction_t  direction,
802 		    hb_tag_t        script_tag,
803 		    hb_tag_t        language_tag,
804 		    hb_tag_t        feature_tag,
805 		    hb_position_t  *min,
806 		    hb_position_t  *max) const
807   {
808     const BaseCoord *min_coord, *max_coord;
809     if (!get_axis (direction).get_min_max (script_tag, language_tag, feature_tag,
810 					   &min_coord, &max_coord))
811       return false;
812 
813     const ItemVariationStore &var_store = get_var_store ();
814     if (likely (min && min_coord)) *min = min_coord->get_coord (font, var_store, direction);
815     if (likely (max && max_coord)) *max = max_coord->get_coord (font, var_store, direction);
816     return true;
817   }
818 
sanitizeOT::BASE819   bool sanitize (hb_sanitize_context_t *c) const
820   {
821     TRACE_SANITIZE (this);
822     return_trace (likely (c->check_struct (this) &&
823 			  hb_barrier () &&
824 			  likely (version.major == 1) &&
825 			  hAxis.sanitize (c, this) &&
826 			  vAxis.sanitize (c, this) &&
827 			  (version.to_int () < 0x00010001u || varStore.sanitize (c, this))));
828   }
829 
830   protected:
831   FixedVersion<>version;	/* Version of the BASE table */
832   Offset16To<Axis>hAxis;		/* Offset to horizontal Axis table, from beginning
833 				 * of BASE table (may be NULL) */
834   Offset16To<Axis>vAxis;		/* Offset to vertical Axis table, from beginning
835 				 * of BASE table (may be NULL) */
836   Offset32To<ItemVariationStore>
837 		varStore;	/* Offset to the table of Item Variation
838 				 * Store--from beginning of BASE
839 				 * header (may be NULL).  Introduced
840 				 * in version 0x00010001. */
841   public:
842   DEFINE_SIZE_MIN (8);
843 };
844 
845 
846 } /* namespace OT */
847 
848 
849 #endif /* HB_OT_LAYOUT_BASE_TABLE_HH */
850