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
35namespace OT {
36
37/*
38 * BASE -- Baseline
39 * https://docs.microsoft.com/en-us/typography/opentype/spec/base
40 */
41
42struct BaseCoordFormat1
43{
44 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
49 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
62struct BaseCoordFormat2
63{
64 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
70 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
86struct BaseCoordFormat3
87{
88 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
100 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
118struct BaseCoord
119{
120 bool has_data () const { return u.format; }
121
122 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
134 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
157struct FeatMinMaxRecord
158{
159 int cmp (hb_tag_t key) const { return tag.cmp (key); }
160
161 bool has_data () const { return tag; }
162
163 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
169 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, base) &&
174 maxCoord.sanitize (c, base)));
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
192struct MinMax
193{
194 void get_min_max (hb_tag_t feature_tag,
195 const BaseCoord **min,
196 const BaseCoord **max) const
197 {
198 const FeatMinMaxRecord &minMaxCoord = featMinMaxRecords.bsearch (feature_tag);
199 if (minMaxCoord.has_data ())
200 minMaxCoord.get_min_max (min, max);
201 else
202 {
203 if (likely (min)) *min = &(this+minCoord);
204 if (likely (max)) *max = &(this+maxCoord);
205 }
206 }
207
208 bool sanitize (hb_sanitize_context_t *c) const
209 {
210 TRACE_SANITIZE (this);
211 return_trace (likely (c->check_struct (this) &&
212 minCoord.sanitize (c, this) &&
213 maxCoord.sanitize (c, this) &&
214 featMinMaxRecords.sanitize (c, this)));
215 }
216
217 protected:
218 Offset16To<BaseCoord>
219 minCoord; /* Offset to BaseCoord table that defines
220 * minimum extent value, from the beginning
221 * of MinMax table (may be NULL) */
222 Offset16To<BaseCoord>
223 maxCoord; /* Offset to BaseCoord table that defines
224 * maximum extent value, from the beginning
225 * of MinMax table (may be NULL) */
226 SortedArray16Of<FeatMinMaxRecord>
227 featMinMaxRecords;
228 /* Array of FeatMinMaxRecords, in alphabetical
229 * order by featureTableTag */
230 public:
231 DEFINE_SIZE_ARRAY (6, featMinMaxRecords);
232};
233
234struct BaseValues
235{
236 const BaseCoord &get_base_coord (int baseline_tag_index) const
237 {
238 if (baseline_tag_index == -1) baseline_tag_index = defaultIndex;
239 return this+baseCoords[baseline_tag_index];
240 }
241
242 bool sanitize (hb_sanitize_context_t *c) const
243 {
244 TRACE_SANITIZE (this);
245 return_trace (likely (c->check_struct (this) &&
246 baseCoords.sanitize (c, this)));
247 }
248
249 protected:
250 Index defaultIndex; /* Index number of default baseline for this
251 * script — equals index position of baseline tag
252 * in baselineTags array of the BaseTagList */
253 Array16OfOffset16To<BaseCoord>
254 baseCoords; /* Number of BaseCoord tables defined — should equal
255 * baseTagCount in the BaseTagList
256 *
257 * Array of offsets to BaseCoord tables, from beginning of
258 * BaseValues table — order matches baselineTags array in
259 * the BaseTagList */
260 public:
261 DEFINE_SIZE_ARRAY (4, baseCoords);
262};
263
264struct BaseLangSysRecord
265{
266 int cmp (hb_tag_t key) const { return baseLangSysTag.cmp (key); }
267
268 bool has_data () const { return baseLangSysTag; }
269
270 const MinMax &get_min_max () const { return this+minMax; }
271
272 bool sanitize (hb_sanitize_context_t *c, const void *base) const
273 {
274 TRACE_SANITIZE (this);
275 return_trace (likely (c->check_struct (this) &&
276 minMax.sanitize (c, base)));
277 }
278
279 protected:
280 Tag baseLangSysTag; /* 4-byte language system identification tag */
281 Offset16To<MinMax>
282 minMax; /* Offset to MinMax table, from beginning
283 * of BaseScript table */
284 public:
285 DEFINE_SIZE_STATIC (6);
286};
287
288struct BaseScript
289{
290 const MinMax &get_min_max (hb_tag_t language_tag) const
291 {
292 const BaseLangSysRecord& record = baseLangSysRecords.bsearch (language_tag);
293 return record.has_data () ? record.get_min_max () : this+defaultMinMax;
294 }
295
296 const BaseCoord &get_base_coord (int baseline_tag_index) const
297 { return (this+baseValues).get_base_coord (baseline_tag_index); }
298
299 bool has_values () const { return baseValues; }
300 bool has_min_max () const { return defaultMinMax; /* TODO What if only per-language is present? */ }
301
302 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
327struct BaseScriptList;
328struct BaseScriptRecord
329{
330 int cmp (hb_tag_t key) const { return baseScriptTag.cmp (key); }
331
332 bool has_data () const { return baseScriptTag; }
333
334 const BaseScript &get_base_script (const BaseScriptList *list) const
335 { return list+baseScript; }
336
337 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
354struct BaseScriptList
355{
356 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
363 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
378struct Axis
379{
380 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_values ())
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
406 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_min_max ())
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
424 bool sanitize (hb_sanitize_context_t *c) const
425 {
426 TRACE_SANITIZE (this);
427 return_trace (likely (c->check_struct (this) &&
428 baseTagList.sanitize (c, this) &&
429 baseScriptList.sanitize (c, this)));
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
448struct BASE
449{
450 static constexpr hb_tag_t tableTag = HB_OT_TAG_BASE;
451
452 const Axis &get_axis (hb_direction_t direction) const
453 { return HB_DIRECTION_IS_VERTICAL (direction) ? this+vAxis : this+hAxis; }
454
455 const VariationStore &get_var_store () const
456 { return version.to_int () < 0x00010001u ? Null (VariationStore) : this+varStore; }
457
458 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 bool get_min_max (hb_font_t *font,
477 hb_direction_t direction,
478 hb_tag_t script_tag,
479 hb_tag_t language_tag,
480 hb_tag_t feature_tag,
481 hb_position_t *min,
482 hb_position_t *max) const
483 {
484 const BaseCoord *min_coord, *max_coord;
485 if (!get_axis (direction).get_min_max (script_tag, language_tag, feature_tag,
486 &min_coord, &max_coord))
487 return false;
488
489 const VariationStore &var_store = get_var_store ();
490 if (likely (min && min_coord)) *min = min_coord->get_coord (font, var_store, direction);
491 if (likely (max && max_coord)) *max = max_coord->get_coord (font, var_store, direction);
492 return true;
493 }
494
495 bool sanitize (hb_sanitize_context_t *c) const
496 {
497 TRACE_SANITIZE (this);
498 return_trace (likely (c->check_struct (this) &&
499 likely (version.major == 1) &&
500 hAxis.sanitize (c, this) &&
501 vAxis.sanitize (c, this) &&
502 (version.to_int () < 0x00010001u || varStore.sanitize (c, this))));
503 }
504
505 protected:
506 FixedVersion<>version; /* Version of the BASE table */
507 Offset16To<Axis>hAxis; /* Offset to horizontal Axis table, from beginning
508 * of BASE table (may be NULL) */
509 Offset16To<Axis>vAxis; /* Offset to vertical Axis table, from beginning
510 * of BASE table (may be NULL) */
511 Offset32To<VariationStore>
512 varStore; /* Offset to the table of Item Variation
513 * Store--from beginning of BASE
514 * header (may be NULL). Introduced
515 * in version 0x00010001. */
516 public:
517 DEFINE_SIZE_MIN (8);
518};
519
520
521} /* namespace OT */
522
523
524#endif /* HB_OT_LAYOUT_BASE_TABLE_HH */
525