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, Calder Kitagawa
25  */
26 
27 #ifndef HB_OT_COLOR_CBDT_TABLE_HH
28 #define HB_OT_COLOR_CBDT_TABLE_HH
29 
30 #include "hb-open-type.hh"
31 #include "hb-font.hh"
32 
33 /*
34  * CBLC -- Color Bitmap Location
35  * https://docs.microsoft.com/en-us/typography/opentype/spec/cblc
36  * https://docs.microsoft.com/en-us/typography/opentype/spec/eblc
37  * CBDT -- Color Bitmap Data
38  * https://docs.microsoft.com/en-us/typography/opentype/spec/cbdt
39  * https://docs.microsoft.com/en-us/typography/opentype/spec/ebdt
40  */
41 #define HB_OT_TAG_CBLC HB_TAG('C', 'B', 'L', 'C')
42 #define HB_OT_TAG_CBDT HB_TAG('C', 'B', 'D', 'T')
43 
44 namespace OT {
45 
46 struct cblc_bitmap_size_subset_context_t
47 {
48     const char *cbdt;
49     unsigned int cbdt_length;
50     hb_vector_t<char> *cbdt_prime;
51     unsigned int size;          /* INOUT
52                                  *  Input: old size of IndexSubtable
53                                  *  Output: new size of IndexSubtable
54                                  */
55     unsigned int num_tables;    /* INOUT
56                                  *  Input: old number of subtables.
57                                  *  Output: new number of subtables.
58                                  */
59     hb_codepoint_t start_glyph; /* OUT */
60     hb_codepoint_t end_glyph;   /* OUT */
61 };
62 
_copy_data_to_cbdt(hb_vector_t<char> * cbdt_prime,const void * data,unsigned length)63 static inline bool _copy_data_to_cbdt(hb_vector_t<char> *cbdt_prime, const void *data, unsigned length)
64 {
65     unsigned int new_len = cbdt_prime->length + length;
66     if (unlikely(!cbdt_prime->alloc(new_len)))
67         return false;
68     memcpy(cbdt_prime->arrayZ + cbdt_prime->length, data, length);
69     cbdt_prime->length = new_len;
70     return true;
71 }
72 
73 struct SmallGlyphMetrics
74 {
sanitizeOT::SmallGlyphMetrics75     bool sanitize(hb_sanitize_context_t *c) const
76     {
77         TRACE_SANITIZE(this);
78         return_trace(c->check_struct(this));
79     }
80 
get_extentsOT::SmallGlyphMetrics81     void get_extents(hb_font_t *font, hb_glyph_extents_t *extents) const
82     {
83         extents->x_bearing = font->em_scale_x(bearingX);
84         extents->y_bearing = font->em_scale_y(bearingY);
85         extents->width = font->em_scale_x(width);
86         extents->height = font->em_scale_y(-static_cast<int>(height));
87     }
88 
89     HBUINT8 height;
90     HBUINT8 width;
91     HBINT8 bearingX;
92     HBINT8 bearingY;
93     HBUINT8 advance;
94 
95 public:
96     DEFINE_SIZE_STATIC(5);
97 };
98 
99 struct BigGlyphMetrics : SmallGlyphMetrics
100 {
101     HBINT8 vertBearingX;
102     HBINT8 vertBearingY;
103     HBUINT8 vertAdvance;
104 
105 public:
106     DEFINE_SIZE_STATIC(8);
107 };
108 
109 struct SBitLineMetrics
110 {
sanitizeOT::SBitLineMetrics111     bool sanitize(hb_sanitize_context_t *c) const
112     {
113         TRACE_SANITIZE(this);
114         return_trace(c->check_struct(this));
115     }
116 
117     HBINT8 ascender;
118     HBINT8 decender;
119     HBUINT8 widthMax;
120     HBINT8 caretSlopeNumerator;
121     HBINT8 caretSlopeDenominator;
122     HBINT8 caretOffset;
123     HBINT8 minOriginSB;
124     HBINT8 minAdvanceSB;
125     HBINT8 maxBeforeBL;
126     HBINT8 minAfterBL;
127     HBINT8 padding1;
128     HBINT8 padding2;
129 
130 public:
131     DEFINE_SIZE_STATIC(12);
132 };
133 
134 /*
135  * Index Subtables.
136  */
137 
138 struct IndexSubtableHeader
139 {
sanitizeOT::IndexSubtableHeader140     bool sanitize(hb_sanitize_context_t *c) const
141     {
142         TRACE_SANITIZE(this);
143         return_trace(c->check_struct(this));
144     }
145 
146     HBUINT16 indexFormat;
147     HBUINT16 imageFormat;
148     HBUINT32 imageDataOffset;
149 
150 public:
151     DEFINE_SIZE_STATIC(8);
152 };
153 
154 template <typename OffsetType> struct IndexSubtableFormat1Or3
155 {
sanitizeOT::IndexSubtableFormat1Or3156     bool sanitize(hb_sanitize_context_t *c, unsigned int glyph_count) const
157     {
158         TRACE_SANITIZE(this);
159         return_trace(c->check_struct(this) && offsetArrayZ.sanitize(c, glyph_count + 1));
160     }
161 
get_image_dataOT::IndexSubtableFormat1Or3162     bool get_image_data(unsigned int idx, unsigned int *offset, unsigned int *length) const
163     {
164         if (unlikely(offsetArrayZ[idx + 1] <= offsetArrayZ[idx]))
165             return false;
166 
167         *offset = header.imageDataOffset + offsetArrayZ[idx];
168         *length = offsetArrayZ[idx + 1] - offsetArrayZ[idx];
169         return true;
170     }
171 
172     IndexSubtableHeader header;
173     UnsizedArrayOf<Offset<OffsetType>> offsetArrayZ;
174 
175 public:
176     DEFINE_SIZE_ARRAY(8, offsetArrayZ);
177 };
178 
179 struct IndexSubtableFormat1 : IndexSubtableFormat1Or3<HBUINT32>
180 {
181 };
182 struct IndexSubtableFormat3 : IndexSubtableFormat1Or3<HBUINT16>
183 {
184 };
185 
186 struct IndexSubtable
187 {
sanitizeOT::IndexSubtable188     bool sanitize(hb_sanitize_context_t *c, unsigned int glyph_count) const
189     {
190         TRACE_SANITIZE(this);
191         if (!u.header.sanitize(c))
192             return_trace(false);
193         switch (u.header.indexFormat) {
194         case 1:
195             return_trace(u.format1.sanitize(c, glyph_count));
196         case 3:
197             return_trace(u.format3.sanitize(c, glyph_count));
198         default:
199             return_trace(true);
200         }
201     }
202 
get_extentsOT::IndexSubtable203     bool get_extents(hb_glyph_extents_t *extents HB_UNUSED) const
204     {
205         switch (u.header.indexFormat) {
206         case 2:
207         case 5: /* TODO */
208         case 1:
209         case 3:
210         case 4: /* Variable-metrics formats do not have metrics here. */
211         default:
212             return (false);
213         }
214     }
215 
get_image_dataOT::IndexSubtable216     bool get_image_data(unsigned int idx, unsigned int *offset, unsigned int *length, unsigned int *format) const
217     {
218         *format = u.header.imageFormat;
219         switch (u.header.indexFormat) {
220         case 1:
221             return u.format1.get_image_data(idx, offset, length);
222         case 3:
223             return u.format3.get_image_data(idx, offset, length);
224         default:
225             return false;
226         }
227     }
228 
get_headerOT::IndexSubtable229     const IndexSubtableHeader *get_header() const
230     {
231         return &u.header;
232     }
233 
234     void
populate_headerOT::IndexSubtable235     populate_header(unsigned index_format, unsigned image_format, unsigned int image_data_offset, unsigned int *size)
236     {
237         u.header.indexFormat = index_format;
238         u.header.imageFormat = image_format;
239         u.header.imageDataOffset = image_data_offset;
240         switch (u.header.indexFormat) {
241         case 1:
242             *size += IndexSubtableFormat1::min_size;
243             break;
244         case 3:
245             *size += IndexSubtableFormat3::min_size;
246             break;
247         }
248     }
249 
250 protected:
251     union {
252         IndexSubtableHeader header;
253         IndexSubtableFormat1 format1;
254         IndexSubtableFormat3 format3;
255         /* TODO: Format 2, 4, 5. */
256     } u;
257 
258 public:
259     DEFINE_SIZE_UNION(8, header);
260 };
261 
262 struct IndexSubtableRecord
263 {
sanitizeOT::IndexSubtableRecord264     bool sanitize(hb_sanitize_context_t *c, const void *base) const
265     {
266         TRACE_SANITIZE(this);
267         return_trace(c->check_struct(this) && firstGlyphIndex <= lastGlyphIndex &&
268                      offsetToSubtable.sanitize(c, base, lastGlyphIndex - firstGlyphIndex + 1));
269     }
270 
get_subtableOT::IndexSubtableRecord271     const IndexSubtable *get_subtable(const void *base) const
272     {
273         return &(base + offsetToSubtable);
274     }
275 
add_glyph_for_subsetOT::IndexSubtableRecord276     unsigned int add_glyph_for_subset(hb_codepoint_t gid)
277     {
278         if (firstGlyphIndex > lastGlyphIndex) {
279             firstGlyphIndex = gid;
280             lastGlyphIndex = gid;
281             return 0;
282         }
283         // TODO maybe assert? this shouldn't occur.
284         if (lastGlyphIndex > gid)
285             return 0;
286         unsigned int num_missing = (unsigned int)(gid - lastGlyphIndex - 1);
287         lastGlyphIndex = gid;
288         return num_missing;
289     }
290 
get_extentsOT::IndexSubtableRecord291     bool get_extents(hb_glyph_extents_t *extents, const void *base) const
292     {
293         return (base + offsetToSubtable).get_extents(extents);
294     }
295 
get_image_dataOT::IndexSubtableRecord296     bool get_image_data(
297         unsigned int gid, const void *base, unsigned int *offset, unsigned int *length, unsigned int *format) const
298     {
299         if (gid < firstGlyphIndex || gid > lastGlyphIndex)
300             return false;
301         return (base + offsetToSubtable).get_image_data(gid - firstGlyphIndex, offset, length, format);
302     }
303 
304     HBGlyphID firstGlyphIndex;
305     HBGlyphID lastGlyphIndex;
306     LOffsetTo<IndexSubtable> offsetToSubtable;
307 
308 public:
309     DEFINE_SIZE_STATIC(8);
310 };
311 
312 struct IndexSubtableArray
313 {
314     friend struct CBDT;
315 
sanitizeOT::IndexSubtableArray316     bool sanitize(hb_sanitize_context_t *c, unsigned int count) const
317     {
318         TRACE_SANITIZE(this);
319         return_trace(indexSubtablesZ.sanitize(c, count, this));
320     }
321 
322 public:
find_tableOT::IndexSubtableArray323     const IndexSubtableRecord *find_table(hb_codepoint_t glyph, unsigned int numTables) const
324     {
325         for (unsigned int i = 0; i < numTables; ++i) {
326             unsigned int firstGlyphIndex = indexSubtablesZ[i].firstGlyphIndex;
327             unsigned int lastGlyphIndex = indexSubtablesZ[i].lastGlyphIndex;
328             if (firstGlyphIndex <= glyph && glyph <= lastGlyphIndex)
329                 return &indexSubtablesZ[i];
330         }
331         return nullptr;
332     }
333 
334 protected:
335     UnsizedArrayOf<IndexSubtableRecord> indexSubtablesZ;
336 };
337 
338 struct BitmapSizeTable
339 {
340     friend struct CBLC;
341     friend struct CBDT;
342 
sanitizeOT::BitmapSizeTable343     bool sanitize(hb_sanitize_context_t *c, const void *base) const
344     {
345         TRACE_SANITIZE(this);
346         return_trace(c->check_struct(this) && indexSubtableArrayOffset.sanitize(c, base, numberOfIndexSubtables) &&
347                      horizontal.sanitize(c) && vertical.sanitize(c));
348     }
349 
find_tableOT::BitmapSizeTable350     const IndexSubtableRecord *find_table(hb_codepoint_t glyph, const void *base, const void **out_base) const
351     {
352         *out_base = &(base + indexSubtableArrayOffset);
353         return (base + indexSubtableArrayOffset).find_table(glyph, numberOfIndexSubtables);
354     }
355 
356 protected:
357     LNNOffsetTo<IndexSubtableArray> indexSubtableArrayOffset;
358     HBUINT32 indexTablesSize;
359     HBUINT32 numberOfIndexSubtables;
360     HBUINT32 colorRef;
361     SBitLineMetrics horizontal;
362     SBitLineMetrics vertical;
363     HBGlyphID startGlyphIndex;
364     HBGlyphID endGlyphIndex;
365     HBUINT8 ppemX;
366     HBUINT8 ppemY;
367     HBUINT8 bitDepth;
368     HBINT8 flags;
369 
370 public:
371     DEFINE_SIZE_STATIC(48);
372 };
373 
374 /*
375  * Glyph Bitmap Data Formats.
376  */
377 
378 struct GlyphBitmapDataFormat17
379 {
380     SmallGlyphMetrics glyphMetrics;
381     LArrayOf<HBUINT8> data;
382 
383 public:
384     DEFINE_SIZE_ARRAY(9, data);
385 };
386 
387 struct GlyphBitmapDataFormat18
388 {
389     BigGlyphMetrics glyphMetrics;
390     LArrayOf<HBUINT8> data;
391 
392 public:
393     DEFINE_SIZE_ARRAY(12, data);
394 };
395 
396 struct GlyphBitmapDataFormat19
397 {
398     LArrayOf<HBUINT8> data;
399 
400 public:
401     DEFINE_SIZE_ARRAY(4, data);
402 };
403 
404 struct CBLC
405 {
406     friend struct CBDT;
407 
408     static constexpr hb_tag_t tableTag = HB_OT_TAG_CBLC;
409 
sanitizeOT::CBLC410     bool sanitize(hb_sanitize_context_t *c) const
411     {
412         TRACE_SANITIZE(this);
413         return_trace(c->check_struct(this) && likely(version.major == 2 || version.major == 3) &&
414                      sizeTables.sanitize(c, this));
415     }
416 
417 protected:
choose_strikeOT::CBLC418     const BitmapSizeTable &choose_strike(hb_font_t *font) const
419     {
420         unsigned count = sizeTables.len;
421         if (unlikely(!count))
422             return Null(BitmapSizeTable);
423 
424         unsigned int requested_ppem = hb_max(font->x_ppem, font->y_ppem);
425         if (!requested_ppem)
426             requested_ppem = 1 << 30; /* Choose largest strike. */
427         unsigned int best_i = 0;
428         unsigned int best_ppem = hb_max(sizeTables[0].ppemX, sizeTables[0].ppemY);
429 
430         for (unsigned int i = 1; i < count; i++) {
431             unsigned int ppem = hb_max(sizeTables[i].ppemX, sizeTables[i].ppemY);
432             if ((requested_ppem <= ppem && ppem < best_ppem) || (requested_ppem > best_ppem && ppem > best_ppem)) {
433                 best_i = i;
434                 best_ppem = ppem;
435             }
436         }
437 
438         return sizeTables[best_i];
439     }
440 
441 protected:
442     FixedVersion<> version;
443     LArrayOf<BitmapSizeTable> sizeTables;
444 
445 public:
446     DEFINE_SIZE_ARRAY(8, sizeTables);
447 };
448 
449 struct CBDT
450 {
451     static constexpr hb_tag_t tableTag = HB_OT_TAG_CBDT;
452 
453     struct accelerator_t
454     {
initOT::CBDT::accelerator_t455         void init(hb_face_t *face)
456         {
457             cblc = hb_sanitize_context_t().reference_table<CBLC>(face);
458             cbdt = hb_sanitize_context_t().reference_table<CBDT>(face);
459 
460             upem = hb_face_get_upem(face);
461         }
462 
finiOT::CBDT::accelerator_t463         void fini()
464         {
465             this->cblc.destroy();
466             this->cbdt.destroy();
467         }
468 
get_extentsOT::CBDT::accelerator_t469         bool get_extents(hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const
470         {
471             const void *base;
472             const BitmapSizeTable &strike = this->cblc->choose_strike(font);
473             const IndexSubtableRecord *subtable_record = strike.find_table(glyph, cblc, &base);
474             if (!subtable_record || !strike.ppemX || !strike.ppemY)
475                 return false;
476 
477             if (subtable_record->get_extents(extents, base))
478                 return true;
479 
480             unsigned int image_offset = 0, image_length = 0, image_format = 0;
481             if (!subtable_record->get_image_data(glyph, base, &image_offset, &image_length, &image_format))
482                 return false;
483 
484             unsigned int cbdt_len = cbdt.get_length();
485             if (unlikely(image_offset > cbdt_len || cbdt_len - image_offset < image_length))
486                 return false;
487 
488             switch (image_format) {
489             case 17: {
490                 if (unlikely(image_length < GlyphBitmapDataFormat17::min_size))
491                     return false;
492                 auto &glyphFormat17 = StructAtOffset<GlyphBitmapDataFormat17>(this->cbdt, image_offset);
493                 glyphFormat17.glyphMetrics.get_extents(font, extents);
494                 break;
495             }
496             case 18: {
497                 if (unlikely(image_length < GlyphBitmapDataFormat18::min_size))
498                     return false;
499                 auto &glyphFormat18 = StructAtOffset<GlyphBitmapDataFormat18>(this->cbdt, image_offset);
500                 glyphFormat18.glyphMetrics.get_extents(font, extents);
501                 break;
502             }
503             default:
504                 return false; /* TODO: Support other image formats. */
505             }
506 
507             /* Convert to font units. */
508             float x_scale = upem / (float)strike.ppemX;
509             float y_scale = upem / (float)strike.ppemY;
510             extents->x_bearing = roundf(extents->x_bearing * x_scale);
511             extents->y_bearing = roundf(extents->y_bearing * y_scale);
512             extents->width = roundf(extents->width * x_scale);
513             extents->height = roundf(extents->height * y_scale);
514 
515             return true;
516         }
517 
reference_pngOT::CBDT::accelerator_t518         hb_blob_t *reference_png(hb_font_t *font, hb_codepoint_t glyph) const
519         {
520             const void *base;
521             const BitmapSizeTable &strike = this->cblc->choose_strike(font);
522             const IndexSubtableRecord *subtable_record = strike.find_table(glyph, cblc, &base);
523             if (!subtable_record || !strike.ppemX || !strike.ppemY)
524                 return hb_blob_get_empty();
525 
526             unsigned int image_offset = 0, image_length = 0, image_format = 0;
527             if (!subtable_record->get_image_data(glyph, base, &image_offset, &image_length, &image_format))
528                 return hb_blob_get_empty();
529 
530             unsigned int cbdt_len = cbdt.get_length();
531             if (unlikely(image_offset > cbdt_len || cbdt_len - image_offset < image_length))
532                 return hb_blob_get_empty();
533 
534             switch (image_format) {
535             case 17: {
536                 if (unlikely(image_length < GlyphBitmapDataFormat17::min_size))
537                     return hb_blob_get_empty();
538                 auto &glyphFormat17 = StructAtOffset<GlyphBitmapDataFormat17>(this->cbdt, image_offset);
539                 return hb_blob_create_sub_blob(
540                     cbdt.get_blob(), image_offset + GlyphBitmapDataFormat17::min_size, glyphFormat17.data.len);
541             }
542             case 18: {
543                 if (unlikely(image_length < GlyphBitmapDataFormat18::min_size))
544                     return hb_blob_get_empty();
545                 auto &glyphFormat18 = StructAtOffset<GlyphBitmapDataFormat18>(this->cbdt, image_offset);
546                 return hb_blob_create_sub_blob(
547                     cbdt.get_blob(), image_offset + GlyphBitmapDataFormat18::min_size, glyphFormat18.data.len);
548             }
549             case 19: {
550                 if (unlikely(image_length < GlyphBitmapDataFormat19::min_size))
551                     return hb_blob_get_empty();
552                 auto &glyphFormat19 = StructAtOffset<GlyphBitmapDataFormat19>(this->cbdt, image_offset);
553                 return hb_blob_create_sub_blob(
554                     cbdt.get_blob(), image_offset + GlyphBitmapDataFormat19::min_size, glyphFormat19.data.len);
555             }
556             default:
557                 return hb_blob_get_empty(); /* TODO: Support other image formats. */
558             }
559         }
560 
has_dataOT::CBDT::accelerator_t561         bool has_data() const
562         {
563             return cbdt.get_length();
564         }
565 
566     private:
567         hb_blob_ptr_t<CBLC> cblc;
568         hb_blob_ptr_t<CBDT> cbdt;
569 
570         unsigned int upem;
571     };
572 
sanitizeOT::CBDT573     bool sanitize(hb_sanitize_context_t *c) const
574     {
575         TRACE_SANITIZE(this);
576         return_trace(c->check_struct(this) && likely(version.major == 2 || version.major == 3));
577     }
578 
579 protected:
580     FixedVersion<> version;
581     UnsizedArrayOf<HBUINT8> dataZ;
582 
583 public:
584     DEFINE_SIZE_ARRAY(4, dataZ);
585 };
586 
587 struct CBDT_accelerator_t : CBDT::accelerator_t
588 {
589 };
590 
591 } /* namespace OT */
592 
593 #endif /* HB_OT_COLOR_CBDT_TABLE_HH */
594