hb-ot-cmap-table.hh 22.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
/*
 * Copyright © 2014  Google, Inc.
 *
 *  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.
 *
 * Google Author(s): Behdad Esfahbod
 */

#ifndef HB_OT_CMAP_TABLE_HH
#define HB_OT_CMAP_TABLE_HH

B
Behdad Esfahbod 已提交
30
#include "hb-open-type-private.hh"
31
#include "hb-subset-plan.hh"
32 33 34 35 36 37 38 39 40 41 42

namespace OT {


/*
 * cmap -- Character To Glyph Index Mapping Table
 */

#define HB_OT_TAG_cmap HB_TAG('c','m','a','p')


43 44 45 46 47 48 49 50 51 52 53
struct CmapSubtableFormat0
{
  inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const
  {
    hb_codepoint_t gid = codepoint < 256 ? glyphIdArray[codepoint] : 0;
    if (!gid)
      return false;
    *glyph = gid;
    return true;
  }

B
Behdad Esfahbod 已提交
54 55
  inline bool sanitize (hb_sanitize_context_t *c) const
  {
56
    TRACE_SANITIZE (this);
B
Behdad Esfahbod 已提交
57
    return_trace (c->check_struct (this));
58 59 60
  }

  protected:
B
Behdad Esfahbod 已提交
61 62 63 64
  HBUINT16	format;		/* Format number is set to 0. */
  HBUINT16	lengthZ;	/* Byte length of this subtable. */
  HBUINT16	languageZ;	/* Ignore. */
  HBUINT8		glyphIdArray[256];/* An array that maps character
65 66 67 68 69
				 * code to glyph index values. */
  public:
  DEFINE_SIZE_STATIC (6 + 256);
};

70 71
struct CmapSubtableFormat4
{
72
  struct accelerator_t
73
  {
74 75 76 77 78 79 80 81 82 83
    inline void init (const CmapSubtableFormat4 *subtable)
    {
      segCount = subtable->segCountX2 / 2;
      endCount = subtable->values;
      startCount = endCount + segCount + 1;
      idDelta = startCount + segCount;
      idRangeOffset = idDelta + segCount;
      glyphIdArray = idRangeOffset + segCount;
      glyphIdArrayLength = (subtable->length - 16 - 8 * segCount) / 2;
    }
84

85
    static inline bool get_glyph_func (const void *obj, hb_codepoint_t codepoint, hb_codepoint_t *glyph)
86
    {
87 88 89 90
      const accelerator_t *thiz = (const accelerator_t *) obj;

      /* Custom two-array bsearch. */
      int min = 0, max = (int) thiz->segCount - 1;
B
Behdad Esfahbod 已提交
91 92
      const HBUINT16 *startCount = thiz->startCount;
      const HBUINT16 *endCount = thiz->endCount;
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
      unsigned int i;
      while (min <= max)
      {
	int mid = (min + max) / 2;
	if (codepoint < startCount[mid])
	  max = mid - 1;
	else if (codepoint > endCount[mid])
	  min = mid + 1;
	else
	{
	  i = mid;
	  goto found;
	}
      }
      return false;

    found:
      hb_codepoint_t gid;
      unsigned int rangeOffset = thiz->idRangeOffset[i];
      if (rangeOffset == 0)
	gid = codepoint + thiz->idDelta[i];
114 115
      else
      {
116 117 118 119 120 121 122 123
	/* Somebody has been smoking... */
	unsigned int index = rangeOffset / 2 + (codepoint - thiz->startCount[i]) + i - thiz->segCount;
	if (unlikely (index >= thiz->glyphIdArrayLength))
	  return false;
	gid = thiz->glyphIdArray[index];
	if (unlikely (!gid))
	  return false;
	gid += thiz->idDelta[i];
124
      }
125 126 127

      *glyph = gid & 0xFFFFu;
      return true;
128 129
    }

B
Behdad Esfahbod 已提交
130 131 132 133 134
    const HBUINT16 *endCount;
    const HBUINT16 *startCount;
    const HBUINT16 *idDelta;
    const HBUINT16 *idRangeOffset;
    const HBUINT16 *glyphIdArray;
135 136 137 138 139 140 141 142 143
    unsigned int segCount;
    unsigned int glyphIdArrayLength;
  };

  inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const
  {
    accelerator_t accel;
    accel.init (this);
    return accel.get_glyph_func (&accel, codepoint, glyph);
144 145
  }

B
Behdad Esfahbod 已提交
146
  inline bool sanitize (hb_sanitize_context_t *c) const
147
  {
148
    TRACE_SANITIZE (this);
149
    if (unlikely (!c->check_struct (this)))
B
Behdad Esfahbod 已提交
150
      return_trace (false);
151 152 153 154 155 156 157 158 159 160

    if (unlikely (!c->check_range (this, length)))
    {
      /* Some broken fonts have too long of a "length" value.
       * If that is the case, just change the value to truncate
       * the subtable at the end of the blob. */
      uint16_t new_length = (uint16_t) MIN ((uintptr_t) 65535,
					    (uintptr_t) (c->end -
							 (char *) this));
      if (!c->try_set (&length, new_length))
B
Behdad Esfahbod 已提交
161
	return_trace (false);
162 163
    }

B
Behdad Esfahbod 已提交
164
    return_trace (16 + 4 * (unsigned int) segCountX2 <= length);
165 166 167
  }

  protected:
B
Behdad Esfahbod 已提交
168 169
  HBUINT16	format;		/* Format number is set to 4. */
  HBUINT16	length;		/* This is the length in bytes of the
170
				 * subtable. */
B
Behdad Esfahbod 已提交
171 172 173 174 175
  HBUINT16	languageZ;	/* Ignore. */
  HBUINT16	segCountX2;	/* 2 x segCount. */
  HBUINT16	searchRangeZ;	/* 2 * (2**floor(log2(segCount))) */
  HBUINT16	entrySelectorZ;	/* log2(searchRange/2) */
  HBUINT16	rangeShiftZ;	/* 2 x segCount - searchRange */
176

B
Behdad Esfahbod 已提交
177
  HBUINT16	values[VAR];
178
#if 0
B
Behdad Esfahbod 已提交
179
  HBUINT16	endCount[segCount];	/* End characterCode for each segment,
180
					 * last=0xFFFFu. */
B
Behdad Esfahbod 已提交
181 182 183 184 185
  HBUINT16	reservedPad;		/* Set to 0. */
  HBUINT16	startCount[segCount];	/* Start character code for each segment. */
  HBINT16		idDelta[segCount];	/* Delta for all character codes in segment. */
  HBUINT16	idRangeOffset[segCount];/* Offsets into glyphIdArray or 0 */
  HBUINT16	glyphIdArray[VAR];	/* Glyph index array (arbitrary length) */
186 187 188 189 190 191
#endif

