hb-ot-hmtx-table.hh 10.7 KB
Newer Older
B
Behdad Esfahbod 已提交
1
/*
2
 * Copyright © 2011,2012  Google, Inc.
B
Behdad Esfahbod 已提交
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 *
 *  This is part of HarfBuzz, a text shaping library.
 *
 * Permission is hereby granted, without written agreement and without
 * license or royalty fees, to use, copy, modify, and distribute this
 * software and its documentation for any purpose, provided that the
 * above copyright notice and the following two paragraphs appear in
 * all copies of this software.
 *
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 *
 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 *
24
 * Google Author(s): Behdad Esfahbod, Roderick Sheeter
B
Behdad Esfahbod 已提交
25 26 27 28 29
 */

#ifndef HB_OT_HMTX_TABLE_HH
#define HB_OT_HMTX_TABLE_HH

30
#include "hb-open-type.hh"
31
#include "hb-ot-hhea-table.hh"
32 33
#include "hb-ot-os2-table.hh"
#include "hb-ot-var-hvar-table.hh"
B
Behdad Esfahbod 已提交
34 35

/*
36 37 38 39
 * hmtx -- Horizontal Metrics
 * https://docs.microsoft.com/en-us/typography/opentype/spec/hmtx
 * vmtx -- Vertical Metrics
 * https://docs.microsoft.com/en-us/typography/opentype/spec/vmtx
B
Behdad Esfahbod 已提交
40 41
 */
#define HB_OT_TAG_hmtx HB_TAG('h','m','t','x')
42
#define HB_OT_TAG_vmtx HB_TAG('v','m','t','x')
B
Behdad Esfahbod 已提交
43 44


