1 /*
2  * Copyright © 2016  Elie Roux <elie.roux@telecom-bretagne.eu>
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 () const { return coordinate; }
45 
sanitizeOT::BaseCoordFormat146   bool sanitize (hb_sanitize_context_t *c) const
47   {
48     TRACE_SANITIZE (this);
49     return_trace (likely (c->check_struct (this)));
50   }
51 
52   protected:
53   HBUINT16	format;		/* Format identifier--format = 1 */
54   FWORD		coordinate;	/* X or Y value, in design units */
55   public:
56   DEFINE_SIZE_STATIC (4);
57 };
58 
59 struct BaseCoordFormat2
60 {
get_coordOT::BaseCoordFormat261   hb_position_t get_coord () const
62   {
63     /* TODO */
64     return coordinate;
65   }
66 
sanitizeOT::BaseCoordFormat267   bool sanitize (hb_sanitize_context_t *c) const
68   {
69     TRACE_SANITIZE (this);
70     return_trace (c->check_struct (this));
71   }
72 
73   protected:
74   HBUINT16	format;		/* Format identifier--format = 2 */
75   FWORD		coordinate;	/* X or Y value, in design units */
76   HBGlyphID	referenceGlyph;	/* Glyph ID of control glyph */
77   HBUINT16	coordPoint;	/* Index of contour point on the
78 				 * reference glyph */
79   public:
80   DEFINE_SIZE_STATIC (8);
81 };
82 
83 struct BaseCoordFormat3
84 {
get_coordOT::BaseCoordFormat385   hb_position_t get_coord (hb_font_t *font,
86 			   const VariationStore &var_store,
87 			   hb_direction_t direction) const
88   {
89     const Device &device = this+deviceTable;
90     return coordinate + (HB_DIRECTION_IS_VERTICAL (direction) ?
91 			 device.get_y_delta (font, var_store) :
92 			 device.get_x_delta (font, var_store));
93   }
94 
95 
sanitizeOT::BaseCoordFormat396   bool sanitize (hb_sanitize_context_t *c) const
97   {
98     TRACE_SANITIZE (this);
99     return_trace (likely (c->check_struct (this) &&
100 			  deviceTable.sanitize (c, this)));
101   }
102 
103   protected:
104   HBUINT16	format;		/* Format identifier--format = 3 */
105   FWORD		coordinate;	/* X or Y value, in design units */
106   OffsetTo<Device>
107 		deviceTable;	/* Offset to Device table for X or
108 				 * Y value, from beginning of
109 				 * BaseCoord table (may be NULL). */
110   public:
111   DEFINE_SIZE_STATIC (6);
112 };
113 
114 struct BaseCoord
115 {
has_dataOT::BaseCoord116   bool has_data () const { return u.format; }
117 
get_coordOT::BaseCoord118   hb_position_t get_coord (hb_font_t            *font,
119 			   const VariationStore &var_store,
120 			   hb_direction_t        direction) const
121   {
122     switch (u.format) {
123     case 1: return u.format1.get_coord ();
124     case 2: return u.format2.get_coord ();
125     case 3: return u.format3.get_coord (font, var_store, direction);
126     default:return 0;
127     }
128   }
129 
sanitizeOT::BaseCoord130   bool sanitize (hb_sanitize_context_t *c) const
131   {
132     TRACE_SANITIZE (this);
133     if (unlikely (!u.format.sanitize (c))) return_trace (false);
134     switch (u.format) {
135     case 1: return_trace (u.format1.sanitize (c));
136     case 2: return_trace (u.format2.sanitize (c));
137     case 3: return_trace (u.format3.sanitize (c));
138     default:return_trace (false);
139     }
140   }
141 
142   protected:
143   union {
144   HBUINT16		format;
145   BaseCoordFormat1	format1;
146   BaseCoordFormat2	format2;
147   BaseCoordFormat3	format3;
148   } u;
149   public:
150   DEFINE_SIZE_UNION (2, format);
151 };
152 
153 struct FeatMinMaxRecord
154 {
cmpOT::FeatMinMaxRecord155   int cmp (hb_tag_t key) const { return tag.cmp (key); }
156 
has_dataOT::FeatMinMaxRecord157   bool has_data () const { return tag; }
158 
get_min_maxOT::FeatMinMaxRecord159   void get_min_max (const BaseCoord **min, const BaseCoord **max) const
160   {
161     if (likely (min)) *min = &(this+minCoord);
162     if (likely (max)) *max = &(this+maxCoord);
163   }
164 
sanitizeOT::FeatMinMaxRecord165   bool sanitize (hb_sanitize_context_t *c, const void *base) const
166   {
167     TRACE_SANITIZE (this);
168     return_trace (likely (c->check_struct (this) &&
169 			  minCoord.sanitize (c, this) &&
170 			  maxCoord.sanitize (c, this)));
171   }
172 
173   protected:
174   Tag		tag;		/* 4-byte feature identification tag--must
175 				 * match feature tag in FeatureList */
176   OffsetTo<BaseCoord>
177 		minCoord;	/* Offset to BaseCoord table that defines
178 				 * the minimum extent value, from beginning
179 				 * of MinMax table (may be NULL) */
180   OffsetTo<BaseCoord>
181 		maxCoord;	/* Offset to BaseCoord table that defines
182 				 * the maximum extent value, from beginning
183 				 * of MinMax table (may be NULL) */
184   public:
185   DEFINE_SIZE_STATIC (8);
186 
187 };
188 
189 struct MinMax
190 {
get_min_maxOT::MinMax191   void get_min_max (hb_tag_t          feature_tag,
192 		    const BaseCoord **min,
193 		    const BaseCoord **max) const
194   {
195     const FeatMinMaxRecord &minMaxCoord = featMinMaxRecords.bsearch (feature_tag);
196     if (minMaxCoord.has_data ())
197       minMaxCoord.get_min_max (min, max);
198     else
199     {
200       if (likely (min)) *min = &(this+minCoord);
201       if (likely (max)) *max = &(this+maxCoord);
202     }
203   }
204 
sanitizeOT::MinMax205   bool sanitize (hb_sanitize_context_t *c) const
206   {
207     TRACE_SANITIZE (this);
208     return_trace (likely (c->check_struct (this) &&
209 			  minCoord.sanitize (c, this) &&
210 			  maxCoord.sanitize (c, this) &&
211 			  featMinMaxRecords.sanitize (c, this)));
212   }
213 
214   protected:
215   OffsetTo<BaseCoord>
216 		minCoord;	/* Offset to BaseCoord table that defines
217 				 * minimum extent value, from the beginning
218 				 * of MinMax table (may be NULL) */
219   OffsetTo<BaseCoord>
220 		maxCoord;	/* Offset to BaseCoord table that defines
221 				 * maximum extent value, from the beginning
222 				 * of MinMax table (may be NULL) */
223   SortedArrayOf<FeatMinMaxRecord>
224 		featMinMaxRecords;
225 				/* Array of FeatMinMaxRecords, in alphabetical
226 				 * order by featureTableTag */
227   public:
228   DEFINE_SIZE_ARRAY (6, featMinMaxRecords);
229 };
230 
231 struct BaseValues
232 {
get_base_coordOT::BaseValues233   const BaseCoord &get_base_coord (int baseline_tag_index) const
234   {
235     if (baseline_tag_index == -1) baseline_tag_index = defaultIndex;
236     return this+baseCoords[baseline_tag_index];
237   }
238 
sanitizeOT::BaseValues239   bool sanitize (hb_sanitize_context_t *c) const
240   {
241     TRACE_SANITIZE (this);
242     return_trace (likely (c->check_struct (this) &&
243 			  baseCoords.sanitize (c, this)));
244   }
245 
246   protected:
247   Index		defaultIndex;	/* Index number of default baseline for this
248 				 * script — equals index position of baseline tag
249 				 * in baselineTags array of the BaseTagList */
250   OffsetArrayOf<BaseCoord>
251 		baseCoords;	/* Number of BaseCoord tables defined — should equal
252 				 * baseTagCount in the BaseTagList
253 				 *
254 				 * Array of offsets to BaseCoord tables, from beginning of
255 				 * BaseValues table — order matches baselineTags array in
256 				 * the BaseTagList */
257   public:
258   DEFINE_SIZE_ARRAY (4, baseCoords);
259 };
260 
261 struct BaseLangSysRecord
262 {
cmpOT::BaseLangSysRecord263   int cmp (hb_tag_t key) const { return baseLangSysTag.cmp (key); }
264 
has_dataOT::BaseLangSysRecord265   bool has_data () const { return baseLangSysTag; }
266 
get_min_maxOT::BaseLangSysRecord267   const MinMax &get_min_max () const { return this+minMax; }
268 
sanitizeOT::BaseLangSysRecord269   bool sanitize (hb_sanitize_context_t *c, const void *base) const
270   {
271     TRACE_SANITIZE (this);
272     return_trace (likely (c->check_struct (this) &&
273 			  minMax.sanitize (c, this)));
274   }
275 
276   protected:
277   Tag		baseLangSysTag;	/* 4-byte language system identification tag */
278   OffsetTo<MinMax>
279 		minMax;		/* Offset to MinMax table, from beginning
280 				 * of BaseScript table */
281   public:
282   DEFINE_SIZE_STATIC (6);
283 };
284 
285 struct BaseScript
286 {
get_min_maxOT::BaseScript287   const MinMax &get_min_max (hb_tag_t language_tag) const
288   {
289     const BaseLangSysRecord& record = baseLangSysRecords.bsearch (language_tag);
290     return record.has_data () ? record.get_min_max () : this+defaultMinMax;
291   }
292 
get_base_coordOT::BaseScript293   const BaseCoord &get_base_coord (int baseline_tag_index) const
294   { return (this+baseValues).get_base_coord (baseline_tag_index); }
295 
has_dataOT::BaseScript296   bool has_data () const { return baseValues; }
297 
sanitizeOT::BaseScript298   bool sanitize (hb_sanitize_context_t *c) const
299   {
300     TRACE_SANITIZE (this);
301     return_trace (likely (c->check_struct (this) &&
302 			  baseValues.sanitize (c, this) &&
303 			  defaultMinMax.sanitize (c, this) &&
304 			  baseLangSysRecords.sanitize (c, this)));
305   }
306 
307   protected:
308   OffsetTo<BaseValues>
309 		baseValues;	/* Offset to BaseValues table, from beginning
310 				 * of BaseScript table (may be NULL) */
311   OffsetTo<MinMax>
312 		defaultMinMax;	/* Offset to MinMax table, from beginning of
313 				 * BaseScript table (may be NULL) */
314   SortedArrayOf<BaseLangSysRecord>
315 		baseLangSysRecords;
316 				/* Number of BaseLangSysRecords
317 				 * defined — may be zero (0) */
318 
319   public:
320   DEFINE_SIZE_ARRAY (6, baseLangSysRecords);
321 };
322 
323 struct BaseScriptList;
324 struct BaseScriptRecord
325 {
cmpOT::BaseScriptRecord326   int cmp (hb_tag_t key) const { return baseScriptTag.cmp (key); }
327 
has_dataOT::BaseScriptRecord328   bool has_data () const { return baseScriptTag; }
329 
get_base_scriptOT::BaseScriptRecord330   const BaseScript &get_base_script (const BaseScriptList *list) const
331   { return list+baseScript; }
332 
sanitizeOT::BaseScriptRecord333   bool sanitize (hb_sanitize_context_t *c, const void *base) const
334   {
335     TRACE_SANITIZE (this);
336     return_trace (likely (c->check_struct (this) &&
337 			  baseScript.sanitize (c, base)));
338   }
339 
340   protected:
341   Tag		baseScriptTag;	/* 4-byte script identification tag */
342   OffsetTo<BaseScript>
343 		baseScript;	/* Offset to BaseScript table, from beginning
344 				 * of BaseScriptList */
345 
346   public:
347   DEFINE_SIZE_STATIC (6);
348 };
349 
350 struct BaseScriptList
351 {
get_base_scriptOT::BaseScriptList352   const BaseScript &get_base_script (hb_tag_t script) const
353   {
354     const BaseScriptRecord *record = &baseScriptRecords.bsearch (script);
355     if (!record->has_data ()) record = &baseScriptRecords.bsearch (HB_TAG ('D','F','L','T'));
356     return record->has_data () ? record->get_base_script (this) : Null (BaseScript);
357   }
358 
sanitizeOT::BaseScriptList359   bool sanitize (hb_sanitize_context_t *c) const
360   {
361     TRACE_SANITIZE (this);
362     return_trace (c->check_struct (this) &&
363 		  baseScriptRecords.sanitize (c, this));
364   }
365 
366   protected:
367   SortedArrayOf<BaseScriptRecord>
368 			baseScriptRecords;
369 
370   public:
371   DEFINE_SIZE_ARRAY (2, baseScriptRecords);
372 };
373 
374 struct Axis
375 {
get_baselineOT::Axis376   bool get_baseline (hb_tag_t          baseline_tag,
377 		     hb_tag_t          script_tag,
378 		     hb_tag_t          language_tag,
379 		     const BaseCoord **coord) const
380   {
381     const BaseScript &base_script = (this+baseScriptList).get_base_script (script_tag);
382     if (!base_script.has_data ()) return false;
383 
384     if (likely (coord))
385     {
386       unsigned int tag_index = 0;
387       (this+baseTagList).bfind (baseline_tag, &tag_index);
388       *coord = &base_script.get_base_coord (tag_index);
389     }
390 
391     return true;
392   }
393 
get_min_maxOT::Axis394   bool get_min_max (hb_tag_t          script_tag,
395 		    hb_tag_t          language_tag,
396 		    hb_tag_t          feature_tag,
397 		    const BaseCoord **min_coord,
398 		    const BaseCoord **max_coord) const
399   {
400     const BaseScript &base_script = (this+baseScriptList).get_base_script (script_tag);
401     if (!base_script.has_data ()) return false;
402 
403     base_script.get_min_max (language_tag).get_min_max (feature_tag, min_coord, max_coord);
404 
405     return true;
406   }
407 
sanitizeOT::Axis408   bool sanitize (hb_sanitize_context_t *c) const
409   {
410     TRACE_SANITIZE (this);
411     return_trace (likely (c->check_struct (this) &&
412 			  (this+baseTagList).sanitize (c) &&
413 			  (this+baseScriptList).sanitize (c)));
414   }
415 
416   protected:
417   OffsetTo<SortedArrayOf<Tag>>
418 		baseTagList;	/* Offset to BaseTagList table, from beginning
419 				 * of Axis table (may be NULL)
420 				 * Array of 4-byte baseline identification tags — must
421 				 * be in alphabetical order */
422   OffsetTo<BaseScriptList>
423 		baseScriptList;	/* Offset to BaseScriptList table, from beginning
424 				 * of Axis table
425 				 * Array of BaseScriptRecords, in alphabetical order
426 				 * by baseScriptTag */
427 
428   public:
429   DEFINE_SIZE_STATIC (4);
430 };
431 
432 struct BASE
433 {
434   static constexpr hb_tag_t tableTag = HB_OT_TAG_BASE;
435 
get_axisOT::BASE436   const Axis &get_axis (hb_direction_t direction) const
437   { return HB_DIRECTION_IS_VERTICAL (direction) ? this+vAxis : this+hAxis; }
438 
get_var_storeOT::BASE439   const VariationStore &get_var_store () const
440   { return version.to_int () < 0x00010001u ? Null (VariationStore) : this+varStore; }
441 
get_baselineOT::BASE442   bool get_baseline (hb_font_t      *font,
443 		     hb_tag_t        baseline_tag,
444 		     hb_direction_t  direction,
445 		     hb_tag_t        script_tag,
446 		     hb_tag_t        language_tag,
447 		     hb_position_t  *base) const
448   {
449     const BaseCoord *base_coord = nullptr;
450     if (unlikely (!get_axis (direction).get_baseline (baseline_tag, script_tag, language_tag, &base_coord) ||
451 		  !base_coord || !base_coord->has_data ()))
452       return false;
453 
454     if (likely (base))
455       *base = base_coord->get_coord (font, get_var_store (), direction);
456 
457     return true;
458   }
459 
460   /* TODO: Expose this separately sometime? */
get_min_maxOT::BASE461   bool get_min_max (hb_font_t      *font,
462 		    hb_direction_t  direction,
463 		    hb_tag_t        script_tag,
464 		    hb_tag_t        language_tag,
465 		    hb_tag_t        feature_tag,
466 		    hb_position_t  *min,
467 		    hb_position_t  *max)
468   {
469     const BaseCoord *min_coord, *max_coord;
470     if (!get_axis (direction).get_min_max (script_tag, language_tag, feature_tag,
471 					   &min_coord, &max_coord))
472       return false;
473 
474     const VariationStore &var_store = get_var_store ();
475     if (likely (min && min_coord)) *min = min_coord->get_coord (font, var_store, direction);
476     if (likely (max && max_coord)) *max = max_coord->get_coord (font, var_store, direction);
477     return true;
478   }
479 
sanitizeOT::BASE480   bool sanitize (hb_sanitize_context_t *c) const
481   {
482     TRACE_SANITIZE (this);
483     return_trace (likely (c->check_struct (this) &&
484 			  likely (version.major == 1) &&
485 			  hAxis.sanitize (c, this) &&
486 			  vAxis.sanitize (c, this) &&
487 			  (version.to_int () < 0x00010001u || varStore.sanitize (c, this))));
488   }
489 
490   protected:
491   FixedVersion<>version;	/* Version of the BASE table */
492   OffsetTo<Axis>hAxis;		/* Offset to horizontal Axis table, from beginning
493 				 * of BASE table (may be NULL) */
494   OffsetTo<Axis>vAxis;		/* Offset to vertical Axis table, from beginning
495 				 * of BASE table (may be NULL) */
496   LOffsetTo<VariationStore>
497 		varStore;	/* Offset to the table of Item Variation
498 				 * Store--from beginning of BASE
499 				 * header (may be NULL).  Introduced
500 				 * in version 0x00010001. */
501   public:
502   DEFINE_SIZE_MIN (8);
503 };
504 
505 
506 } /* namespace OT */
507 
508 
509 #endif /* HB_OT_LAYOUT_BASE_TABLE_HH */
510