  public:
  DEFINE_SIZE_ARRAY (14, values);
};

192
struct CmapSubtableLongGroup
193 194
{
  friend struct CmapSubtableFormat12;
195
  friend struct CmapSubtableFormat13;
196
  friend struct cmap;
197 198 199 200 201 202 203 204

  int cmp (hb_codepoint_t codepoint) const
  {
    if (codepoint < startCharCode) return -1;
    if (codepoint > endCharCode)   return +1;
    return 0;
  }

B
Behdad Esfahbod 已提交
205 206
  inline bool sanitize (hb_sanitize_context_t *c) const
  {
207
    TRACE_SANITIZE (this);
B
Behdad Esfahbod 已提交
208
    return_trace (c->check_struct (this));
209 210 211
  }

  private:
B
Behdad Esfahbod 已提交
212 213 214
  HBUINT32		startCharCode;	/* First character code in this group. */
  HBUINT32		endCharCode;	/* Last character code in this group. */
  HBUINT32		glyphID;	/* Glyph index; interpretation depends on
215
				 * subtable format. */
216 217 218 219
  public:
  DEFINE_SIZE_STATIC (12);
};

220 221
template <typename UINT>
struct CmapSubtableTrimmed
222 223 224 225 226 227 228 229 230 231 232
{
  inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const
  {
    /* Rely on our implicit array bound-checking. */
    hb_codepoint_t gid = glyphIdArray[codepoint - startCharCode];
    if (!gid)
      return false;
    *glyph = gid;
    return true;
  }

B
Behdad Esfahbod 已提交
233 234
  inline bool sanitize (hb_sanitize_context_t *c) const
  {
235
    TRACE_SANITIZE (this);
B
Behdad Esfahbod 已提交
236
    return_trace (c->check_struct (this) && glyphIdArray.sanitize (c));
237 238 239
  }

  protected:
240
  UINT		formatReserved;	/* Subtable format and (maybe) padding. */
241 242
  UINT		lengthZ;	/* Byte length of this subtable. */
  UINT		languageZ;	/* Ignore. */
243
  UINT		startCharCode;	/* First character code covered. */
244
  ArrayOf<GlyphID, UINT>
245 246 247
		glyphIdArray;	/* Array of glyph index values for character
				 * codes in the range. */
  public:
248
  DEFINE_SIZE_ARRAY (5 * sizeof (UINT), glyphIdArray);
249 250
};

B
Behdad Esfahbod 已提交
251 252
struct CmapSubtableFormat6  : CmapSubtableTrimmed<HBUINT16> {};
struct CmapSubtableFormat10 : CmapSubtableTrimmed<HBUINT32 > {};
253

254 255
template <typename T>
struct CmapSubtableLongSegmented
256
{
R
Rod Sheeter 已提交
257 258
  friend struct cmap;

259 260
  inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const
  {
261
    int i = groups.bsearch (codepoint);
262 263
    if (i == -1)
      return false;
264
    *glyph = T::group_get_glyph (groups[i], codepoint);
265 266 267
    return true;
  }

B
Behdad Esfahbod 已提交
268 269
  inline bool sanitize (hb_sanitize_context_t *c) const
  {
270
    TRACE_SANITIZE (this);
B
Behdad Esfahbod 已提交
271
    return_trace (c->check_struct (this) && groups.sanitize (c));
272 273
  }

R
Rod Sheeter 已提交
274 275 276 277 278 279 280 281 282 283 284 285 286 287
  inline bool serialize(hb_serialize_context_t *context,
                        unsigned int group_count,
                        Supplier<CmapSubtableLongGroup> &group_supplier)
  {
    TRACE_SERIALIZE (this);
    if (unlikely(!context->extend_min (*this))) return_trace (false);
    if (unlikely(!groups.serialize(context, group_count))) return_trace (false);
    for (unsigned int i = 0; i < group_count; i++) {
      const CmapSubtableLongGroup &group = group_supplier[i];
      memcpy(&groups[i], &group, sizeof(group));
    }
    return true;
  }

288
  protected:
B
Behdad Esfahbod 已提交
289 290 291 292 293
  HBUINT16	format;		/* Subtable format; set to 12. */
  HBUINT16	reservedZ;	/* Reserved; set to 0. */
  HBUINT32		lengthZ;	/* Byte length of this subtable. */
  HBUINT32		languageZ;	/* Ignore. */
  SortedArrayOf<CmapSubtableLongGroup, HBUINT32>
294 295 296 297 298
		groups;		/* Groupings. */
  public:
  DEFINE_SIZE_ARRAY (16, groups);
};

299
struct CmapSubtableFormat12 : CmapSubtableLongSegmented<CmapSubtableFormat12>
300
{
301 302 303 304
  static inline hb_codepoint_t group_get_glyph (const CmapSubtableLongGroup &group,
						hb_codepoint_t u)
  { return group.glyphID + (u - group.startCharCode); }
};
305

306 307 308 309 310
struct CmapSubtableFormat13 : CmapSubtableLongSegmented<CmapSubtableFormat13>
{
  static inline hb_codepoint_t group_get_glyph (const CmapSubtableLongGroup &group,
						hb_codepoint_t u HB_UNUSED)
  { return group.glyphID; }
311 312
};

313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
typedef enum
{
  GLYPH_VARIANT_NOT_FOUND = 0,
  GLYPH_VARIANT_FOUND = 1,
  GLYPH_VARIANT_USE_DEFAULT = 2
} glyph_variant_t;

struct UnicodeValueRange
{
  inline int cmp (const hb_codepoint_t &codepoint) const
  {
    if (codepoint < startUnicodeValue) return -1;
    if (codepoint > startUnicodeValue + additionalCount) return +1;
    return 0;
  }

B
Behdad Esfahbod 已提交
329 330
  inline bool sanitize (hb_sanitize_context_t *c) const
  {
331
    TRACE_SANITIZE (this);
B
Behdad Esfahbod 已提交
332
    return_trace (c->check_struct (this));
333 334 335
  }

