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