1 /*
   2  * Copyright © 2016 Elie Roux <elie.roux@telecom-bretagne.eu>
   3  * Copyright © 2018  Google, Inc.
   4  * Copyright © 2018  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 /* To be removed */
  36 typedef hb_tag_t hb_ot_layout_baseline_t;
  37 
  38 namespace OT {
  39 
  40 /*
  41  * BASE -- Baseline
  42  * https://docs.microsoft.com/en-us/typography/opentype/spec/base
  43  */
  44 
  45 struct BaseCoordFormat1
  46 {
  47   hb_position_t get_coord () const { return coordinate; }
  48 
  49   bool sanitize (hb_sanitize_context_t *c) const
  50   {
  51     TRACE_SANITIZE (this);
  52     return_trace (likely (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 {
  64   hb_position_t get_coord () const
  65   {
  66     /* TODO */
  67     return 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   GlyphID       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 {
  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     return coordinate + (HB_DIRECTION_IS_VERTICAL (direction) ?
  94                          device.get_y_delta (font, var_store) :
  95                          device.get_x_delta (font, var_store));
  96   }
  97 
  98 
  99   bool sanitize (hb_sanitize_context_t *c) const
 100   {
 101     TRACE_SANITIZE (this);
 102     return_trace (likely (c->check_struct (this) &&
 103                           deviceTable.sanitize (c, this)));
 104   }
 105 
 106   protected:
 107   HBUINT16      format;         /* Format identifier--format = 3 */
 108   FWORD         coordinate;     /* X or Y value, in design units */
 109   OffsetTo<Device>
 110                 deviceTable;    /* Offset to Device table for X or
 111                                  * Y value, from beginning of
 112                                  * BaseCoord table (may be NULL). */
 113   public:
 114   DEFINE_SIZE_STATIC (6);
 115 };
 116 
 117 struct BaseCoord
 118 {
 119   hb_position_t get_coord (hb_font_t *font,
 120                            const VariationStore &var_store,
 121                            hb_direction_t direction) const
 122   {
 123     switch (u.format) {
 124     case 1: return u.format1.get_coord ();
 125     case 2: return u.format2.get_coord ();
 126     case 3: return u.format3.get_coord (font, var_store, direction);
 127     default:return 0;
 128     }
 129   }
 130 
 131   bool sanitize (hb_sanitize_context_t *c) const
 132   {
 133     TRACE_SANITIZE (this);
 134     if (unlikely (!u.format.sanitize (c))) return_trace (false);
 135     switch (u.format) {
 136     case 1: return_trace (u.format1.sanitize (c));
 137     case 2: return_trace (u.format2.sanitize (c));
 138     case 3: return_trace (u.format3.sanitize (c));
 139     default:return_trace (false);
 140     }
 141   }
 142 
 143   protected:
 144   union {
 145     HBUINT16            format;
 146     BaseCoordFormat1    format1;
 147     BaseCoordFormat2    format2;
 148     BaseCoordFormat3    format3;
 149   } u;
 150   public:
 151   DEFINE_SIZE_UNION (2, format);
 152 };
 153 
 154 struct FeatMinMaxRecord
 155 {
 156   static int cmp (const void *key_, const void *entry_)
 157   {
 158     hb_tag_t key = * (hb_tag_t *) key_;
 159     const FeatMinMaxRecord &entry = * (const FeatMinMaxRecord *) entry_;
 160     return key < (unsigned int) entry.tag ? -1 :
 161            key > (unsigned int) entry.tag ? 1 :
 162            0;
 163   }
 164 
 165   void get_min_max (const BaseCoord **min, const BaseCoord **max) const
 166   {
 167     if (likely (min)) *min = &(this+minCoord);
 168     if (likely (max)) *max = &(this+maxCoord);
 169   }
 170 
 171   bool sanitize (hb_sanitize_context_t *c, const void *base) const
 172   {
 173     TRACE_SANITIZE (this);
 174     return_trace (likely (c->check_struct (this) &&
 175                           minCoord.sanitize (c, this) &&
 176                           maxCoord.sanitize (c, this)));
 177   }
 178 
 179   protected:
 180   Tag           tag;            /* 4-byte feature identification tag--must
 181                                  * match feature tag in FeatureList */
 182   OffsetTo<BaseCoord>
 183                 minCoord;       /* Offset to BaseCoord table that defines
 184                                  * the minimum extent value, from beginning
 185                                  * of MinMax table (may be NULL) */
 186   OffsetTo<BaseCoord>
 187                 maxCoord;       /* Offset to BaseCoord table that defines
 188                                  * the maximum extent value, from beginning
 189                                  * of MinMax table (may be NULL) */
 190   public:
 191   DEFINE_SIZE_STATIC (8);
 192 
 193 };
 194 
 195 struct MinMax
 196 {
 197   void get_min_max (hb_tag_t          feature_tag,
 198                            const BaseCoord **min,
 199                            const BaseCoord **max) const
 200   {
 201     /* TODO Replace hb_bsearch() with .bsearch(). */
 202     const FeatMinMaxRecord *minMaxCoord = (const FeatMinMaxRecord *)
 203                                           hb_bsearch (&feature_tag, featMinMaxRecords.arrayZ,
 204                                                       featMinMaxRecords.len,
 205                                                       FeatMinMaxRecord::static_size,
 206                                                       FeatMinMaxRecord::cmp);
 207     if (minMaxCoord)
 208       minMaxCoord->get_min_max (min, max);
 209     else
 210     {
 211       if (likely (min)) *min = &(this+minCoord);
 212       if (likely (max)) *max = &(this+maxCoord);
 213     }
 214   }
 215 
 216   bool sanitize (hb_sanitize_context_t *c) const
 217   {
 218     TRACE_SANITIZE (this);
 219     return_trace (likely (c->check_struct (this) &&
 220                           minCoord.sanitize (c, this) &&
 221                           maxCoord.sanitize (c, this) &&
 222                           featMinMaxRecords.sanitize (c, this)));
 223   }
 224 
 225   protected:
 226   OffsetTo<BaseCoord>
 227                 minCoord;       /* Offset to BaseCoord table that defines
 228                                  * minimum extent value, from the beginning
 229                                  * of MinMax table (may be NULL) */
 230   OffsetTo<BaseCoord>
 231                 maxCoord;       /* Offset to BaseCoord table that defines
 232                                  * maximum extent value, from the beginning
 233                                  * of MinMax table (may be NULL) */
 234   SortedArrayOf<FeatMinMaxRecord>
 235                 featMinMaxRecords;
 236                                 /* Array of FeatMinMaxRecords, in alphabetical
 237                                  * order by featureTableTag */
 238   public:
 239   DEFINE_SIZE_ARRAY (6, featMinMaxRecords);
 240 };
 241 
 242 struct BaseValues
 243 {
 244   const BaseCoord &get_base_coord (int baseline_tag_index) const
 245   {
 246     if (baseline_tag_index == -1) baseline_tag_index = defaultIndex;
 247     return this+baseCoords[baseline_tag_index];
 248   }
 249 
 250   bool sanitize (hb_sanitize_context_t *c) const
 251   {
 252     TRACE_SANITIZE (this);
 253     return_trace (likely (c->check_struct (this) &&
 254                           baseCoords.sanitize (c, this)));
 255   }
 256 
 257   protected:
 258   Index         defaultIndex;   /* Index number of default baseline for this
 259                                  * script — equals index position of baseline tag
 260                                  * in baselineTags array of the BaseTagList */
 261   OffsetArrayOf<BaseCoord>
 262                 baseCoords;     /* Number of BaseCoord tables defined — should equal
 263                                  * baseTagCount in the BaseTagList
 264                                  *
 265                                  * Array of offsets to BaseCoord tables, from beginning of
 266                                  * BaseValues table — order matches baselineTags array in
 267                                  * the BaseTagList */
 268   public:
 269   DEFINE_SIZE_ARRAY (4, baseCoords);
 270 };
 271 
 272 struct BaseLangSysRecord
 273 {
 274   static int cmp (const void *key_, const void *entry_)
 275   {
 276     hb_tag_t key = * (hb_tag_t *) key_;
 277     const BaseLangSysRecord &entry = * (const BaseLangSysRecord *) entry_;
 278     return key < (unsigned int) entry.baseLangSysTag ? -1 :
 279            key > (unsigned int) entry.baseLangSysTag ? 1 :
 280            0;
 281   }
 282 
 283   const MinMax &get_min_max () const
 284   { return this+minMax; }
 285 
 286   bool sanitize (hb_sanitize_context_t *c, const void *base) const
 287   {
 288     TRACE_SANITIZE (this);
 289     return_trace (likely (c->check_struct (this) &&
 290                           minMax.sanitize (c, this)));
 291   }
 292 
 293   protected:
 294   Tag           baseLangSysTag; /* 4-byte language system identification tag */
 295   OffsetTo<MinMax>
 296                 minMax;         /* Offset to MinMax table, from beginning
 297                                  * of BaseScript table */
 298   public:
 299   DEFINE_SIZE_STATIC (6);
 300 };
 301 
 302 struct BaseScript
 303 {
 304   const MinMax &get_min_max (hb_tag_t language_tag) const
 305   {
 306     /* TODO Replace hb_bsearch() with .bsearch(). */
 307     const BaseLangSysRecord* record = (const BaseLangSysRecord *)
 308                                       hb_bsearch (&language_tag, baseLangSysRecords.arrayZ,
 309                                                   baseLangSysRecords.len,
 310                                                   BaseLangSysRecord::static_size,
 311                                                   BaseLangSysRecord::cmp);
 312     return record ? record->get_min_max () : this+defaultMinMax;
 313   }
 314 
 315   const BaseCoord &get_base_coord (int baseline_tag_index) const
 316   { return (this+baseValues).get_base_coord (baseline_tag_index); }
 317 
 318   bool is_empty () const { return !baseValues; }
 319 
 320   bool sanitize (hb_sanitize_context_t *c) const
 321   {
 322     TRACE_SANITIZE (this);
 323     return_trace (likely (c->check_struct (this) &&
 324                           baseValues.sanitize (c, this) &&
 325                           defaultMinMax.sanitize (c, this) &&
 326                           baseLangSysRecords.sanitize (c, this)));
 327   }
 328 
 329   protected:
 330   OffsetTo<BaseValues>
 331                 baseValues;     /* Offset to BaseValues table, from beginning
 332                                  * of BaseScript table (may be NULL) */
 333   OffsetTo<MinMax>
 334                 defaultMinMax;  /* Offset to MinMax table, from beginning of
 335                                  * BaseScript table (may be NULL) */
 336   SortedArrayOf<BaseLangSysRecord>
 337                 baseLangSysRecords;
 338                                 /* Number of BaseLangSysRecords
 339                                  * defined — may be zero (0) */
 340 
 341   public:
 342   DEFINE_SIZE_ARRAY (6, baseLangSysRecords);
 343 };
 344 
 345 struct BaseScriptList;
 346 struct BaseScriptRecord
 347 {
 348   static int cmp (const void *key_, const void *entry_)
 349   {
 350     hb_tag_t key = * (hb_tag_t *) key_;
 351     const BaseScriptRecord &entry = * (const BaseScriptRecord *) entry_;
 352     return key < (unsigned int) entry.baseScriptTag ? -1 :
 353            key > (unsigned int) entry.baseScriptTag ? 1 :
 354            0;
 355   }
 356 
 357   const BaseScript &get_base_script (const BaseScriptList *list) const
 358   { return list+baseScript; }
 359 
 360   bool sanitize (hb_sanitize_context_t *c, const void *base) const
 361   {
 362     TRACE_SANITIZE (this);
 363     return_trace (likely (c->check_struct (this) &&
 364                           baseScript.sanitize (c, base)));
 365   }
 366 
 367   protected:
 368   Tag           baseScriptTag;  /* 4-byte script identification tag */
 369   OffsetTo<BaseScript>
 370                 baseScript;     /* Offset to BaseScript table, from beginning
 371                                  * of BaseScriptList */
 372 
 373   public:
 374   DEFINE_SIZE_STATIC (6);
 375 };
 376 
 377 struct BaseScriptList
 378 {
 379   const BaseScriptRecord *find_record (hb_tag_t script) const
 380   {
 381     /* TODO Replace hb_bsearch() with .bsearch(). */
 382     return (const BaseScriptRecord *) hb_bsearch (&script, baseScriptRecords.arrayZ,
 383                                                   baseScriptRecords.len,
 384                                                   BaseScriptRecord::static_size,
 385                                                   BaseScriptRecord::cmp);
 386   }
 387 
 388   /* TODO: Or client should handle fallback? */
 389   const BaseScript &get_base_script (hb_tag_t script) const
 390   {
 391     const BaseScriptRecord *record = find_record (script);
 392     if (!record) record = find_record ((hb_script_t) HB_TAG ('D','F','L','T'));
 393 
 394     return record ? record->get_base_script (this) : Null (BaseScript);
 395   }
 396 
 397   bool sanitize (hb_sanitize_context_t *c) const
 398   {
 399     TRACE_SANITIZE (this);
 400     return_trace (c->check_struct (this) &&
 401                   baseScriptRecords.sanitize (c, this));
 402   }
 403 
 404   protected:
 405   SortedArrayOf<BaseScriptRecord>
 406                         baseScriptRecords;
 407 
 408   public:
 409   DEFINE_SIZE_ARRAY (2, baseScriptRecords);
 410 };
 411 
 412 struct Axis
 413 {
 414   bool get_baseline (hb_ot_layout_baseline_t   baseline,
 415                             hb_tag_t                  script_tag,
 416                             hb_tag_t                  language_tag,
 417                             const BaseCoord         **coord) const
 418   {
 419     const BaseScript &base_script = (this+baseScriptList).get_base_script (script_tag);
 420     if (base_script.is_empty ()) return false;
 421 
 422     if (likely (coord)) *coord = &base_script.get_base_coord ((this+baseTagList).bsearch (baseline));
 423 
 424     return true;
 425   }
 426 
 427   bool get_min_max (hb_tag_t          script_tag,
 428                     hb_tag_t          language_tag,
 429                     hb_tag_t          feature_tag,
 430                     const BaseCoord **min_coord,
 431                     const BaseCoord **max_coord) const
 432   {
 433     const BaseScript &base_script = (this+baseScriptList).get_base_script (script_tag);
 434     if (base_script.is_empty ()) return false;
 435 
 436     base_script.get_min_max (language_tag).get_min_max (feature_tag, min_coord, max_coord);
 437 
 438     return true;
 439   }
 440 
 441   bool sanitize (hb_sanitize_context_t *c) const
 442   {
 443     TRACE_SANITIZE (this);
 444     return_trace (likely (c->check_struct (this) &&
 445                           (this+baseTagList).sanitize (c) &&
 446                           (this+baseScriptList).sanitize (c)));
 447   }
 448 
 449   protected:
 450   OffsetTo<SortedArrayOf<Tag> >
 451                 baseTagList;    /* Offset to BaseTagList table, from beginning
 452                                  * of Axis table (may be NULL)
 453                                  * Array of 4-byte baseline identification tags — must
 454                                  * be in alphabetical order */
 455   OffsetTo<BaseScriptList>
 456                 baseScriptList; /* Offset to BaseScriptList table, from beginning
 457                                  * of Axis table
 458                                  * Array of BaseScriptRecords, in alphabetical order
 459                                  * by baseScriptTag */
 460 
 461   public:
 462   DEFINE_SIZE_STATIC (4);
 463 };
 464 
 465 struct BASE
 466 {
 467   static constexpr hb_tag_t tableTag = HB_OT_TAG_BASE;
 468 
 469   const Axis &get_axis (hb_direction_t direction) const
 470   { return HB_DIRECTION_IS_VERTICAL (direction) ? this+vAxis : this+hAxis; }
 471 
 472   const VariationStore &get_var_store () const
 473   { return version.to_int () < 0x00010001u ? Null (VariationStore) : this+varStore; }
 474 
 475   bool get_baseline (hb_font_t               *font,
 476                      hb_ot_layout_baseline_t  baseline,
 477                      hb_direction_t           direction,
 478                      hb_tag_t                 script_tag,
 479                      hb_tag_t                 language_tag,
 480                      hb_position_t           *base) const
 481   {
 482     const BaseCoord *base_coord;
 483     if (!get_axis (direction).get_baseline (baseline, script_tag, language_tag, &base_coord))
 484       return false;
 485 
 486     if (likely (base && base_coord)) *base = base_coord->get_coord (font,
 487                                                                     get_var_store (),
 488                                                                     direction);
 489     return true;
 490   }
 491 
 492   /* TODO: Expose this separately sometime? */
 493   bool get_min_max (hb_font_t      *font,
 494                     hb_direction_t  direction,
 495                     hb_tag_t        script_tag,
 496                     hb_tag_t        language_tag,
 497                     hb_tag_t        feature_tag,
 498                     hb_position_t  *min,
 499                     hb_position_t  *max)
 500   {
 501     const BaseCoord *min_coord, *max_coord;
 502     if (!get_axis (direction).get_min_max (script_tag, language_tag, feature_tag,
 503                                            &min_coord, &max_coord))
 504       return false;
 505 
 506     const VariationStore &var_store = get_var_store ();
 507     if (likely (min && min_coord)) *min = min_coord->get_coord (font, var_store, direction);
 508     if (likely (max && max_coord)) *max = max_coord->get_coord (font, var_store, direction);
 509     return true;
 510   }
 511 
 512   bool sanitize (hb_sanitize_context_t *c) const
 513   {
 514     TRACE_SANITIZE (this);
 515     return_trace (likely (c->check_struct (this) &&
 516                           likely (version.major == 1) &&
 517                           hAxis.sanitize (c, this) &&
 518                           vAxis.sanitize (c, this) &&
 519                           (version.to_int () < 0x00010001u || varStore.sanitize (c, this))));
 520   }
 521 
 522   protected:
 523   FixedVersion<>version;        /* Version of the BASE table */
 524   OffsetTo<Axis>hAxis;          /* Offset to horizontal Axis table, from beginning
 525                                  * of BASE table (may be NULL) */
 526   OffsetTo<Axis>vAxis;          /* Offset to vertical Axis table, from beginning
 527                                  * of BASE table (may be NULL) */
 528   LOffsetTo<VariationStore>
 529                 varStore;       /* Offset to the table of Item Variation
 530                                  * Store--from beginning of BASE
 531                                  * header (may be NULL).  Introduced
 532                                  * in version 0x00010001. */
 533   public:
 534   DEFINE_SIZE_MIN (8);
 535 };
 536 
 537 
 538 } /* namespace OT */
 539 
 540 
 541 #endif /* HB_OT_LAYOUT_BASE_TABLE_HH */