  UINT24	startUnicodeValue;	/* First value in this range. */
B
Behdad Esfahbod 已提交
336
  HBUINT8		additionalCount;	/* Number of additional values in this
337 338 339 340 341
					 * range. */
  public:
  DEFINE_SIZE_STATIC (4);
};

B
Behdad Esfahbod 已提交
342
typedef SortedArrayOf<UnicodeValueRange, HBUINT32> DefaultUVS;
343 344 345 346 347 348 349 350

struct UVSMapping
{
  inline int cmp (const hb_codepoint_t &codepoint) const
  {
    return unicodeValue.cmp (codepoint);
  }

B
Behdad Esfahbod 已提交
351 352
  inline bool sanitize (hb_sanitize_context_t *c) const
  {
353
    TRACE_SANITIZE (this);
B
Behdad Esfahbod 已提交
354
    return_trace (c->check_struct (this));
355 356 357 358 359 360 361 362
  }

  UINT24	unicodeValue;	/* Base Unicode value of the UVS */
  GlyphID	glyphID;	/* Glyph ID of the UVS */
  public:
  DEFINE_SIZE_STATIC (5);
};

B
Behdad Esfahbod 已提交
363
typedef SortedArrayOf<UVSMapping, HBUINT32> NonDefaultUVS;
364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390

struct VariationSelectorRecord
{
  inline glyph_variant_t get_glyph (hb_codepoint_t codepoint,
				    hb_codepoint_t *glyph,
				    const void *base) const
  {
    int i;
    const DefaultUVS &defaults = base+defaultUVS;
    i = defaults.bsearch (codepoint);
    if (i != -1)
      return GLYPH_VARIANT_USE_DEFAULT;
    const NonDefaultUVS &nonDefaults = base+nonDefaultUVS;
    i = nonDefaults.bsearch (codepoint);
    if (i != -1)
    {
      *glyph = nonDefaults[i].glyphID;
       return GLYPH_VARIANT_FOUND;
    }
    return GLYPH_VARIANT_NOT_FOUND;
  }

  inline int cmp (const hb_codepoint_t &variation_selector) const
  {
    return varSelector.cmp (variation_selector);
  }

B
Behdad Esfahbod 已提交
391 392
  inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
  {
393
    TRACE_SANITIZE (this);
B
Behdad Esfahbod 已提交
394 395 396
    return_trace (c->check_struct (this) &&
		  defaultUVS.sanitize (c, base) &&
		  nonDefaultUVS.sanitize (c, base));
397 398 399
  }

  UINT24	varSelector;	/* Variation selector. */
B
Behdad Esfahbod 已提交
400
  LOffsetTo<DefaultUVS>
401
		defaultUVS;	/* Offset to Default UVS Table. May be 0. */
B
Behdad Esfahbod 已提交
402
  LOffsetTo<NonDefaultUVS>
403 404 405 406 407 408 409 410 411 412 413 414 415 416
		nonDefaultUVS;	/* Offset to Non-Default UVS Table. May be 0. */
  public:
  DEFINE_SIZE_STATIC (11);
};

struct CmapSubtableFormat14
{
  inline glyph_variant_t get_glyph_variant (hb_codepoint_t codepoint,
					    hb_codepoint_t variation_selector,
					    hb_codepoint_t *glyph) const
  {
    return record[record.bsearch(variation_selector)].get_glyph (codepoint, glyph, this);
  }

B
Behdad Esfahbod 已提交
417 418
  inline bool sanitize (hb_sanitize_context_t *c) const
  {
419
    TRACE_SANITIZE (this);
B
Behdad Esfahbod 已提交
420 421
    return_trace (c->check_struct (this) &&
		  record.sanitize (c, this));
422 423 424
  }

