1 /*
   2  * Copyright © 2016  Google, Inc.
   3  *
   4  *  This is part of HarfBuzz, a text shaping library.
   5  *
   6  * Permission is hereby granted, without written agreement and without
   7  * license or royalty fees, to use, copy, modify, and distribute this
   8  * software and its documentation for any purpose, provided that the
   9  * above copyright notice and the following two paragraphs appear in
  10  * all copies of this software.
  11  *
  12  * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
  13  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
  14  * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
  15  * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
  16  * DAMAGE.
  17  *
  18  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
  19  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  20  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  21  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
  22  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  23  *
  24  * Google Author(s): Seigo Nonaka
  25  */
  26 
  27 #ifndef HB_OT_COLOR_CBDT_TABLE_HH
  28 #define HB_OT_COLOR_CBDT_TABLE_HH
  29 
  30 #include "hb-open-type-private.hh"
  31 
  32 /*
  33  * CBLC -- Color Bitmap Location
  34  * https://docs.microsoft.com/en-us/typography/opentype/spec/cblc
  35  * https://docs.microsoft.com/en-us/typography/opentype/spec/eblc
  36  * CBDT -- Color Bitmap Data
  37  * https://docs.microsoft.com/en-us/typography/opentype/spec/cbdt
  38  * https://docs.microsoft.com/en-us/typography/opentype/spec/ebdt
  39  */
  40 #define HB_OT_TAG_CBLC HB_TAG('C','B','L','C')
  41 #define HB_OT_TAG_CBDT HB_TAG('C','B','D','T')
  42 
  43 
  44 namespace OT {
  45 
  46 struct SmallGlyphMetrics
  47 {
  48   inline bool sanitize (hb_sanitize_context_t *c) const
  49   {
  50     TRACE_SANITIZE (this);
  51     return_trace (c->check_struct (this));
  52   }
  53 
  54   inline void get_extents (hb_glyph_extents_t *extents) const
  55   {
  56     extents->x_bearing = bearingX;
  57     extents->y_bearing = bearingY;
  58     extents->width = width;
  59     extents->height = -height;
  60   }
  61 
  62   HBUINT8       height;
  63   HBUINT8       width;
  64   HBINT8        bearingX;
  65   HBINT8        bearingY;
  66   HBUINT8       advance;
  67   public:
  68   DEFINE_SIZE_STATIC(5);
  69 };
  70 
  71 struct BigGlyphMetrics : SmallGlyphMetrics
  72 {
  73   HBINT8        vertBearingX;
  74   HBINT8        vertBearingY;
  75   HBUINT8       vertAdvance;
  76   public:
  77   DEFINE_SIZE_STATIC(8);
  78 };
  79 
  80 struct SBitLineMetrics
  81 {
  82   inline bool sanitize (hb_sanitize_context_t *c) const
  83   {
  84     TRACE_SANITIZE (this);
  85     return_trace (c->check_struct (this));
  86   }
  87 
  88   HBINT8        ascender;
  89   HBINT8        decender;
  90   HBUINT8       widthMax;
  91   HBINT8        caretSlopeNumerator;
  92   HBINT8        caretSlopeDenominator;
  93   HBINT8        caretOffset;
  94   HBINT8        minOriginSB;
  95   HBINT8        minAdvanceSB;
  96   HBINT8        maxBeforeBL;
  97   HBINT8        minAfterBL;
  98   HBINT8        padding1;
  99   HBINT8        padding2;
 100   public:
 101   DEFINE_SIZE_STATIC(12);
 102 };
 103 
 104 
 105 /*
 106  * Index Subtables.
 107  */
 108 
 109 struct IndexSubtableHeader
 110 {
 111   inline bool sanitize (hb_sanitize_context_t *c) const
 112   {
 113     TRACE_SANITIZE (this);
 114     return_trace (c->check_struct (this));
 115   }
 116 
 117   HBUINT16      indexFormat;
 118   HBUINT16      imageFormat;
 119   HBUINT32      imageDataOffset;
 120   public:
 121   DEFINE_SIZE_STATIC(8);
 122 };
 123 
 124 template <typename OffsetType>
 125 struct IndexSubtableFormat1Or3
 126 {
 127   inline bool sanitize (hb_sanitize_context_t *c, unsigned int glyph_count) const
 128   {
 129     TRACE_SANITIZE (this);
 130     return_trace (c->check_struct (this) &&
 131                   c->check_array (offsetArrayZ, offsetArrayZ[0].static_size, glyph_count + 1));
 132   }
 133 
 134   bool get_image_data (unsigned int idx,
 135                        unsigned int *offset,
 136                        unsigned int *length) const
 137   {
 138     if (unlikely (offsetArrayZ[idx + 1] <= offsetArrayZ[idx]))
 139       return false;
 140 
 141     *offset = header.imageDataOffset + offsetArrayZ[idx];
 142     *length = offsetArrayZ[idx + 1] - offsetArrayZ[idx];
 143     return true;
 144   }
 145 
 146   IndexSubtableHeader   header;
 147   Offset<OffsetType>    offsetArrayZ[VAR];
 148   public:
 149   DEFINE_SIZE_ARRAY(8, offsetArrayZ);
 150 };
 151 
 152 struct IndexSubtableFormat1 : IndexSubtableFormat1Or3<HBUINT32> {};
 153 struct IndexSubtableFormat3 : IndexSubtableFormat1Or3<HBUINT16> {};
 154 
 155 struct IndexSubtable
 156 {
 157   inline bool sanitize (hb_sanitize_context_t *c, unsigned int glyph_count) const
 158   {
 159     TRACE_SANITIZE (this);
 160     if (!u.header.sanitize (c)) return_trace (false);
 161     switch (u.header.indexFormat) {
 162     case 1: return_trace (u.format1.sanitize (c, glyph_count));
 163     case 3: return_trace (u.format3.sanitize (c, glyph_count));
 164     default:return_trace (true);
 165     }
 166   }
 167 
 168   inline bool get_extents (hb_glyph_extents_t *extents) const
 169   {
 170     switch (u.header.indexFormat) {
 171     case 2: case 5: /* TODO */
 172     case 1: case 3: case 4: /* Variable-metrics formats do not have metrics here. */
 173     default:return (false);
 174     }
 175   }
 176 
 177   bool get_image_data (unsigned int idx,
 178                        unsigned int *offset,
 179                        unsigned int *length,
 180                        unsigned int *format) const
 181   {
 182     *format = u.header.imageFormat;
 183     switch (u.header.indexFormat) {
 184     case 1: return u.format1.get_image_data (idx, offset, length);
 185     case 3: return u.format3.get_image_data (idx, offset, length);
 186     default: return false;
 187     }
 188   }
 189 
 190   protected:
 191   union {
 192   IndexSubtableHeader   header;
 193   IndexSubtableFormat1  format1;
 194   IndexSubtableFormat3  format3;
 195   /* TODO: Format 2, 4, 5. */
 196   } u;
 197   public:
 198   DEFINE_SIZE_UNION (8, header);
 199 };
 200 
 201 struct IndexSubtableRecord
 202 {
 203   inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
 204   {
 205     TRACE_SANITIZE (this);
 206     return_trace (c->check_struct (this) &&
 207                   firstGlyphIndex <= lastGlyphIndex &&
 208                   offsetToSubtable.sanitize (c, this, lastGlyphIndex - firstGlyphIndex + 1));
 209   }
 210 
 211   inline bool get_extents (hb_glyph_extents_t *extents) const
 212   {
 213     return (this+offsetToSubtable).get_extents (extents);
 214   }
 215 
 216   bool get_image_data (unsigned int gid,
 217                        unsigned int *offset,
 218                        unsigned int *length,
 219                        unsigned int *format) const
 220   {
 221     if (gid < firstGlyphIndex || gid > lastGlyphIndex)
 222     {
 223       return false;
 224     }
 225     return (this+offsetToSubtable).get_image_data (gid - firstGlyphIndex,
 226                                                    offset, length, format);
 227   }
 228 
 229   GlyphID                       firstGlyphIndex;
 230   GlyphID                       lastGlyphIndex;
 231   LOffsetTo<IndexSubtable>      offsetToSubtable;
 232   public:
 233   DEFINE_SIZE_STATIC(8);
 234 };
 235 
 236 struct IndexSubtableArray
 237 {
 238   friend struct CBDT;
 239 
 240   inline bool sanitize (hb_sanitize_context_t *c, unsigned int count) const
 241   {
 242     TRACE_SANITIZE (this);
 243     if (unlikely (!c->check_array (&indexSubtablesZ, indexSubtablesZ[0].static_size, count)))
 244       return_trace (false);
 245     for (unsigned int i = 0; i < count; i++)
 246       if (unlikely (!indexSubtablesZ[i].sanitize (c, this)))
 247         return_trace (false);
 248     return_trace (true);
 249   }
 250 
 251   public:
 252   const IndexSubtableRecord* find_table (hb_codepoint_t glyph, unsigned int numTables) const
 253   {
 254     for (unsigned int i = 0; i < numTables; ++i)
 255     {
 256       unsigned int firstGlyphIndex = indexSubtablesZ[i].firstGlyphIndex;
 257       unsigned int lastGlyphIndex = indexSubtablesZ[i].lastGlyphIndex;
 258       if (firstGlyphIndex <= glyph && glyph <= lastGlyphIndex) {
 259         return &indexSubtablesZ[i];
 260       }
 261     }
 262     return nullptr;
 263   }
 264 
 265   protected:
 266   IndexSubtableRecord   indexSubtablesZ[VAR];
 267   public:
 268   DEFINE_SIZE_ARRAY(0, indexSubtablesZ);
 269 };
 270 
 271 struct BitmapSizeTable
 272 {
 273   friend struct CBLC;
 274   friend struct CBDT;
 275 
 276   inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
 277   {
 278     TRACE_SANITIZE (this);
 279     return_trace (c->check_struct (this) &&
 280                   indexSubtableArrayOffset.sanitize (c, base, numberOfIndexSubtables) &&
 281                   c->check_range (&(base+indexSubtableArrayOffset), indexTablesSize) &&
 282                   horizontal.sanitize (c) &&
 283                   vertical.sanitize (c));
 284   }
 285 
 286   const IndexSubtableRecord *find_table (hb_codepoint_t glyph, const void *base) const
 287   {
 288     return (base+indexSubtableArrayOffset).find_table (glyph, numberOfIndexSubtables);
 289   }
 290 
 291   protected:
 292   LOffsetTo<IndexSubtableArray>
 293                         indexSubtableArrayOffset;
 294   HBUINT32              indexTablesSize;
 295   HBUINT32              numberOfIndexSubtables;
 296   HBUINT32              colorRef;
 297   SBitLineMetrics       horizontal;
 298   SBitLineMetrics       vertical;
 299   GlyphID               startGlyphIndex;
 300   GlyphID               endGlyphIndex;
 301   HBUINT8               ppemX;
 302   HBUINT8               ppemY;
 303   HBUINT8               bitDepth;
 304   HBINT8                flags;
 305   public:
 306   DEFINE_SIZE_STATIC(48);
 307 };
 308 
 309 
 310 /*
 311  * Glyph Bitmap Data Formats.
 312  */
 313 
 314 struct GlyphBitmapDataFormat17
 315 {
 316   SmallGlyphMetrics     glyphMetrics;
 317   LArrayOf<HBUINT8>     data;
 318   public:
 319   DEFINE_SIZE_ARRAY(9, data);
 320 };
 321 
 322 struct GlyphBitmapDataFormat18
 323 {
 324   BigGlyphMetrics       glyphMetrics;
 325   LArrayOf<HBUINT8>     data;
 326   public:
 327   DEFINE_SIZE_ARRAY(12, data);
 328 };
 329 
 330 struct GlyphBitmapDataFormat19
 331 {
 332   LArrayOf<HBUINT8>     data;
 333   public:
 334   DEFINE_SIZE_ARRAY(4, data);
 335 };
 336 
 337 struct CBLC
 338 {
 339   friend struct CBDT;
 340 
 341   static const hb_tag_t tableTag = HB_OT_TAG_CBLC;
 342 
 343   inline bool sanitize (hb_sanitize_context_t *c) const
 344   {
 345     TRACE_SANITIZE (this);
 346     return_trace (c->check_struct (this) &&
 347                   likely (version.major == 2 || version.major == 3) &&
 348                   sizeTables.sanitize (c, this));
 349   }
 350 
 351   protected:
 352   const IndexSubtableRecord *find_table (hb_codepoint_t glyph,
 353                                          unsigned int *x_ppem, unsigned int *y_ppem) const
 354   {
 355     /* TODO: Make it possible to select strike. */
 356 
 357     unsigned int count = sizeTables.len;
 358     for (uint32_t i = 0; i < count; ++i)
 359     {
 360       unsigned int startGlyphIndex = sizeTables.arrayZ[i].startGlyphIndex;
 361       unsigned int endGlyphIndex = sizeTables.arrayZ[i].endGlyphIndex;
 362       if (startGlyphIndex <= glyph && glyph <= endGlyphIndex)
 363       {
 364         *x_ppem = sizeTables[i].ppemX;
 365         *y_ppem = sizeTables[i].ppemY;
 366         return sizeTables[i].find_table (glyph, this);
 367       }
 368     }
 369 
 370     return nullptr;
 371   }
 372 
 373   protected:
 374   FixedVersion<>                version;
 375   LArrayOf<BitmapSizeTable>     sizeTables;
 376   public:
 377   DEFINE_SIZE_ARRAY(8, sizeTables);
 378 };
 379 
 380 struct CBDT
 381 {
 382   static const hb_tag_t tableTag = HB_OT_TAG_CBDT;
 383 
 384   inline bool sanitize (hb_sanitize_context_t *c) const
 385   {
 386     TRACE_SANITIZE (this);
 387     return_trace (c->check_struct (this) &&
 388                   likely (version.major == 2 || version.major == 3));
 389   }
 390 
 391   struct accelerator_t
 392   {
 393     inline void init (hb_face_t *face)
 394     {
 395       upem = hb_face_get_upem (face);
 396 
 397       cblc_blob = Sanitizer<CBLC>().sanitize (face->reference_table (HB_OT_TAG_CBLC));
 398       cbdt_blob = Sanitizer<CBDT>().sanitize (face->reference_table (HB_OT_TAG_CBDT));
 399       cbdt_len = hb_blob_get_length (cbdt_blob);
 400 
 401       if (hb_blob_get_length (cblc_blob) == 0) {
 402         cblc = nullptr;
 403         cbdt = nullptr;
 404         return;  /* Not a bitmap font. */
 405       }
 406       cblc = cblc_blob->as<CBLC> ();
 407       cbdt = cbdt_blob->as<CBDT> ();
 408 
 409     }
 410 
 411     inline void fini (void)
 412     {
 413       hb_blob_destroy (this->cblc_blob);
 414       hb_blob_destroy (this->cbdt_blob);
 415     }
 416 
 417     inline bool get_extents (hb_codepoint_t glyph, hb_glyph_extents_t *extents) const
 418     {
 419       unsigned int x_ppem = upem, y_ppem = upem; /* TODO Use font ppem if available. */
 420 
 421       if (!cblc)
 422         return false;  // Not a color bitmap font.
 423 
 424       const IndexSubtableRecord *subtable_record = this->cblc->find_table(glyph, &x_ppem, &y_ppem);
 425       if (!subtable_record || !x_ppem || !y_ppem)
 426         return false;
 427 
 428       if (subtable_record->get_extents (extents))
 429         return true;
 430 
 431       unsigned int image_offset = 0, image_length = 0, image_format = 0;
 432       if (!subtable_record->get_image_data (glyph, &image_offset, &image_length, &image_format))
 433         return false;
 434 
 435       {
 436         if (unlikely (image_offset > cbdt_len || cbdt_len - image_offset < image_length))
 437           return false;
 438 
 439         switch (image_format)
 440         {
 441           case 17: {
 442             if (unlikely (image_length < GlyphBitmapDataFormat17::min_size))
 443               return false;
 444 
 445             const GlyphBitmapDataFormat17& glyphFormat17 =
 446                 StructAtOffset<GlyphBitmapDataFormat17> (this->cbdt, image_offset);
 447             glyphFormat17.glyphMetrics.get_extents (extents);
 448           }
 449           break;
 450           default:
 451             // TODO: Support other image formats.
 452             return false;
 453         }
 454       }
 455 
 456       /* Convert to the font units. */
 457       extents->x_bearing *= upem / (float) x_ppem;
 458       extents->y_bearing *= upem / (float) y_ppem;
 459       extents->width *= upem / (float) x_ppem;
 460       extents->height *= upem / (float) y_ppem;
 461 
 462       return true;
 463     }
 464 
 465     inline void dump (void (*callback) (const uint8_t* data, unsigned int length,
 466         unsigned int group, unsigned int gid)) const
 467     {
 468       if (!cblc)
 469         return;  // Not a color bitmap font.
 470 
 471       for (unsigned int i = 0; i < cblc->sizeTables.len; ++i)
 472       {
 473         const BitmapSizeTable &sizeTable = cblc->sizeTables[i];
 474         const IndexSubtableArray &subtable_array = cblc+sizeTable.indexSubtableArrayOffset;
 475         for (unsigned int j = 0; j < sizeTable.numberOfIndexSubtables; ++j)
 476         {
 477           const IndexSubtableRecord &subtable_record = subtable_array.indexSubtablesZ[j];
 478           for (unsigned int gid = subtable_record.firstGlyphIndex;
 479                 gid <= subtable_record.lastGlyphIndex; ++gid)
 480           {
 481             unsigned int image_offset = 0, image_length = 0, image_format = 0;
 482 
 483             if (!subtable_record.get_image_data (gid,
 484                   &image_offset, &image_length, &image_format))
 485               continue;
 486 
 487             switch (image_format)
 488             {
 489             case 17: {
 490               const GlyphBitmapDataFormat17& glyphFormat17 =
 491                 StructAtOffset<GlyphBitmapDataFormat17> (this->cbdt, image_offset);
 492               callback ((const uint8_t *) &glyphFormat17.data.arrayZ,
 493                 glyphFormat17.data.len, i, gid);
 494             }
 495             break;
 496             case 18: {
 497               const GlyphBitmapDataFormat18& glyphFormat18 =
 498                 StructAtOffset<GlyphBitmapDataFormat18> (this->cbdt, image_offset);
 499               callback ((const uint8_t *) &glyphFormat18.data.arrayZ,
 500                 glyphFormat18.data.len, i, gid);
 501             }
 502             break;
 503             case 19: {
 504               const GlyphBitmapDataFormat19& glyphFormat19 =
 505                 StructAtOffset<GlyphBitmapDataFormat19> (this->cbdt, image_offset);
 506               callback ((const uint8_t *) &glyphFormat19.data.arrayZ,
 507                 glyphFormat19.data.len, i, gid);
 508             }
 509             break;
 510             default:
 511               continue;
 512             }
 513           }
 514         }
 515       }
 516     }
 517 
 518     private:
 519     hb_blob_t *cblc_blob;
 520     hb_blob_t *cbdt_blob;
 521     const CBLC *cblc;
 522     const CBDT *cbdt;
 523 
 524     unsigned int cbdt_len;
 525     unsigned int upem;
 526   };
 527 
 528 
 529   protected:
 530   FixedVersion<>        version;
 531   HBUINT8               dataZ[VAR];
 532   public:
 533   DEFINE_SIZE_ARRAY(4, dataZ);
 534 };
 535 
 536 } /* namespace OT */
 537 
 538 #endif /* HB_OT_COLOR_CBDT_TABLE_HH */