• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 (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 
sanitizeOT::BaseCoordFormat149   bool sanitize (hb_sanitize_context_t *c) const
50   {
51     TRACE_SANITIZE (this);
52     return_trace (c->check_struct (this));
53   }
54 
55   protected:
56   HBUINT16	format;		/* Format identifier--format = 1 */
57   FWORD		coordinate;	/* X or Y value, in design units */
58   public:
59   DEFINE_SIZE_STATIC (4);
60 };
61 
62 struct BaseCoordFormat2
63 {
get_coordOT::BaseCoordFormat264   hb_position_t get_coord (hb_font_t *font, hb_direction_t direction) const
65   {
66     /* TODO */
67     return HB_DIRECTION_IS_HORIZONTAL (direction) ? font->em_scale_y (coordinate) : font->em_scale_x (coordinate);
68   }
69 
sanitizeOT::BaseCoordFormat270   bool sanitize (hb_sanitize_context_t *c) const
71   {
72     TRACE_SANITIZE (this);
73     return_trace (c->check_struct (this));
74   }
75 
76   protected:
77   HBUINT16	format;		/* Format identifier--format = 2 */
78   FWORD		coordinate;	/* X or Y value, in design units */
79   HBGlyphID16	referenceGlyph;	/* Glyph ID of control glyph */
80   HBUINT16	coordPoint;	/* Index of contour point on the
81 				 * reference glyph */
82   public:
83   DEFINE_SIZE_STATIC (8);
84 };
85 
86 struct BaseCoordFormat3
87 {
get_coordOT::BaseCoordFormat388   hb_position_t get_coord (hb_font_t *font,
89 			   const VariationStore &var_store,
90 			   hb_direction_t direction) const
91   {
92     const Device &device = this+deviceTable;
93 
94     return HB_DIRECTION_IS_HORIZONTAL (direction)
95 	 ? font->em_scale_y (coordinate) + device.get_y_delta (font, var_store)
96 	 : font->em_scale_x (coordinate) + device.get_x_delta (font, var_store);
97   }
98 
99 
sanitizeOT::BaseCoordFormat3100   bool sanitize (hb_sanitize_context_t *c) const
101   {
102     TRACE_SANITIZE (this);
103     return_trace (likely (c->check_struct (this) &&
104 			  deviceTable.sanitize (c, this)));
105   }
106 
107   protected:
108   HBUINT16	format;		/* Format identifier--format = 3 */
109   FWORD		coordinate;	/* X or Y value, in design units */
110   Offset16To<Device>
111 		deviceTable;	/* Offset to Device table for X or
112 				 * Y value, from beginning of
113 				 * BaseCoord table (may be NULL). */
114   public:
115   DEFINE_SIZE_STATIC (6);
116 };
117 
118 struct BaseCoord
119 {
has_dataOT::BaseCoord120   bool has_data () const { return u.format; }
121 
get_coordOT::BaseCoord122   hb_position_t get_coord (hb_font_t            *font,
123 			   const VariationStore &var_store,
124 			   hb_direction_t        direction) const
125   {
126     switch (u.format) {
127     case 1: return u.format1.get_coord (font, direction);
128     case 2: return u.format2.get_coord (font, direction);
129     case 3: return u.format3.get_coord (font, var_store, direction);
130     default:return 0;
131     }
132   }
133 
sanitizeOT::BaseCoord134   bool sanitize (hb_sanitize_context_t *c) const
135   {
136     TRACE_SANITIZE (this);
137     if (unlikely (!u.format.sanitize (c))) return_trace (false);
138     switch (u.format) {
139     case 1: return_trace (u.format1.sanitize (c));
140     case 2: return_trace (u.format2.sanitize (c));
141     case 3: return_trace (u.format3.sanitize (c));
142     default:return_trace (false);
143     }
144   }
145 
146   protected:
147   union {
148   HBUINT16		format;
149   BaseCoordFormat1	format1;
150   BaseCoordFormat2	format2;
151   BaseCoordFormat3	format3;
152   } u;
153   public:
154   DEFINE_SIZE_UNION (2, format);
155 };
156 
157 struct FeatMinMaxRecord
158 {
cmpOT::FeatMinMaxRecord159   int cmp (hb_tag_t key) const { return tag.cmp (key); }
160 
has_dataOT::FeatMinMaxRecord161   bool has_data () const { return tag; }
162 
get_min_maxOT::FeatMinMaxRecord163   void get_min_max (const BaseCoord **min, const BaseCoord **max) const
164   {
165     if (likely (min)) *min = &(this+minCoord);
166     if (likely (max)) *max = &(this+maxCoord);
167   }
168 
sanitizeOT::FeatMinMaxRecord169   bool sanitize (hb_sanitize_context_t *c, const void *base) const
170   {
171     TRACE_SANITIZE (this);
172     return_trace (likely (c->check_struct (this) &&
173 			  minCoord.sanitize (c, this) &&
174 			  maxCoord.sanitize (c, this)));
175   }
176 
177   protected:
178   Tag		tag;		/* 4-byte feature identification tag--must
179 				 * match feature tag in FeatureList */
180   Offset16To<BaseCoord>
181 		minCoord;	/* Offset to BaseCoord table that defines
182 				 * the minimum extent value, from beginning
183 				 * of MinMax table (may be NULL) */
184   Offset16To<BaseCoord>
185 		maxCoord;	/* Offset to BaseCoord table that defines
186 				 * the maximum extent value, from beginning
187 				 * of MinMax table (may be NULL) */
188   public:
189   DEFINE_SIZE_STATIC (8);
190 
191 };
192 
193 struct MinMax
194 {
get_min_maxOT::MinMax195   void get_min_max (hb_tag_t          feature_tag,
196 		    const BaseCoord **min,
197 		    const BaseCoord **max) const
198   {
199     const FeatMinMaxRecord &minMaxCoord = featMinMaxRecords.bsearch (feature_tag);
200     if (minMaxCoord.has_data ())
201       minMaxCoord.get_min_max (min, max);
202     else
203     {
204       if (likely (min)) *min = &(this+minCoord);
205       if (likely (max)) *max = &(this+maxCoord);
206     }
207   }
208 
sanitizeOT::MinMax209   bool sanitize (hb_sanitize_context_t *c) const
210   {
211     TRACE_SANITIZE (this);
212     return_trace (likely (c->check_struct (this) &&
213 			  minCoord.sanitize (c, this) &&
214 			  maxCoord.sanitize (c, this) &&
215 			  featMinMaxRecords.sanitize (c, this)));
216   }
217 
218   protected:
219   Offset16To<BaseCoord>
220 		minCoord;	/* Offset to BaseCoord table that defines
221 				 * minimum extent value, from the beginning
222 				 * of MinMax table (may be NULL) */
223   Offset16To<BaseCoord>
224 		maxCoord;	/* Offset to BaseCoord table that defines
225 				 * maximum extent value, from the beginning
226 				 * of MinMax table (may be NULL) */
227   SortedArray16Of<FeatMinMaxRecord>
228 		featMinMaxRecords;
229 				/* Array of FeatMinMaxRecords, in alphabetical
230 				 * order by featureTableTag */
231   public:
232   DEFINE_SIZE_ARRAY (6, featMinMaxRecords);
233 };
234 
235 struct BaseValues
236 {
get_base_coordOT::BaseValues237   const BaseCoord &get_base_coord (int baseline_tag_index) const
238   {
239     if (baseline_tag_index == -1) baseline_tag_index = defaultIndex;
240     return this+baseCoords[baseline_tag_index];
241   }
242 
sanitizeOT::BaseValues243   bool sanitize (hb_sanitize_context_t *c) const
244   {
245     TRACE_SANITIZE (this);
246     return_trace (likely (c->check_struct (this) &&
247 			  baseCoords.sanitize (c, this)));
248   }
249 
250   protected:
251   Index		defaultIndex;	/* Index number of default baseline for this
252 				 * script — equals index position of baseline tag
253 				 * in baselineTags array of the BaseTagList */
254   Array16OfOffset16To<BaseCoord>
255 		baseCoords;	/* Number of BaseCoord tables defined — should equal
256 				 * baseTagCount in the BaseTagList
257 				 *
258 				 * Array of offsets to BaseCoord tables, from beginning of
259 				 * BaseValues table — order matches baselineTags array in
260 				 * the BaseTagList */
261   public:
262   DEFINE_SIZE_ARRAY (4, baseCoords);
263 };
264 
265 struct BaseLangSysRecord
266 {
cmpOT::BaseLangSysRecord267   int cmp (hb_tag_t key) const { return baseLangSysTag.cmp (key); }
268 
has_dataOT::BaseLangSysRecord269   bool has_data () const { return baseLangSysTag; }
270 
get_min_maxOT::BaseLangSysRecord271   const MinMax &get_min_max () const { return this+minMax; }
272 
sanitizeOT::BaseLangSysRecord273   bool sanitize (hb_sanitize_context_t *c, const void *base) const
274   {
275     TRACE_SANITIZE (this);
276     return_trace (likely (c->check_struct (this) &&
277 			  minMax.sanitize (c, this)));
278   }
279 
280   protected:
281   Tag		baseLangSysTag;	/* 4-byte language system identification tag */
282   Offset16To<MinMax>
283 		minMax;		/* Offset to MinMax table, from beginning
284 				 * of BaseScript table */
285   public:
286   DEFINE_SIZE_STATIC (6);
287 };
288 
289 struct BaseScript
290 {
get_min_maxOT::BaseScript291   const MinMax &get_min_max (hb_tag_t language_tag) const
292   {
293     const BaseLangSysRecord& record = baseLangSysRecords.bsearch (language_tag);
294     return record.has_data () ? record.get_min_max () : this+defaultMinMax;
295   }
296 
get_base_coordOT::BaseScript297   const BaseCoord &get_base_coord (int baseline_tag_index) const
298   { return (this+baseValues).get_base_coord (baseline_tag_index); }
299 
has_dataOT::BaseScript300   bool has_data () const { return baseValues; }
301 
sanitizeOT::BaseScript302   bool sanitize (hb_sanitize_context_t *c) const
303   {
304     TRACE_SANITIZE (this);
305     return_trace (likely (c->check_struct (this) &&
306 			  baseValues.sanitize (c, this) &&
307 			  defaultMinMax.sanitize (c, this) &&
308 			  baseLangSysRecords.sanitize (c, this)));
309   }
310 
311   protected:
312   Offset16To<BaseValues>
313 		baseValues;	/* Offset to BaseValues table, from beginning
314 				 * of BaseScript table (may be NULL) */
315   Offset16To<MinMax>
316 		defaultMinMax;	/* Offset to MinMax table, from beginning of
317 				 * BaseScript table (may be NULL) */
318   SortedArray16Of<BaseLangSysRecord>
319 		baseLangSysRecords;
320 				/* Number of BaseLangSysRecords
321 				 * defined — may be zero (0) */
322 
323   public:
324   DEFINE_SIZE_ARRAY (6, baseLangSysRecords);
325 };
326 
327 struct BaseScriptList;
328 struct BaseScriptRecord
329 {
cmpOT::BaseScriptRecord330   int cmp (hb_tag_t key) const { return baseScriptTag.cmp (key); }
331 
has_dataOT::BaseScriptRecord332   bool has_data () const { return baseScriptTag; }
333 
get_base_scriptOT::BaseScriptRecord334   const BaseScript &get_base_script (const BaseScriptList *list) const
335   { return list+baseScript; }
336 
sanitizeOT::BaseScriptRecord337   bool sanitize (hb_sanitize_context_t *c, const void *base) const
338   {
339     TRACE_SANITIZE (this);
340     return_trace (likely (c->check_struct (this) &&
341 			  baseScript.sanitize (c, base)));
342   }
343 
344   protected:
345   Tag		baseScriptTag;	/* 4-byte script identification tag */
346   Offset16To<BaseScript>
347 		baseScript;	/* Offset to BaseScript table, from beginning
348 				 * of BaseScriptList */
349 
350   public:
351   DEFINE_SIZE_STATIC (6);
352 };
353 
354 struct BaseScriptList
355 {
get_base_scriptOT::BaseScriptList356   const BaseScript &get_base_script (hb_tag_t script) const
357   {
358     const BaseScriptRecord *record = &baseScriptRecords.bsearch (script);
359     if (!record->has_data ()) record = &baseScriptRecords.bsearch (HB_TAG ('D','F','L','T'));
360     return record->has_data () ? record->get_base_script (this) : Null (BaseScript);
361   }
362 
sanitizeOT::BaseScriptList363   bool sanitize (hb_sanitize_context_t *c) const
364   {
365     TRACE_SANITIZE (this);
366     return_trace (c->check_struct (this) &&
367 		  baseScriptRecords.sanitize (c, this));
368   }
369 
370   protected:
371   SortedArray16Of<BaseScriptRecord>
372 			baseScriptRecords;
373 
374   public:
375   DEFINE_SIZE_ARRAY (2, baseScriptRecords);
376 };
377 
378 struct Axis
379 {
get_baselineOT::Axis380   bool get_baseline (hb_tag_t          baseline_tag,
381 		     hb_tag_t          script_tag,
382 		     hb_tag_t          language_tag,
383 		     const BaseCoord **coord) const
384   {
385     const BaseScript &base_script = (this+baseScriptList).get_base_script (script_tag);
386     if (!base_script.has_data ())
387     {
388       *coord = nullptr;
389       return false;
390     }
391 
392     if (likely (coord))
393     {
394       unsigned int tag_index = 0;
395       if (!(this+baseTagList).bfind (baseline_tag, &tag_index))
396       {
397         *coord = nullptr;
398         return false;
399       }
400       *coord = &base_script.get_base_coord (tag_index);
401     }
402 
403     return true;
404   }
405 
get_min_maxOT::Axis406   bool get_min_max (hb_tag_t          script_tag,
407 		    hb_tag_t          language_tag,
408 		    hb_tag_t          feature_tag,
409 		    const BaseCoord **min_coord,
410 		    const BaseCoord **max_coord) const
411   {
412     const BaseScript &base_script = (this+baseScriptList).get_base_script (script_tag);
413     if (!base_script.has_data ())
414     {
415       *min_coord = *max_coord = nullptr;
416       return false;
417     }
418 
419     base_script.get_min_max (language_tag).get_min_max (feature_tag, min_coord, max_coord);
420 
421     return true;
422   }
423 
sanitizeOT::Axis424   bool sanitize (hb_sanitize_context_t *c) const
425   {
426     TRACE_SANITIZE (this);
427     return_trace (likely (c->check_struct (this) &&
428 			  (this+baseTagList).sanitize (c) &&
429 			  (this+baseScriptList).sanitize (c)));
430   }
431 
432   protected:
433   Offset16To<SortedArray16Of<Tag>>
434 		baseTagList;	/* Offset to BaseTagList table, from beginning
435 				 * of Axis table (may be NULL)
436 				 * Array of 4-byte baseline identification tags — must
437 				 * be in alphabetical order */
438   Offset16To<BaseScriptList>
439 		baseScriptList;	/* Offset to BaseScriptList table, from beginning
440 				 * of Axis table
441 				 * Array of BaseScriptRecords, in alphabetical order
442 				 * by baseScriptTag */
443 
444   public:
445   DEFINE_SIZE_STATIC (4);
446 };
447 
448 struct BASE
449 {
450   static constexpr hb_tag_t tableTag = HB_OT_TAG_BASE;
451 
get_axisOT::BASE452   const Axis &get_axis (hb_direction_t direction) const
453   { return HB_DIRECTION_IS_VERTICAL (direction) ? this+vAxis : this+hAxis; }
454 
get_var_storeOT::BASE455   const VariationStore &get_var_store () const
456   { return version.to_int () < 0x00010001u ? Null (VariationStore) : this+varStore; }
457 
get_baselineOT::BASE458   bool get_baseline (hb_font_t      *font,
459 		     hb_tag_t        baseline_tag,
460 		     hb_direction_t  direction,
461 		     hb_tag_t        script_tag,
462 		     hb_tag_t        language_tag,
463 		     hb_position_t  *base) const
464   {
465     const BaseCoord *base_coord = nullptr;
466     if (unlikely (!get_axis (direction).get_baseline (baseline_tag, script_tag, language_tag, &base_coord) ||
467 		  !base_coord || !base_coord->has_data ()))
468       return false;
469 
470     if (likely (base))
471       *base = base_coord->get_coord (font, get_var_store (), direction);
472 
473     return true;
474   }
475 
476   /* TODO: Expose this separately sometime? */
get_min_maxOT::BASE477   bool get_min_max (hb_font_t      *font,
478 		    hb_direction_t  direction,
479 		    hb_tag_t        script_tag,
480 		    hb_tag_t        language_tag,
481 		    hb_tag_t        feature_tag,
482 		    hb_position_t  *min,
483 		    hb_position_t  *max)
484   {
485     const BaseCoord *min_coord, *max_coord;
486     if (!get_axis (direction).get_min_max (script_tag, language_tag, feature_tag,
487 					   &min_coord, &max_coord))
488       return false;
489 
490     const VariationStore &var_store = get_var_store ();
491     if (likely (min && min_coord)) *min = min_coord->get_coord (font, var_store, direction);
492     if (likely (max && max_coord)) *max = max_coord->get_coord (font, var_store, direction);
493     return true;
494   }
495 
sanitizeOT::BASE496   bool sanitize (hb_sanitize_context_t *c) const
497   {
498     TRACE_SANITIZE (this);
499     return_trace (likely (c->check_struct (this) &&
500 			  likely (version.major == 1) &&
501 			  hAxis.sanitize (c, this) &&
502 			  vAxis.sanitize (c, this) &&
503 			  (version.to_int () < 0x00010001u || varStore.sanitize (c, this))));
504   }
505 
506   protected:
507   FixedVersion<>version;	/* Version of the BASE table */
508   Offset16To<Axis>hAxis;		/* Offset to horizontal Axis table, from beginning
509 				 * of BASE table (may be NULL) */
510   Offset16To<Axis>vAxis;		/* Offset to vertical Axis table, from beginning
511 				 * of BASE table (may be NULL) */
512   Offset32To<VariationStore>
513 		varStore;	/* Offset to the table of Item Variation
514 				 * Store--from beginning of BASE
515 				 * header (may be NULL).  Introduced
516 				 * in version 0x00010001. */
517   public:
518   DEFINE_SIZE_MIN (8);
519 };
520 
521 
522 } /* namespace OT */
523 
524 
525 #endif /* HB_OT_LAYOUT_BASE_TABLE_HH */
526