  protected:
B
Behdad Esfahbod 已提交
425 426 427
  HBUINT16	format;		/* Format number is set to 14. */
  HBUINT32		lengthZ;	/* Byte length of this subtable. */
  SortedArrayOf<VariationSelectorRecord, HBUINT32>
428 429 430 431 432 433
		record;		/* Variation selector records; sorted
				 * in increasing order of `varSelector'. */
  public:
  DEFINE_SIZE_ARRAY (10, record);
};

434 435
struct CmapSubtable
{
436 437
  /* Note: We intentionally do NOT implement subtable formats 2 and 8. */

438 439
  inline bool get_glyph (hb_codepoint_t codepoint,
			 hb_codepoint_t *glyph) const
440 441
  {
    switch (u.format) {
442
    case  0: return u.format0 .get_glyph(codepoint, glyph);
443
    case  4: return u.format4 .get_glyph(codepoint, glyph);
444 445
    case  6: return u.format6 .get_glyph(codepoint, glyph);
    case 10: return u.format10.get_glyph(codepoint, glyph);
446
    case 12: return u.format12.get_glyph(codepoint, glyph);
447
    case 13: return u.format13.get_glyph(codepoint, glyph);
448 449 450 451 452
    case 14:
    default: return false;
    }
  }

B
Behdad Esfahbod 已提交
453 454
  inline bool sanitize (hb_sanitize_context_t *c) const
  {
455
    TRACE_SANITIZE (this);
B
Behdad Esfahbod 已提交
456
    if (!u.format.sanitize (c)) return_trace (false);
457
    switch (u.format) {
B
Behdad Esfahbod 已提交
458 459 460 461 462 463 464 465
    case  0: return_trace (u.format0 .sanitize (c));
    case  4: return_trace (u.format4 .sanitize (c));
    case  6: return_trace (u.format6 .sanitize (c));
    case 10: return_trace (u.format10.sanitize (c));
    case 12: return_trace (u.format12.sanitize (c));
    case 13: return_trace (u.format13.sanitize (c));
    case 14: return_trace (u.format14.sanitize (c));
    default:return_trace (true);
466 467 468
    }
  }

469
  public:
470
  union {
B
Behdad Esfahbod 已提交
471
  HBUINT16		format;		/* Format identifier */
472
  CmapSubtableFormat0	format0;
473
  CmapSubtableFormat4	format4;
474 475
  CmapSubtableFormat6	format6;
  CmapSubtableFormat10	format10;
476
  CmapSubtableFormat12	format12;
477
  CmapSubtableFormat13	format13;
478
  CmapSubtableFormat14	format14;
479 480 481 482 483 484 485 486
  } u;
  public:
  DEFINE_SIZE_UNION (2, format);
};


struct EncodingRecord
{
487
  inline int cmp (const EncodingRecord &other) const
488 489
  {
    int ret;
490
    ret = platformID.cmp (other.platformID);
491
    if (ret) return ret;
492
    ret = encodingID.cmp (other.encodingID);
493 494 495 496
    if (ret) return ret;
    return 0;
  }

B
Behdad Esfahbod 已提交
497 498
  inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
  {
499
    TRACE_SANITIZE (this);
B
Behdad Esfahbod 已提交
500 501
    return_trace (c->check_struct (this) &&
		  subtable.sanitize (c, base));
502 503
  }

B
Behdad Esfahbod 已提交
504 505
  HBUINT16	platformID;	/* Platform ID. */
  HBUINT16	encodingID;	/* Platform-specific encoding ID. */
B
Behdad Esfahbod 已提交
506
  LOffsetTo<CmapSubtable>
507 508 509 510 511 512 513 514 515
		subtable;	/* Byte offset from beginning of table to the subtable for this encoding. */
  public:
  DEFINE_SIZE_STATIC (8);
};

struct cmap
{
  static const hb_tag_t tableTag	= HB_OT_TAG_cmap;

516 517 518 519 520 521 522 523
  inline bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this) &&
		  likely (version == 0) &&
		  encodingRecord.sanitize (c, this));
  }

R
Rod Sheeter 已提交
524 525
  inline void populate_groups(hb_auto_array_t<hb_codepoint_t> &codepoints,
                              hb_auto_array_t<CmapSubtableLongGroup> *groups) const
