Home | History | Annotate | Download | only in src
      1 /*
      2  * Copyright  2018  Ebrahim Byagowi
      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 
     25 #ifndef HB_OT_COLOR_SBIX_TABLE_HH
     26 #define HB_OT_COLOR_SBIX_TABLE_HH
     27 
     28 #include "hb-open-type.hh"
     29 
     30 /*
     31  * sbix -- Standard Bitmap Graphics
     32  * https://docs.microsoft.com/en-us/typography/opentype/spec/sbix
     33  * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6sbix.html
     34  */
     35 #define HB_OT_TAG_sbix HB_TAG('s','b','i','x')
     36 
     37 
     38 namespace OT {
     39 
     40 
     41 struct SBIXGlyph
     42 {
     43   HBINT16	xOffset;	/* The horizontal (x-axis) offset from the left
     44 				 * edge of the graphic to the glyphs origin.
     45 				 * That is, the x-coordinate of the point on the
     46 				 * baseline at the left edge of the glyph. */
     47   HBINT16	yOffset;	/* The vertical (y-axis) offset from the bottom
     48 				 * edge of the graphic to the glyphs origin.
     49 				 * That is, the y-coordinate of the point on the
     50 				 * baseline at the left edge of the glyph. */
     51   Tag		graphicType;	/* Indicates the format of the embedded graphic
     52 				 * data: one of 'jpg ', 'png ' or 'tiff', or the
     53 				 * special format 'dupe'. */
     54   UnsizedArrayOf<HBUINT8>
     55 		data;		/* The actual embedded graphic data. The total
     56 				 * length is inferred from sequential entries in
     57 				 * the glyphDataOffsets array and the fixed size
     58 				 * (8 bytes) of the preceding fields. */
     59   public:
     60   DEFINE_SIZE_ARRAY (8, data);
     61 };
     62 
     63 struct SBIXStrike
     64 {
     65   bool sanitize (hb_sanitize_context_t *c) const
     66   {
     67     TRACE_SANITIZE (this);
     68     return_trace (c->check_struct (this) &&
     69 		  imageOffsetsZ.sanitize_shallow (c, c->get_num_glyphs () + 1));
     70   }
     71 
     72   hb_blob_t *get_glyph_blob (unsigned int  glyph_id,
     73 			     hb_blob_t    *sbix_blob,
     74 			     hb_tag_t      file_type,
     75 			     int          *x_offset,
     76 			     int          *y_offset,
     77 			     unsigned int  num_glyphs,
     78 			     unsigned int *strike_ppem) const
     79   {
     80     if (unlikely (!ppem)) return hb_blob_get_empty (); /* To get Null() object out of the way. */
     81 
     82     unsigned int retry_count = 8;
     83     unsigned int sbix_len = sbix_blob->length;
     84     unsigned int strike_offset = (const char *) this - (const char *) sbix_blob->data;
     85     assert (strike_offset < sbix_len);
     86 
     87   retry:
     88     if (unlikely (glyph_id >= num_glyphs ||
     89 		  imageOffsetsZ[glyph_id + 1] <= imageOffsetsZ[glyph_id] ||
     90 		  imageOffsetsZ[glyph_id + 1] - imageOffsetsZ[glyph_id] <= SBIXGlyph::min_size ||
     91 		  (unsigned int) imageOffsetsZ[glyph_id + 1] > sbix_len - strike_offset))
     92       return hb_blob_get_empty ();
     93 
     94     unsigned int glyph_offset = strike_offset + (unsigned int) imageOffsetsZ[glyph_id] + SBIXGlyph::min_size;
     95     unsigned int glyph_length = imageOffsetsZ[glyph_id + 1] - imageOffsetsZ[glyph_id] - SBIXGlyph::min_size;
     96 
     97     const SBIXGlyph *glyph = &(this+imageOffsetsZ[glyph_id]);
     98 
     99     if (glyph->graphicType == HB_TAG ('d','u','p','e'))
    100     {
    101       if (glyph_length >= 2)
    102       {
    103 	glyph_id = *((HBUINT16 *) &glyph->data);
    104 	if (retry_count--)
    105 	  goto retry;
    106       }
    107       return hb_blob_get_empty ();
    108     }
    109 
    110     if (unlikely (file_type != glyph->graphicType))
    111       return hb_blob_get_empty ();
    112 
    113     if (strike_ppem) *strike_ppem = ppem;
    114     if (x_offset) *x_offset = glyph->xOffset;
    115     if (y_offset) *y_offset = glyph->yOffset;
    116     return hb_blob_create_sub_blob (sbix_blob, glyph_offset, glyph_length);
    117   }
    118 
    119   public:
    120   HBUINT16	ppem;		/* The PPEM size for which this strike was designed. */
    121   HBUINT16	resolution;	/* The device pixel density (in PPI) for which this
    122 				 * strike was designed. (E.g., 96 PPI, 192 PPI.) */
    123   protected:
    124   UnsizedArrayOf<LOffsetTo<SBIXGlyph> >
    125 		imageOffsetsZ;	/* Offset from the beginning of the strike data header
    126 				 * to bitmap data for an individual glyph ID. */
    127   public:
    128   DEFINE_SIZE_STATIC (8);
    129 };
    130 
    131 struct sbix
    132 {
    133   enum { tableTag = HB_OT_TAG_sbix };
    134 
    135   bool has_data () const { return version; }
    136 
    137   const SBIXStrike &get_strike (unsigned int i) const { return this+strikes[i]; }
    138 
    139   struct accelerator_t
    140   {
    141     void init (hb_face_t *face)
    142     {
    143       table = hb_sanitize_context_t().reference_table<sbix> (face);
    144       num_glyphs = face->get_num_glyphs ();
    145     }
    146     void fini () { table.destroy (); }
    147 
    148     bool has_data () const { return table->has_data (); }
    149 
    150     bool get_extents (hb_font_t          *font,
    151 		      hb_codepoint_t      glyph,
    152 		      hb_glyph_extents_t *extents) const
    153     {
    154       /* We only support PNG right now, and following function checks type. */
    155       return get_png_extents (font, glyph, extents);
    156     }
    157 
    158     hb_blob_t *reference_png (hb_font_t      *font,
    159 			      hb_codepoint_t  glyph_id,
    160 			      int            *x_offset,
    161 			      int            *y_offset,
    162 			      unsigned int   *available_ppem) const
    163     {
    164       return choose_strike (font).get_glyph_blob (glyph_id, table.get_blob (),
    165 						  HB_TAG ('p','n','g',' '),
    166 						  x_offset, y_offset,
    167 						  num_glyphs, available_ppem);
    168     }
    169 
    170     private:
    171 
    172     const SBIXStrike &choose_strike (hb_font_t *font) const
    173     {
    174       unsigned count = table->strikes.len;
    175       if (unlikely (!count))
    176         return Null(SBIXStrike);
    177 
    178       unsigned int requested_ppem = MAX (font->x_ppem, font->y_ppem);
    179       if (!requested_ppem)
    180         requested_ppem = 1<<30; /* Choose largest strike. */
    181       /* TODO Add DPI sensitivity as well? */
    182       unsigned int best_i = 0;
    183       unsigned int best_ppem = table->get_strike (0).ppem;
    184 
    185       for (unsigned int i = 1; i < count; i++)
    186       {
    187 	unsigned int ppem = (table->get_strike (i)).ppem;
    188 	if ((requested_ppem <= ppem && ppem < best_ppem) ||
    189 	    (requested_ppem > best_ppem && ppem > best_ppem))
    190 	{
    191 	  best_i = i;
    192 	  best_ppem = ppem;
    193 	}
    194       }
    195 
    196       return table->get_strike (best_i);
    197     }
    198 
    199     struct PNGHeader
    200     {
    201       HBUINT8	signature[8];
    202       struct
    203       {
    204         struct
    205 	{
    206 	  HBUINT32	length;
    207 	  Tag		type;
    208 	}		header;
    209 	HBUINT32	width;
    210 	HBUINT32	height;
    211 	HBUINT8		bitDepth;
    212 	HBUINT8		colorType;
    213 	HBUINT8		compressionMethod;
    214 	HBUINT8		filterMethod;
    215 	HBUINT8		interlaceMethod;
    216       } IHDR;
    217 
    218       public:
    219       DEFINE_SIZE_STATIC (29);
    220     };
    221 
    222     bool get_png_extents (hb_font_t          *font,
    223 			  hb_codepoint_t      glyph,
    224 			  hb_glyph_extents_t *extents) const
    225     {
    226       /* Following code is safe to call even without data.
    227        * But faster to short-circuit. */
    228       if (!has_data ())
    229         return false;
    230 
    231       int x_offset = 0, y_offset = 0;
    232       unsigned int strike_ppem = 0;
    233       hb_blob_t *blob = reference_png (font, glyph, &x_offset, &y_offset, &strike_ppem);
    234 
    235       const PNGHeader &png = *blob->as<PNGHeader>();
    236 
    237       extents->x_bearing = x_offset;
    238       extents->y_bearing = y_offset;
    239       extents->width     = png.IHDR.width;
    240       extents->height    = png.IHDR.height;
    241 
    242       /* Convert to font units. */
    243       if (strike_ppem)
    244       {
    245 	double scale = font->face->get_upem () / (double) strike_ppem;
    246 	extents->x_bearing = round (extents->x_bearing * scale);
    247 	extents->y_bearing = round (extents->y_bearing * scale);
    248 	extents->width = round (extents->width * scale);
    249 	extents->height = round (extents->height * scale);
    250       }
    251 
    252       hb_blob_destroy (blob);
    253 
    254       return strike_ppem;
    255     }
    256 
    257     private:
    258     hb_blob_ptr_t<sbix> table;
    259 
    260     unsigned int num_glyphs;
    261   };
    262 
    263   bool sanitize (hb_sanitize_context_t *c) const
    264   {
    265     TRACE_SANITIZE (this);
    266     return_trace (likely (c->check_struct (this) &&
    267 			  version >= 1 &&
    268 			  strikes.sanitize (c, this)));
    269   }
    270 
    271   protected:
    272   HBUINT16	version;	/* Table version number  set to 1 */
    273   HBUINT16	flags;		/* Bit 0: Set to 1. Bit 1: Draw outlines.
    274 				 * Bits 2 to 15: reserved (set to 0). */
    275   LOffsetLArrayOf<SBIXStrike>
    276 		strikes;	/* Offsets from the beginning of the 'sbix'
    277 				 * table to data for each individual bitmap strike. */
    278   public:
    279   DEFINE_SIZE_ARRAY (8, strikes);
    280 };
    281 
    282 struct sbix_accelerator_t : sbix::accelerator_t {};
    283 
    284 } /* namespace OT */
    285 
    286 #endif /* HB_OT_COLOR_SBIX_TABLE_HH */
    287