45 46 47
namespace OT {


48
struct LongMetric
B
Behdad Esfahbod 已提交
49
{
50
  UFWORD	advance; /* Advance width/height. */
51
  FWORD		sb; /* Leading (left/top) side bearing. */
B
Behdad Esfahbod 已提交
52 53 54 55
  public:
  DEFINE_SIZE_STATIC (4);
};

R
Rod Sheeter 已提交
56
template <typename T, typename H>
B
Minor  
Behdad Esfahbod 已提交
57
struct hmtxvmtx
B
Behdad Esfahbod 已提交
58
{
59
  inline bool sanitize (hb_sanitize_context_t *c HB_UNUSED) const
B
Behdad Esfahbod 已提交
60
  {
B
Behdad Esfahbod 已提交
61
    TRACE_SANITIZE (this);
B
Behdad Esfahbod 已提交
62 63
    /* We don't check for anything specific here.  The users of the
     * struct do all the hard work... */
B
Behdad Esfahbod 已提交
64
    return_trace (true);
B
Behdad Esfahbod 已提交
65 66
  }

R
Rod Sheeter 已提交
67

68 69
  inline bool subset_update_header (hb_subset_plan_t *plan,
                                    unsigned int num_hmetrics) const
R
Rod Sheeter 已提交
70
  {
71
    hb_blob_t *src_blob = hb_sanitize_context_t().reference_table<H> (plan->source, H::tableTag);
72
    hb_blob_t *dest_blob = hb_blob_copy_writable_or_fail(src_blob);
73
    hb_blob_destroy (src_blob);
R
Rod Sheeter 已提交
74

R
Rod Sheeter 已提交
75
    if (unlikely (!dest_blob)) {
76 77
      return false;
    }
R
Rod Sheeter 已提交
78

79
    unsigned int length;
80
    H *table = (H *) hb_blob_get_data (dest_blob, &length);
81
    table->numberOfLongMetrics.set (num_hmetrics);
R
Rod Sheeter 已提交
82

83
    bool result = plan->add_table (H::tableTag, dest_blob);
84 85 86
    hb_blob_destroy (dest_blob);

    return result;
R
Rod Sheeter 已提交
87 88 89 90 91
  }

  inline bool subset (hb_subset_plan_t *plan) const
  {
    typename T::accelerator_t _mtx;
92
    _mtx.init (plan->source);
R
Rod Sheeter 已提交
93 94 95

    /* All the trailing glyphs with the same advance can use one LongMetric
     * and just keep LSB */
96
    hb_vector_t<hb_codepoint_t> &gids = plan->glyphs;
R
Rod Sheeter 已提交
97
    unsigned int num_advances = gids.len;
98
    unsigned int last_advance = _mtx.get_advance (gids[num_advances - 1]);
R
Rod Sheeter 已提交
99
    while (num_advances > 1
100
        && last_advance == _mtx.get_advance (gids[num_advances - 2]))
R
Rod Sheeter 已提交
101 102 103 104 105 106 107
    {
      num_advances--;
    }

    /* alloc the new table */
    size_t dest_sz = num_advances * 4
                  + (gids.len - num_advances) * 2;
108
    void *dest = (void *) malloc (dest_sz);
R
Rod Sheeter 已提交
109
    if (unlikely (!dest))
R
Rod Sheeter 已提交
110 111 112 113
    {
      return false;
    }
    DEBUG_MSG(SUBSET, nullptr, "%c%c%c%c in src has %d advances, %d lsbs", HB_UNTAG(T::tableTag), _mtx.num_advances, _mtx.num_metrics - _mtx.num_advances);
114
    DEBUG_MSG(SUBSET, nullptr, "%c%c%c%c in dest has %d advances, %d lsbs, %u bytes", HB_UNTAG(T::tableTag), num_advances, gids.len - num_advances, (unsigned int) dest_sz);
R
Rod Sheeter 已提交
115

B
Behdad Esfahbod 已提交
116
    const char *source_table = hb_blob_get_data (_mtx.table.get_blob (), nullptr);
R
Rod Sheeter 已提交
117 118 119 120
    // Copy everything over
    LongMetric * old_metrics = (LongMetric *) source_table;
    FWORD *lsbs = (FWORD *) (old_metrics + _mtx.num_advances);
    char * dest_pos = (char *) dest;
121 122

    bool failed = false;
R
Rod Sheeter 已提交
123 124 125
    for (unsigned int i = 0; i < gids.len; i++)
    {
      /* the last metric or the one for gids[i] */
126
      LongMetric *src_metric = old_metrics + MIN ((hb_codepoint_t) _mtx.num_advances - 1, gids[i]);
R
Rod Sheeter 已提交
127 128 129 130 131 132 133 134 135 136
      if (gids[i] < _mtx.num_advances)
      {
        /* src is a LongMetric */
        if (i < num_advances)
        {
          /* dest is a LongMetric, copy it */
          *((LongMetric *) dest_pos) = *src_metric;
        }
        else
        {
137 138
          /* dest just sb */
          *((FWORD *) dest_pos) = src_metric->sb;
R
Rod Sheeter 已提交
139 140 141 142
        }
      }
      else
      {
143 144 145 146 147 148 149
	if (gids[i] >= _mtx.num_metrics)
	{
	  DEBUG_MSG(SUBSET, nullptr, "gid %d is >= number of source metrics %d",
		    gids[i], _mtx.num_metrics);
	  failed = true;
	  break;
	}
150
	FWORD src_sb = *(lsbs + gids[i] - _mtx.num_advances);
R
Rod Sheeter 已提交
151 152 153 154 155
        if (i < num_advances)
        {
          /* dest needs a full LongMetric */
          LongMetric *metric = (LongMetric *)dest_pos;
          metric->advance = src_metric->advance;
156
          metric->sb = src_sb;
R
Rod Sheeter 已提交
157 158 159
        }
        else
        {
160 161
          /* dest just needs an sb */
          *((FWORD *) dest_pos) = src_sb;
R
Rod Sheeter 已提交
162 163 164 165
        }
      }
      dest_pos += (i < num_advances ? 4 : 2);
    }
166
    _mtx.fini ();
R
Rod Sheeter 已提交
167 168

    // Amend header num hmetrics
169
    if (failed || unlikely (!subset_update_header (plan, num_advances)))
R
Rod Sheeter 已提交
170
    {
171
      free (dest);
R
Rod Sheeter 已提交
172 173 174 175 176 177
      return false;
    }

    hb_blob_t *result = hb_blob_create ((const char *)dest,
                                        dest_sz,
                                        HB_MEMORY_MODE_READONLY,
178
                                        dest,
R
Rod Sheeter 已提交
179
                                        free);
180
    bool success = plan->add_table (T::tableTag, result);
181 182
    hb_blob_destroy (result);
    return success;
R
Rod Sheeter 已提交
183 184
  }

185 186
  struct accelerator_t
  {
R
Rod Sheeter 已提交
187 188
    friend struct hmtxvmtx;

189
    inline void init (hb_face_t *face,
B
Shuffle  
Behdad Esfahbod 已提交
190
		      unsigned int default_advance_ = 0)
191
    {
192
      default_advance = default_advance_ ? default_advance_ : hb_face_get_upem (face);
193 194

      bool got_font_extents = false;
195
      if (T::os2Tag)
196
      {
197 198
	hb_blob_t *os2_blob = hb_sanitize_context_t().reference_table<OS2> (face);
	const OS2 *os2_table = os2_blob->as<OS2> ();
199 200 201
#define USE_TYPO_METRICS (1u<<7)
	if (0 != (os2_table->fsSelection & USE_TYPO_METRICS))
	{
202 203
	  ascender = abs (os2_table->sTypoAscender);
	  descender = -abs (os2_table->sTypoDescender);
B
Shuffle  
Behdad Esfahbod 已提交
204 205
	  line_gap = os2_table->sTypoLineGap;
	  got_font_extents = (ascender | descender) != 0;
206 207 208 209
	}
	hb_blob_destroy (os2_blob);
      }

B
Behdad Esfahbod 已提交
210
      hb_blob_t *_hea_blob = hb_sanitize_context_t().reference_table<H> (face);
211
      const H *_hea_table = _hea_blob->as<H> ();
B
Shuffle  
Behdad Esfahbod 已提交
212
      num_advances = _hea_table->numberOfLongMetrics;
213 214
      if (!got_font_extents)
      {
215 216
	ascender = abs (_hea_table->ascender);
	descender = -abs (_hea_table->descender);
B
Shuffle  
Behdad Esfahbod 已提交
217 218
	line_gap = _hea_table->lineGap;
	got_font_extents = (ascender | descender) != 0;
219 220 221
      }
      hb_blob_destroy (_hea_blob);

B
Shuffle  
Behdad Esfahbod 已提交
222
      has_font_extents = got_font_extents;
223

B
Behdad Esfahbod 已提交
224
      table = hb_sanitize_context_t().reference_table<hmtxvmtx> (face, T::tableTag);
225 226

      /* Cap num_metrics() and num_advances() based on table length. */
B
Behdad Esfahbod 已提交
227
      unsigned int len = table.get_length ();
B
Shuffle  
Behdad Esfahbod 已提交
228 229 230
      if (unlikely (num_advances * 4 > len))
	num_advances = len / 4;
      num_metrics = num_advances + (len - 4 * num_advances) / 2;
231 232 233

      /* We MUST set num_metrics to zero if num_advances is zero.
       * Our get_advance() depends on that. */
B
Shuffle  
Behdad Esfahbod 已提交
234
      if (unlikely (!num_advances))
235
      {
B
Shuffle  
Behdad Esfahbod 已提交
236
	num_metrics = num_advances = 0;
B
Behdad Esfahbod 已提交
237
	table.destroy ();
B
Behdad Esfahbod 已提交
238
	table = hb_blob_get_empty ();
239 240
      }

B
Behdad Esfahbod 已提交
241
      var_table = hb_sanitize_context_t().reference_table<HVARVVAR> (face, T::variationsTag);
242 243 244 245
    }

    inline void fini (void)
    {
B
Behdad Esfahbod 已提交
246 247
      table.destroy ();
      var_table.destroy ();
248 249
    }

250 251 252 253 254 255
    /* TODO Add variations version. */
    inline unsigned int get_side_bearing (hb_codepoint_t glyph) const
    {
      if (glyph < num_advances)
        return table->longMetricZ[glyph].sb;

B
Behdad Esfahbod 已提交
256
      if (unlikely (glyph >= num_metrics))
257 258 259 260 261 262
        return 0;

      const FWORD *bearings = (const FWORD *) &table->longMetricZ[num_advances];
      return bearings[glyph - num_advances];
    }

B
Behdad Esfahbod 已提交
263
    inline unsigned int get_advance (hb_codepoint_t glyph) const
264
    {
B
Shuffle  
Behdad Esfahbod 已提交
265
      if (unlikely (glyph >= num_metrics))
266
      {
267 268 269 270 271 272 273
	/* If num_metrics is zero, it means we don't have the metrics table
	 * for this direction: return default advance.  Otherwise, it means that the
	 * glyph index is out of bound: return zero. */
	if (num_metrics)
	  return 0;
	else
	  return default_advance;
274 275
      }

276
      return table->longMetricZ[MIN (glyph, (uint32_t) num_advances - 1)].advance;
R
Rod Sheeter 已提交
277 278 279 280 281
    }

    inline unsigned int get_advance (hb_codepoint_t  glyph,
                                     hb_font_t      *font) const
    {
282 283 284
      unsigned int advance = get_advance (glyph);
      if (likely(glyph < num_metrics))
      {
285
	advance += (font->num_coords ? var_table->get_advance_var (glyph, font->coords, font->num_coords) : 0); // TODO Optimize?!
286
      }
G
Garret Rieger 已提交
287
      return advance;
288 289 290 291 292 293 294
    }

    public:
    bool has_font_extents;
    unsigned short ascender;
    unsigned short descender;
    unsigned short line_gap;
B
Shuffle  
Behdad Esfahbod 已提交
295

R
Rod Sheeter 已提交
296
    protected:
297 298 299
    unsigned int num_metrics;
    unsigned int num_advances;
    unsigned int default_advance;
B
Shuffle  
Behdad Esfahbod 已提交
300

R
Rod Sheeter 已提交
301
    private:
B
Behdad Esfahbod 已提交
302 303
    hb_blob_ptr_t<hmtxvmtx> table;
    hb_blob_ptr_t<HVARVVAR> var_table;
304 305 306
  };

  protected:
307
  UnsizedArrayOf<LongMetric>longMetricZ;/* Paired advance width and leading
B
Behdad Esfahbod 已提交
308 309 310 311 312 313 314
					 * bearing values for each glyph. The
					 * value numOfHMetrics comes from
					 * the 'hhea' table. If the font is
					 * monospaced, only one entry need
					 * be in the array, but that entry is
					 * required. The last entry applies to
					 * all subsequent glyphs. */
315
/*UnsizedArrayOf<FWORD>	leadingBearingX;*//* Here the advance is assumed
316
					 * to be the same as the advance
B
Behdad Esfahbod 已提交
317 318 319
					 * for the last entry above. The
					 * number of entries in this array is
					 * derived from numGlyphs (from 'maxp'
320 321 322
					 * table) minus numberOfLongMetrics.
					 * This generally is used with a run
					 * of monospaced glyphs (e.g., Kanji
B
Behdad Esfahbod 已提交
323 324 325
					 * fonts or Courier fonts). Only one
					 * run is allowed and it must be at
					 * the end. This allows a monospaced
326
					 * font to vary the side bearing
B
Behdad Esfahbod 已提交
327 328
					 * values for each glyph. */
  public:
329
  DEFINE_SIZE_ARRAY (0, longMetricZ);
B
Behdad Esfahbod 已提交
330 331
};

R
Rod Sheeter 已提交
332
struct hmtx : hmtxvmtx<hmtx, hhea> {
333
  static const hb_tag_t tableTag	= HB_OT_TAG_hmtx;
334
  static const hb_tag_t variationsTag	= HB_OT_TAG_HVAR;
335
  static const hb_tag_t os2Tag		= HB_OT_TAG_OS2;
336
};
R
Rod Sheeter 已提交
337
struct vmtx : hmtxvmtx<vmtx, vhea> {
338
  static const hb_tag_t tableTag	= HB_OT_TAG_vmtx;
339 340
  static const hb_tag_t variationsTag	= HB_OT_TAG_VVAR;
  static const hb_tag_t os2Tag		= HB_TAG_NONE;
341
};
342

343 344 345
struct hmtx_accelerator_t : hmtx::accelerator_t {};
struct vmtx_accelerator_t : vmtx::accelerator_t {};

B
Behdad Esfahbod 已提交
346
} /* namespace OT */
347 348


B
Behdad Esfahbod 已提交
349
#endif /* HB_OT_HMTX_TABLE_HH */