526
  {
527
    CmapSubtableLongGroup *group = nullptr;
R
Rod Sheeter 已提交
528 529
    for (unsigned int i = 0; i < codepoints.len; i++) {
      hb_codepoint_t cp = codepoints[i];
530 531
      if (!group)
      {
R
Rod Sheeter 已提交
532
        group = groups->push();
533 534 535 536 537 538 539 540 541 542 543 544 545
        group->startCharCode.set(cp);
        group->endCharCode.set(cp);
        group->glyphID.set(i);  // index in codepoints is new gid
      } else if (cp -1 == group->endCharCode)
      {
        group->endCharCode.set(cp);
      } else
      {
        group = nullptr;
      }
    }

    DEBUG_MSG(SUBSET, nullptr, "cmap");
R
Rod Sheeter 已提交
546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585
    for (unsigned int i = 0; i < groups->len; i++) {
      CmapSubtableLongGroup& group = (*groups)[i];
      DEBUG_MSG(SUBSET, nullptr, "  %d: U+%04X-U+%04X, gid %d-%d", i, (uint32_t) group.startCharCode, (uint32_t) group.endCharCode, (uint32_t) group.glyphID, (uint32_t) group.glyphID + ((uint32_t) group.endCharCode - (uint32_t) group.startCharCode));
    }
  }

  hb_bool_t _subset (hb_auto_array_t<CmapSubtableLongGroup> &groups,
                     size_t dest_sz,
                     void *dest) const
  {
    hb_serialize_context_t context(dest, dest_sz);

    OT::cmap *cmap = context.start_serialize<OT::cmap> ();
    if (unlikely(!context.extend_min(*cmap)))
    {
      return false;
    }

    cmap->version.set(0);

    if (unlikely(!cmap->encodingRecord.serialize(&context, /* numTables */ 1)))
    {
      return false;
    }

    EncodingRecord &rec = cmap->encodingRecord[0];
    rec.platformID.set (3); // Windows
    rec.encodingID.set (1); // Unicode BMP

    CmapSubtable &subtable = rec.subtable.serialize(&context, &rec.subtable);
    subtable.u.format.set(12);

    CmapSubtableFormat12 &format12 = subtable.u.format12;
    format12.format.set(12);
    format12.reservedZ.set(0);

    OT::Supplier<CmapSubtableLongGroup> group_supplier  (&groups[0], groups.len, sizeof (CmapSubtableLongGroup));
    if (unlikely(!format12.serialize(&context, groups.len, group_supplier)))
    {
      return false;
586
    }
R
Rod Sheeter 已提交
587 588

    context.end_serialize ();
589 590 591
    return true;
  }

R
Rod Sheeter 已提交
592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623
  hb_blob_t * subset (hb_subset_plan_t *plan, hb_face_t *source) const
  {
    hb_auto_array_t<CmapSubtableLongGroup> groups;

    populate_groups(plan->codepoints, &groups);

    // We now know how big our blob needs to be
    // TODO use APIs from the structs to get size?
    size_t dest_sz = 4 // header
                   + 8 // 1 EncodingRecord
                   + 16 // Format 12 header
                   + 12 * groups.len; // SequentialMapGroup records
    void *dest = calloc(dest_sz, 1);
    if (unlikely(!dest)) {
      DEBUG_MSG(SUBSET, nullptr, "Unable to alloc %ld for cmap subset output", dest_sz);
      return nullptr;
    }

    if (unlikely(!_subset(groups, dest_sz, dest)))
    {
      free(dest);
      return nullptr;
    }

    // all done, write the blob into dest
    return hb_blob_create((const char *)dest, 
                          dest_sz,
                          HB_MEMORY_MODE_READONLY,
                          /* userdata */ nullptr,
                          free);
  }

