1 /* 2 * Copyright © 2018 Ebrahim Byagowi 3 * Copyright © 2020 Google, Inc. 4 * 5 * This is part of HarfBuzz, a text shaping library. 6 * 7 * Permission is hereby granted, without written agreement and without 8 * license or royalty fees, to use, copy, modify, and distribute this 9 * software and its documentation for any purpose, provided that the 10 * above copyright notice and the following two paragraphs appear in 11 * all copies of this software. 12 * 13 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR 14 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 15 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN 16 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 17 * DAMAGE. 18 * 19 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, 20 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 21 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS 22 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO 23 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 24 * 25 * Google Author(s): Calder Kitagawa 26 */ 27 28 #ifndef HB_OT_COLOR_SBIX_TABLE_HH 29 #define HB_OT_COLOR_SBIX_TABLE_HH 30 31 #include "hb-open-type.hh" 32 #include "hb-ot-layout-common.hh" 33 34 /* 35 * sbix -- Standard Bitmap Graphics 36 * https://docs.microsoft.com/en-us/typography/opentype/spec/sbix 37 * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6sbix.html 38 */ 39 #define HB_OT_TAG_sbix HB_TAG('s', 'b', 'i', 'x') 40 41 namespace OT { 42 43 struct SBIXGlyph 44 { 45 HBINT16 xOffset; /* The horizontal (x-axis) offset from the left 46 * edge of the graphic to the glyph’s origin. 47 * That is, the x-coordinate of the point on the 48 * baseline at the left edge of the glyph. */ 49 HBINT16 yOffset; /* The vertical (y-axis) offset from the bottom 50 * edge of the graphic to the glyph’s origin. 51 * That is, the y-coordinate of the point on the 52 * baseline at the left edge of the glyph. */ 53 Tag graphicType; /* Indicates the format of the embedded graphic 54 * data: one of 'jpg ', 'png ' or 'tiff', or the 55 * special format 'dupe'. */ 56 UnsizedArrayOf<HBUINT8> data; /* The actual embedded graphic data. The total 57 * length is inferred from sequential entries in 58 * the glyphDataOffsets array and the fixed size 59 * (8 bytes) of the preceding fields. */ 60 public: 61 DEFINE_SIZE_ARRAY(8, data); 62 }; 63 64 struct SBIXStrike 65 { get_sizeOT::SBIXStrike66 static unsigned int get_size(unsigned num_glyphs) 67 { 68 return min_size + num_glyphs * HBUINT32::static_size; 69 } 70 sanitizeOT::SBIXStrike71 bool sanitize(hb_sanitize_context_t *c) const 72 { 73 TRACE_SANITIZE(this); 74 return_trace(c->check_struct(this) && imageOffsetsZ.sanitize_shallow(c, c->get_num_glyphs() + 1)); 75 } 76 get_glyph_blobOT::SBIXStrike77 hb_blob_t *get_glyph_blob(unsigned int glyph_id, 78 hb_blob_t *sbix_blob, 79 hb_tag_t file_type, 80 int *x_offset, 81 int *y_offset, 82 unsigned int num_glyphs, 83 unsigned int *strike_ppem) const 84 { 85 if (unlikely(!ppem)) 86 return hb_blob_get_empty(); /* To get Null() object out of the way. */ 87 88 unsigned int retry_count = 8; 89 unsigned int sbix_len = sbix_blob->length; 90 unsigned int strike_offset = (const char *)this - (const char *)sbix_blob->data; 91 assert(strike_offset < sbix_len); 92 93 retry: 94 if (unlikely(glyph_id >= num_glyphs || imageOffsetsZ[glyph_id + 1] <= imageOffsetsZ[glyph_id] || 95 imageOffsetsZ[glyph_id + 1] - imageOffsetsZ[glyph_id] <= SBIXGlyph::min_size || 96 (unsigned int)imageOffsetsZ[glyph_id + 1] > sbix_len - strike_offset)) 97 return hb_blob_get_empty(); 98 99 unsigned int glyph_offset = strike_offset + (unsigned int)imageOffsetsZ[glyph_id] + SBIXGlyph::min_size; 100 unsigned int glyph_length = imageOffsetsZ[glyph_id + 1] - imageOffsetsZ[glyph_id] - SBIXGlyph::min_size; 101 102 const SBIXGlyph *glyph = &(this + imageOffsetsZ[glyph_id]); 103 104 if (glyph->graphicType == HB_TAG('d', 'u', 'p', 'e')) { 105 if (glyph_length >= 2) { 106 glyph_id = *((HBUINT16 *)&glyph->data); 107 if (retry_count--) 108 goto retry; 109 } 110 return hb_blob_get_empty(); 111 } 112 113 if (unlikely(file_type != glyph->graphicType)) 114 return hb_blob_get_empty(); 115 116 if (strike_ppem) 117 *strike_ppem = ppem; 118 if (x_offset) 119 *x_offset = glyph->xOffset; 120 if (y_offset) 121 *y_offset = glyph->yOffset; 122 return hb_blob_create_sub_blob(sbix_blob, glyph_offset, glyph_length); 123 } 124 125 public: 126 HBUINT16 ppem; /* The PPEM size for which this strike was designed. */ 127 HBUINT16 resolution; /* The device pixel density (in PPI) for which this 128 * strike was designed. (E.g., 96 PPI, 192 PPI.) */ 129 protected: 130 UnsizedArrayOf<LOffsetTo<SBIXGlyph>> imageOffsetsZ; /* Offset from the beginning of the strike data header 131 * to bitmap data for an individual glyph ID. */ 132 public: 133 DEFINE_SIZE_ARRAY(4, imageOffsetsZ); 134 }; 135 136 struct sbix 137 { 138 static constexpr hb_tag_t tableTag = HB_OT_TAG_sbix; 139 has_dataOT::sbix140 bool has_data() const 141 { 142 return version; 143 } 144 get_strikeOT::sbix145 const SBIXStrike &get_strike(unsigned int i) const 146 { 147 return this + strikes[i]; 148 } 149 150 struct accelerator_t 151 { initOT::sbix::accelerator_t152 void init(hb_face_t *face) 153 { 154 table = hb_sanitize_context_t().reference_table<sbix>(face); 155 num_glyphs = face->get_num_glyphs(); 156 } finiOT::sbix::accelerator_t157 void fini() 158 { 159 table.destroy(); 160 } 161 has_dataOT::sbix::accelerator_t162 bool has_data() const 163 { 164 return table->has_data(); 165 } 166 get_extentsOT::sbix::accelerator_t167 bool get_extents(hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const 168 { 169 /* We only support PNG right now, and following function checks type. */ 170 return get_png_extents(font, glyph, extents); 171 } 172 reference_pngOT::sbix::accelerator_t173 hb_blob_t *reference_png( 174 hb_font_t *font, hb_codepoint_t glyph_id, int *x_offset, int *y_offset, unsigned int *available_ppem) const 175 { 176 return choose_strike(font).get_glyph_blob( 177 glyph_id, table.get_blob(), HB_TAG('p', 'n', 'g', ' '), x_offset, y_offset, num_glyphs, available_ppem); 178 } 179 180 private: choose_strikeOT::sbix::accelerator_t181 const SBIXStrike &choose_strike(hb_font_t *font) const 182 { 183 unsigned count = table->strikes.len; 184 if (unlikely(!count)) 185 return Null(SBIXStrike); 186 187 unsigned int requested_ppem = hb_max(font->x_ppem, font->y_ppem); 188 if (!requested_ppem) 189 requested_ppem = 1 << 30; /* Choose largest strike. */ 190 /* TODO Add DPI sensitivity as well? */ 191 unsigned int best_i = 0; 192 unsigned int best_ppem = table->get_strike(0).ppem; 193 194 for (unsigned int i = 1; i < count; i++) { 195 unsigned int ppem = (table->get_strike(i)).ppem; 196 if ((requested_ppem <= ppem && ppem < best_ppem) || (requested_ppem > best_ppem && ppem > best_ppem)) { 197 best_i = i; 198 best_ppem = ppem; 199 } 200 } 201 202 return table->get_strike(best_i); 203 } 204 205 struct PNGHeader 206 { 207 HBUINT8 signature[8]; 208 struct 209 { 210 struct 211 { 212 HBUINT32 length; 213 Tag type; 214 } header; 215 HBUINT32 width; 216 HBUINT32 height; 217 HBUINT8 bitDepth; 218 HBUINT8 colorType; 219 HBUINT8 compressionMethod; 220 HBUINT8 filterMethod; 221 HBUINT8 interlaceMethod; 222 } IHDR; 223 224 public: 225 DEFINE_SIZE_STATIC(29); 226 }; 227 get_png_extentsOT::sbix::accelerator_t228 bool get_png_extents(hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const 229 { 230 /* Following code is safe to call even without data. 231 * But faster to short-circuit. */ 232 if (!has_data()) 233 return false; 234 235 int x_offset = 0, y_offset = 0; 236 unsigned int strike_ppem = 0; 237 hb_blob_t *blob = reference_png(font, glyph, &x_offset, &y_offset, &strike_ppem); 238 239 const PNGHeader &png = *blob->as<PNGHeader>(); 240 241 extents->x_bearing = x_offset; 242 extents->y_bearing = png.IHDR.height + y_offset; 243 extents->width = png.IHDR.width; 244 extents->height = -1 * png.IHDR.height; 245 246 /* Convert to font units. */ 247 if (strike_ppem) { 248 float scale = font->face->get_upem() / (float)strike_ppem; 249 extents->x_bearing = font->em_scalef_x(extents->x_bearing * scale); 250 extents->y_bearing = font->em_scalef_y(extents->y_bearing * scale); 251 extents->width = font->em_scalef_x(extents->width * scale); 252 extents->height = font->em_scalef_y(extents->height * scale); 253 } else { 254 extents->x_bearing = font->em_scale_x(extents->x_bearing); 255 extents->y_bearing = font->em_scale_y(extents->y_bearing); 256 extents->width = font->em_scale_x(extents->width); 257 extents->height = font->em_scale_y(extents->height); 258 } 259 260 hb_blob_destroy(blob); 261 262 return strike_ppem; 263 } 264 265 private: 266 hb_blob_ptr_t<sbix> table; 267 268 unsigned int num_glyphs; 269 }; 270 sanitizeOT::sbix271 bool sanitize(hb_sanitize_context_t *c) const 272 { 273 TRACE_SANITIZE(this); 274 return_trace(likely(c->check_struct(this) && version >= 1 && strikes.sanitize(c, this))); 275 } 276 277 protected: 278 HBUINT16 version; /* Table version number — set to 1 */ 279 HBUINT16 flags; /* Bit 0: Set to 1. Bit 1: Draw outlines. 280 * Bits 2 to 15: reserved (set to 0). */ 281 LOffsetLArrayOf<SBIXStrike> strikes; /* Offsets from the beginning of the 'sbix' 282 * table to data for each individual bitmap strike. */ 283 public: 284 DEFINE_SIZE_ARRAY(8, strikes); 285 }; 286 287 struct sbix_accelerator_t : sbix::accelerator_t 288 { 289 }; 290 291 } /* namespace OT */ 292 293 #endif /* HB_OT_COLOR_SBIX_TABLE_HH */ 294