| /* | 
 |  * 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 | 
 |  | 
 | #include "hb-open-type-private.hh" | 
 |  | 
 |  | 
 | namespace OT { | 
 |  | 
 |  | 
 | /* | 
 |  * cmap -- Character To Glyph Index Mapping Table | 
 |  */ | 
 |  | 
 | #define HB_OT_TAG_cmap HB_TAG('c','m','a','p') | 
 |  | 
 |  | 
 | 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; | 
 |   } | 
 |  | 
 |   inline bool sanitize (hb_sanitize_context_t *c) const | 
 |   { | 
 |     TRACE_SANITIZE (this); | 
 |     return_trace (c->check_struct (this)); | 
 |   } | 
 |  | 
 |   protected: | 
 |   USHORT	format;		/* Format number is set to 0. */ | 
 |   USHORT	lengthZ;	/* Byte length of this subtable. */ | 
 |   USHORT	languageZ;	/* Ignore. */ | 
 |   BYTE		glyphIdArray[256];/* An array that maps character | 
 | 				 * code to glyph index values. */ | 
 |   public: | 
 |   DEFINE_SIZE_STATIC (6 + 256); | 
 | }; | 
 |  | 
 | struct CmapSubtableFormat4 | 
 | { | 
 |   inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const | 
 |   { | 
 |     unsigned int segCount; | 
 |     const USHORT *endCount; | 
 |     const USHORT *startCount; | 
 |     const USHORT *idDelta; | 
 |     const USHORT *idRangeOffset; | 
 |     const USHORT *glyphIdArray; | 
 |     unsigned int glyphIdArrayLength; | 
 |  | 
 |     segCount = this->segCountX2 / 2; | 
 |     endCount = this->values; | 
 |     startCount = endCount + segCount + 1; | 
 |     idDelta = startCount + segCount; | 
 |     idRangeOffset = idDelta + segCount; | 
 |     glyphIdArray = idRangeOffset + segCount; | 
 |     glyphIdArrayLength = (this->length - 16 - 8 * segCount) / 2; | 
 |  | 
 |     /* Custom two-array bsearch. */ | 
 |     int min = 0, max = (int) segCount - 1; | 
 |     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 = idRangeOffset[i]; | 
 |     if (rangeOffset == 0) | 
 |       gid = codepoint + idDelta[i]; | 
 |     else | 
 |     { | 
 |       /* Somebody has been smoking... */ | 
 |       unsigned int index = rangeOffset / 2 + (codepoint - startCount[i]) + i - segCount; | 
 |       if (unlikely (index >= glyphIdArrayLength)) | 
 | 	return false; | 
 |       gid = glyphIdArray[index]; | 
 |       if (unlikely (!gid)) | 
 | 	return false; | 
 |       gid += idDelta[i]; | 
 |     } | 
 |  | 
 |     *glyph = gid & 0xFFFFu; | 
 |     return true; | 
 |   } | 
 |  | 
 |   inline bool sanitize (hb_sanitize_context_t *c) const | 
 |   { | 
 |     TRACE_SANITIZE (this); | 
 |     if (unlikely (!c->check_struct (this))) | 
 |       return_trace (false); | 
 |  | 
 |     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)) | 
 | 	return_trace (false); | 
 |     } | 
 |  | 
 |     return_trace (16 + 4 * (unsigned int) segCountX2 <= length); | 
 |   } | 
 |  | 
 |   protected: | 
 |   USHORT	format;		/* Format number is set to 4. */ | 
 |   USHORT	length;		/* This is the length in bytes of the | 
 | 				 * subtable. */ | 
 |   USHORT	languageZ;	/* Ignore. */ | 
 |   USHORT	segCountX2;	/* 2 x segCount. */ | 
 |   USHORT	searchRangeZ;	/* 2 * (2**floor(log2(segCount))) */ | 
 |   USHORT	entrySelectorZ;	/* log2(searchRange/2) */ | 
 |   USHORT	rangeShiftZ;	/* 2 x segCount - searchRange */ | 
 |  | 
 |   USHORT	values[VAR]; | 
 | #if 0 | 
 |   USHORT	endCount[segCount];	/* End characterCode for each segment, | 
 | 					 * last=0xFFFFu. */ | 
 |   USHORT	reservedPad;		/* Set to 0. */ | 
 |   USHORT	startCount[segCount];	/* Start character code for each segment. */ | 
 |   SHORT		idDelta[segCount];	/* Delta for all character codes in segment. */ | 
 |   USHORT	idRangeOffset[segCount];/* Offsets into glyphIdArray or 0 */ | 
 |   USHORT	glyphIdArray[VAR];	/* Glyph index array (arbitrary length) */ | 
 | #endif | 
 |  | 
 |   public: | 
 |   DEFINE_SIZE_ARRAY (14, values); | 
 | }; | 
 |  | 
 | struct CmapSubtableLongGroup | 
 | { | 
 |   friend struct CmapSubtableFormat12; | 
 |   friend struct CmapSubtableFormat13; | 
 |  | 
 |   int cmp (hb_codepoint_t codepoint) const | 
 |   { | 
 |     if (codepoint < startCharCode) return -1; | 
 |     if (codepoint > endCharCode)   return +1; | 
 |     return 0; | 
 |   } | 
 |  | 
 |   inline bool sanitize (hb_sanitize_context_t *c) const | 
 |   { | 
 |     TRACE_SANITIZE (this); | 
 |     return_trace (c->check_struct (this)); | 
 |   } | 
 |  | 
 |   private: | 
 |   ULONG		startCharCode;	/* First character code in this group. */ | 
 |   ULONG		endCharCode;	/* Last character code in this group. */ | 
 |   ULONG		glyphID;	/* Glyph index; interpretation depends on | 
 | 				 * subtable format. */ | 
 |   public: | 
 |   DEFINE_SIZE_STATIC (12); | 
 | }; | 
 |  | 
 | template <typename UINT> | 
 | struct CmapSubtableTrimmed | 
 | { | 
 |   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; | 
 |   } | 
 |  | 
 |   inline bool sanitize (hb_sanitize_context_t *c) const | 
 |   { | 
 |     TRACE_SANITIZE (this); | 
 |     return_trace (c->check_struct (this) && glyphIdArray.sanitize (c)); | 
 |   } | 
 |  | 
 |   protected: | 
 |   UINT		formatReserved;	/* Subtable format and (maybe) padding. */ | 
 |   UINT		lengthZ;	/* Byte length of this subtable. */ | 
 |   UINT		languageZ;	/* Ignore. */ | 
 |   UINT		startCharCode;	/* First character code covered. */ | 
 |   ArrayOf<GlyphID, UINT> | 
 | 		glyphIdArray;	/* Array of glyph index values for character | 
 | 				 * codes in the range. */ | 
 |   public: | 
 |   DEFINE_SIZE_ARRAY (5 * sizeof (UINT), glyphIdArray); | 
 | }; | 
 |  | 
 | struct CmapSubtableFormat6  : CmapSubtableTrimmed<USHORT> {}; | 
 | struct CmapSubtableFormat10 : CmapSubtableTrimmed<ULONG > {}; | 
 |  | 
 | template <typename T> | 
 | struct CmapSubtableLongSegmented | 
 | { | 
 |   inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const | 
 |   { | 
 |     int i = groups.bsearch (codepoint); | 
 |     if (i == -1) | 
 |       return false; | 
 |     *glyph = T::group_get_glyph (groups[i], codepoint); | 
 |     return true; | 
 |   } | 
 |  | 
 |   inline bool sanitize (hb_sanitize_context_t *c) const | 
 |   { | 
 |     TRACE_SANITIZE (this); | 
 |     return_trace (c->check_struct (this) && groups.sanitize (c)); | 
 |   } | 
 |  | 
 |   protected: | 
 |   USHORT	format;		/* Subtable format; set to 12. */ | 
 |   USHORT	reservedZ;	/* Reserved; set to 0. */ | 
 |   ULONG		lengthZ;	/* Byte length of this subtable. */ | 
 |   ULONG		languageZ;	/* Ignore. */ | 
 |   SortedArrayOf<CmapSubtableLongGroup, ULONG> | 
 | 		groups;		/* Groupings. */ | 
 |   public: | 
 |   DEFINE_SIZE_ARRAY (16, groups); | 
 | }; | 
 |  | 
 | struct CmapSubtableFormat12 : CmapSubtableLongSegmented<CmapSubtableFormat12> | 
 | { | 
 |   static inline hb_codepoint_t group_get_glyph (const CmapSubtableLongGroup &group, | 
 | 						hb_codepoint_t u) | 
 |   { return group.glyphID + (u - group.startCharCode); } | 
 | }; | 
 |  | 
 | struct CmapSubtableFormat13 : CmapSubtableLongSegmented<CmapSubtableFormat13> | 
 | { | 
 |   static inline hb_codepoint_t group_get_glyph (const CmapSubtableLongGroup &group, | 
 | 						hb_codepoint_t u HB_UNUSED) | 
 |   { return group.glyphID; } | 
 | }; | 
 |  | 
 | 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; | 
 |   } | 
 |  | 
 |   inline bool sanitize (hb_sanitize_context_t *c) const | 
 |   { | 
 |     TRACE_SANITIZE (this); | 
 |     return_trace (c->check_struct (this)); | 
 |   } | 
 |  | 
 |   UINT24	startUnicodeValue;	/* First value in this range. */ | 
 |   BYTE		additionalCount;	/* Number of additional values in this | 
 | 					 * range. */ | 
 |   public: | 
 |   DEFINE_SIZE_STATIC (4); | 
 | }; | 
 |  | 
 | typedef SortedArrayOf<UnicodeValueRange, ULONG> DefaultUVS; | 
 |  | 
 | struct UVSMapping | 
 | { | 
 |   inline int cmp (const hb_codepoint_t &codepoint) const | 
 |   { | 
 |     return unicodeValue.cmp (codepoint); | 
 |   } | 
 |  | 
 |   inline bool sanitize (hb_sanitize_context_t *c) const | 
 |   { | 
 |     TRACE_SANITIZE (this); | 
 |     return_trace (c->check_struct (this)); | 
 |   } | 
 |  | 
 |   UINT24	unicodeValue;	/* Base Unicode value of the UVS */ | 
 |   GlyphID	glyphID;	/* Glyph ID of the UVS */ | 
 |   public: | 
 |   DEFINE_SIZE_STATIC (5); | 
 | }; | 
 |  | 
 | typedef SortedArrayOf<UVSMapping, ULONG> NonDefaultUVS; | 
 |  | 
 | 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); | 
 |   } | 
 |  | 
 |   inline bool sanitize (hb_sanitize_context_t *c, const void *base) const | 
 |   { | 
 |     TRACE_SANITIZE (this); | 
 |     return_trace (c->check_struct (this) && | 
 | 		  defaultUVS.sanitize (c, base) && | 
 | 		  nonDefaultUVS.sanitize (c, base)); | 
 |   } | 
 |  | 
 |   UINT24	varSelector;	/* Variation selector. */ | 
 |   OffsetTo<DefaultUVS, ULONG> | 
 | 		defaultUVS;	/* Offset to Default UVS Table. May be 0. */ | 
 |   OffsetTo<NonDefaultUVS, ULONG> | 
 | 		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); | 
 |   } | 
 |  | 
 |   inline bool sanitize (hb_sanitize_context_t *c) const | 
 |   { | 
 |     TRACE_SANITIZE (this); | 
 |     return_trace (c->check_struct (this) && | 
 | 		  record.sanitize (c, this)); | 
 |   } | 
 |  | 
 |   protected: | 
 |   USHORT	format;		/* Format number is set to 0. */ | 
 |   ULONG		lengthZ;	/* Byte length of this subtable. */ | 
 |   SortedArrayOf<VariationSelectorRecord, ULONG> | 
 | 		record;		/* Variation selector records; sorted | 
 | 				 * in increasing order of `varSelector'. */ | 
 |   public: | 
 |   DEFINE_SIZE_ARRAY (10, record); | 
 | }; | 
 |  | 
 | struct CmapSubtable | 
 | { | 
 |   /* Note: We intentionally do NOT implement subtable formats 2 and 8. */ | 
 |  | 
 |   inline bool get_glyph (hb_codepoint_t codepoint, | 
 | 			 hb_codepoint_t *glyph) const | 
 |   { | 
 |     switch (u.format) { | 
 |     case  0: return u.format0 .get_glyph(codepoint, glyph); | 
 |     case  4: return u.format4 .get_glyph(codepoint, glyph); | 
 |     case  6: return u.format6 .get_glyph(codepoint, glyph); | 
 |     case 10: return u.format10.get_glyph(codepoint, glyph); | 
 |     case 12: return u.format12.get_glyph(codepoint, glyph); | 
 |     case 13: return u.format13.get_glyph(codepoint, glyph); | 
 |     case 14: | 
 |     default: return false; | 
 |     } | 
 |   } | 
 |  | 
 |   inline glyph_variant_t get_glyph_variant (hb_codepoint_t codepoint, | 
 | 					    hb_codepoint_t variation_selector, | 
 | 					    hb_codepoint_t *glyph) const | 
 |   { | 
 |     switch (u.format) { | 
 |     case 14: return u.format14.get_glyph_variant(codepoint, variation_selector, glyph); | 
 |     default: return GLYPH_VARIANT_NOT_FOUND; | 
 |     } | 
 |   } | 
 |  | 
 |   inline bool sanitize (hb_sanitize_context_t *c) const | 
 |   { | 
 |     TRACE_SANITIZE (this); | 
 |     if (!u.format.sanitize (c)) return_trace (false); | 
 |     switch (u.format) { | 
 |     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); | 
 |     } | 
 |   } | 
 |  | 
 |   protected: | 
 |   union { | 
 |   USHORT		format;		/* Format identifier */ | 
 |   CmapSubtableFormat0	format0; | 
 |   CmapSubtableFormat4	format4; | 
 |   CmapSubtableFormat6	format6; | 
 |   CmapSubtableFormat10	format10; | 
 |   CmapSubtableFormat12	format12; | 
 |   CmapSubtableFormat13	format13; | 
 |   CmapSubtableFormat14	format14; | 
 |   } u; | 
 |   public: | 
 |   DEFINE_SIZE_UNION (2, format); | 
 | }; | 
 |  | 
 |  | 
 | struct EncodingRecord | 
 | { | 
 |   inline int cmp (const EncodingRecord &other) const | 
 |   { | 
 |     int ret; | 
 |     ret = platformID.cmp (other.platformID); | 
 |     if (ret) return ret; | 
 |     ret = encodingID.cmp (other.encodingID); | 
 |     if (ret) return ret; | 
 |     return 0; | 
 |   } | 
 |  | 
 |   inline bool sanitize (hb_sanitize_context_t *c, const void *base) const | 
 |   { | 
 |     TRACE_SANITIZE (this); | 
 |     return_trace (c->check_struct (this) && | 
 | 		  subtable.sanitize (c, base)); | 
 |   } | 
 |  | 
 |   USHORT	platformID;	/* Platform ID. */ | 
 |   USHORT	encodingID;	/* Platform-specific encoding ID. */ | 
 |   OffsetTo<CmapSubtable, ULONG> | 
 | 		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; | 
 |  | 
 |   inline const CmapSubtable *find_subtable (unsigned int platform_id, | 
 | 					    unsigned int encoding_id) const | 
 |   { | 
 |     EncodingRecord key; | 
 |     key.platformID.set (platform_id); | 
 |     key.encodingID.set (encoding_id); | 
 |  | 
 |     /* 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); | 
 |     if (result == -1 || !encodingRecord[result].subtable) | 
 |       return NULL; | 
 |  | 
 |     return &(this+encodingRecord[result].subtable); | 
 |   } | 
 |  | 
 |   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)); | 
 |   } | 
 |  | 
 |   USHORT		version;	/* Table version number (0). */ | 
 |   SortedArrayOf<EncodingRecord> | 
 | 			encodingRecord;	/* Encoding tables. */ | 
 |   public: | 
 |   DEFINE_SIZE_ARRAY (4, encodingRecord); | 
 | }; | 
 |  | 
 |  | 
 | } /* namespace OT */ | 
 |  | 
 |  | 
 | #endif /* HB_OT_CMAP_TABLE_HH */ |