624 625 626 627
  struct accelerator_t
  {
    inline void init (hb_face_t *face)
    {
B
Behdad Esfahbod 已提交
628
      this->blob = OT::Sanitizer<OT::cmap>().sanitize (face->reference_table (HB_OT_TAG_cmap));
629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755
      const OT::cmap *cmap = OT::Sanitizer<OT::cmap>::lock_instance (this->blob);
      const OT::CmapSubtable *subtable = nullptr;
      const OT::CmapSubtableFormat14 *subtable_uvs = nullptr;

      bool symbol = false;
      /* 32-bit subtables. */
      if (!subtable) subtable = cmap->find_subtable (3, 10);
      if (!subtable) subtable = cmap->find_subtable (0, 6);
      if (!subtable) subtable = cmap->find_subtable (0, 4);
      /* 16-bit subtables. */
      if (!subtable) subtable = cmap->find_subtable (3, 1);
      if (!subtable) subtable = cmap->find_subtable (0, 3);
      if (!subtable) subtable = cmap->find_subtable (0, 2);
      if (!subtable) subtable = cmap->find_subtable (0, 1);
      if (!subtable) subtable = cmap->find_subtable (0, 0);
      if (!subtable)
      {
	subtable = cmap->find_subtable (3, 0);
	if (subtable) symbol = true;
      }
      /* Meh. */
      if (!subtable) subtable = &OT::Null(OT::CmapSubtable);

      /* UVS subtable. */
      if (!subtable_uvs)
      {
	const OT::CmapSubtable *st = cmap->find_subtable (0, 5);
	if (st && st->u.format == 14)
	  subtable_uvs = &st->u.format14;
      }
      /* Meh. */
      if (!subtable_uvs) subtable_uvs = &OT::Null(OT::CmapSubtableFormat14);

      this->uvs_table = subtable_uvs;

      this->get_glyph_data = subtable;
      if (unlikely (symbol))
	this->get_glyph_func = get_glyph_from_symbol<OT::CmapSubtable>;
      else
	switch (subtable->u.format) {
	/* Accelerate format 4 and format 12. */
	default: this->get_glyph_func = get_glyph_from<OT::CmapSubtable>;		break;
	case 12: this->get_glyph_func = get_glyph_from<OT::CmapSubtableFormat12>;	break;
	case  4:
	  {
	    this->format4_accel.init (&subtable->u.format4);
	    this->get_glyph_data = &this->format4_accel;
	    this->get_glyph_func = this->format4_accel.get_glyph_func;
	  }
	  break;
	}
    }

    inline void fini (void)
    {
      hb_blob_destroy (this->blob);
    }

    inline bool get_nominal_glyph (hb_codepoint_t  unicode,
				   hb_codepoint_t *glyph) const
    {
      return this->get_glyph_func (this->get_glyph_data, unicode, glyph);
    }

    inline bool get_variation_glyph (hb_codepoint_t  unicode,
				     hb_codepoint_t  variation_selector,
				     hb_codepoint_t *glyph) const
    {
      switch (this->uvs_table->get_glyph_variant (unicode,
						  variation_selector,
						  glyph))
      {
	case OT::GLYPH_VARIANT_NOT_FOUND:		return false;
	case OT::GLYPH_VARIANT_FOUND:		return true;
	case OT::GLYPH_VARIANT_USE_DEFAULT:	break;
      }

      return get_nominal_glyph (unicode, glyph);
    }

    protected:
    typedef bool (*hb_cmap_get_glyph_func_t) (const void *obj,
					      hb_codepoint_t codepoint,
					      hb_codepoint_t *glyph);

    template <typename Type>
    static inline bool get_glyph_from (const void *obj,
				       hb_codepoint_t codepoint,
				       hb_codepoint_t *glyph)
    {
      const Type *typed_obj = (const Type *) obj;
      return typed_obj->get_glyph (codepoint, glyph);
    }

    template <typename Type>
    static inline bool get_glyph_from_symbol (const void *obj,
					      hb_codepoint_t codepoint,
					      hb_codepoint_t *glyph)
    {
      const Type *typed_obj = (const Type *) obj;
      if (likely (typed_obj->get_glyph (codepoint, glyph)))
	return true;

      if (codepoint <= 0x00FFu)
      {
	/* For symbol-encoded OpenType fonts, we duplicate the
	 * U+F000..F0FF range at U+0000..U+00FF.  That's what
	 * Windows seems to do, and that's hinted about at:
	 * http://www.microsoft.com/typography/otspec/recom.htm
	 * under "Non-Standard (Symbol) Fonts". */
	return typed_obj->get_glyph (0xF000u + codepoint, glyph);
      }

      return false;
    }

    private:
    hb_cmap_get_glyph_func_t get_glyph_func;
    const void *get_glyph_data;
    OT::CmapSubtableFormat4::accelerator_t format4_accel;

    const OT::CmapSubtableFormat14 *uvs_table;
    hb_blob_t *blob;
  };

  protected:

756 757
  inline const CmapSubtable *find_subtable (unsigned int platform_id,
					    unsigned int encoding_id) const
758 759 760 761 762
  {
    EncodingRecord key;
    key.platformID.set (platform_id);
    key.encodingID.set (encoding_id);

763 764 765 766
    /* Note: We can use bsearch, but since it has no performance
     * implications, we use lsearch and as such accept fonts with
     * unsorted subtable list. */
    int result = encodingRecord./*bsearch*/lsearch (key);
767
    if (result == -1 || !encodingRecord[result].subtable)
B
Behdad Esfahbod 已提交
768
      return nullptr;
769 770

    return &(this+encodingRecord[result].subtable);
771 772
  }

773
  protected:
B
Behdad Esfahbod 已提交
774
  HBUINT16		version;	/* Table version number (0). */
775
  SortedArrayOf<EncodingRecord>
776
			encodingRecord;	/* Encoding tables. */
777 778 779 780 781 782 783 784 785
  public:
  DEFINE_SIZE_ARRAY (4, encodingRecord);
};


} /* namespace OT */


#endif /* HB_OT_CMAP_TABLE_HH */