1 /*
   2  * Copyright © 2018  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): Garret Rieger
  25  */
  26 
  27 #ifndef HB_OT_HDMX_TABLE_HH
  28 #define HB_OT_HDMX_TABLE_HH
  29 
  30 #include "hb-open-type.hh"
  31 
  32 /*
  33  * hdmx -- Horizontal Device Metrics
  34  * https://docs.microsoft.com/en-us/typography/opentype/spec/hdmx
  35  */
  36 #define HB_OT_TAG_hdmx HB_TAG('h','d','m','x')
  37 
  38 
  39 namespace OT {
  40 
  41 
  42 struct DeviceRecord
  43 {
  44   struct SubsetView
  45   {
  46     const DeviceRecord *source_device_record;
  47     unsigned int sizeDeviceRecord;
  48     hb_subset_plan_t *subset_plan;
  49 
  50     void init (const DeviceRecord *source_device_record,
  51                unsigned int sizeDeviceRecord,
  52                hb_subset_plan_t   *subset_plan)
  53     {
  54       this->source_device_record = source_device_record;
  55       this->sizeDeviceRecord = sizeDeviceRecord;
  56       this->subset_plan = subset_plan;
  57     }
  58 
  59     unsigned int len () const
  60     { return this->subset_plan->glyphs.length; }
  61 
  62     const HBUINT8* operator [] (unsigned int i) const
  63     {
  64       if (unlikely (i >= len ())) return nullptr;
  65       hb_codepoint_t gid = this->subset_plan->glyphs [i];
  66 
  67       if (gid >= sizeDeviceRecord - DeviceRecord::min_size)
  68         return nullptr;
  69       return &(this->source_device_record->widthsZ[gid]);
  70     }
  71   };
  72 
  73   static unsigned int get_size (unsigned int count)
  74   { return hb_ceil_to_4 (min_size + count * HBUINT8::static_size); }
  75 
  76   bool serialize (hb_serialize_context_t *c, const SubsetView &subset_view)
  77   {
  78     TRACE_SERIALIZE (this);
  79 
  80     unsigned int size = get_size (subset_view.len ());
  81     if (unlikely (!c->allocate_size<DeviceRecord> (size)))
  82     {
  83       DEBUG_MSG(SUBSET, nullptr, "Couldn't allocate enough space for DeviceRecord: %d.",
  84                  size);
  85       return_trace (false);
  86     }
  87 
  88     this->pixelSize.set (subset_view.source_device_record->pixelSize);
  89     this->maxWidth.set (subset_view.source_device_record->maxWidth);
  90 
  91     for (unsigned int i = 0; i < subset_view.len (); i++)
  92     {
  93       const HBUINT8 *width = subset_view[i];
  94       if (!width)
  95       {
  96         DEBUG_MSG(SUBSET, nullptr, "HDMX width for new gid %d is missing.", i);
  97         return_trace (false);
  98       }
  99       widthsZ[i].set (*width);
 100     }
 101 
 102     return_trace (true);
 103   }
 104 
 105   bool sanitize (hb_sanitize_context_t *c, unsigned int sizeDeviceRecord) const
 106   {
 107     TRACE_SANITIZE (this);
 108     return_trace (likely (c->check_struct (this) &&
 109                           c->check_range (this, sizeDeviceRecord)));
 110   }
 111 
 112   HBUINT8                       pixelSize;      /* Pixel size for following widths (as ppem). */
 113   HBUINT8                       maxWidth;       /* Maximum width. */
 114   UnsizedArrayOf<HBUINT8>       widthsZ;        /* Array of widths (numGlyphs is from the 'maxp' table). */
 115   public:
 116   DEFINE_SIZE_ARRAY (2, widthsZ);
 117 };
 118 
 119 
 120 struct hdmx
 121 {
 122   static constexpr hb_tag_t tableTag = HB_OT_TAG_hdmx;
 123 
 124   unsigned int get_size () const
 125   { return min_size + numRecords * sizeDeviceRecord; }
 126 
 127   const DeviceRecord& operator [] (unsigned int i) const
 128   {
 129     /* XXX Null(DeviceRecord) is NOT safe as it's num-glyphs lengthed.
 130      * https://github.com/harfbuzz/harfbuzz/issues/1300 */
 131     if (unlikely (i >= numRecords)) return Null (DeviceRecord);
 132     return StructAtOffset<DeviceRecord> (&this->firstDeviceRecord, i * sizeDeviceRecord);
 133   }
 134 
 135   bool serialize (hb_serialize_context_t *c, const hdmx *source_hdmx, hb_subset_plan_t *plan)
 136   {
 137     TRACE_SERIALIZE (this);
 138 
 139     if (unlikely (!c->extend_min ((*this))))  return_trace (false);
 140 
 141     this->version.set (source_hdmx->version);
 142     this->numRecords.set (source_hdmx->numRecords);
 143     this->sizeDeviceRecord.set (DeviceRecord::get_size (plan->glyphs.length));
 144 
 145     for (unsigned int i = 0; i < source_hdmx->numRecords; i++)
 146     {
 147       DeviceRecord::SubsetView subset_view;
 148       subset_view.init (&(*source_hdmx)[i], source_hdmx->sizeDeviceRecord, plan);
 149 
 150       if (!c->start_embed<DeviceRecord> ()->serialize (c, subset_view))
 151         return_trace (false);
 152     }
 153 
 154     return_trace (true);
 155   }
 156 
 157   static size_t get_subsetted_size (const hdmx *source_hdmx, hb_subset_plan_t *plan)
 158   {
 159     return min_size + source_hdmx->numRecords * DeviceRecord::get_size (plan->glyphs.length);
 160   }
 161 
 162   bool subset (hb_subset_plan_t *plan) const
 163   {
 164     size_t dest_size = get_subsetted_size (this, plan);
 165     hdmx *dest = (hdmx *) malloc (dest_size);
 166     if (unlikely (!dest))
 167     {
 168       DEBUG_MSG(SUBSET, nullptr, "Unable to alloc %lu for hdmx subset output.", (unsigned long) dest_size);
 169       return false;
 170     }
 171 
 172     hb_serialize_context_t c (dest, dest_size);
 173     hdmx *hdmx_prime = c.start_serialize<hdmx> ();
 174     if (!hdmx_prime || !hdmx_prime->serialize (&c, this, plan))
 175     {
 176       free (dest);
 177       DEBUG_MSG(SUBSET, nullptr, "Failed to serialize write new hdmx.");
 178       return false;
 179     }
 180     c.end_serialize ();
 181 
 182     hb_blob_t *hdmx_prime_blob = hb_blob_create ((const char *) dest,
 183                                                  dest_size,
 184                                                  HB_MEMORY_MODE_READONLY,
 185                                                  dest,
 186                                                  free);
 187     bool result = plan->add_table (HB_OT_TAG_hdmx, hdmx_prime_blob);
 188     hb_blob_destroy (hdmx_prime_blob);
 189 
 190     return result;
 191   }
 192 
 193   bool sanitize (hb_sanitize_context_t *c) const
 194   {
 195     TRACE_SANITIZE (this);
 196     return_trace (c->check_struct (this) &&
 197                   !hb_unsigned_mul_overflows (numRecords, sizeDeviceRecord) &&
 198                   sizeDeviceRecord >= DeviceRecord::min_size &&
 199                   c->check_range (this, get_size ()));
 200   }
 201 
 202   protected:
 203   HBUINT16              version;                /* Table version number (0) */
 204   HBUINT16              numRecords;             /* Number of device records. */
 205   HBUINT32              sizeDeviceRecord;       /* Size of a device record, 32-bit aligned. */
 206   DeviceRecord          firstDeviceRecord;      /* Array of device records. */
 207   public:
 208   DEFINE_SIZE_MIN (8);
 209 };
 210 
 211 } /* namespace OT */
 212 
 213 
 214 #endif /* HB_OT_HDMX_TABLE_HH */