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