|  | /* | 
|  | * 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 | 
|  | { | 
|  | struct accelerator_t | 
|  | { | 
|  | 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; | 
|  | } | 
|  |  | 
|  | static inline bool get_glyph_func (const void *obj, hb_codepoint_t codepoint, hb_codepoint_t *glyph) | 
|  | { | 
|  | const accelerator_t *thiz = (const accelerator_t *) obj; | 
|  |  | 
|  | /* Custom two-array bsearch. */ | 
|  | int min = 0, max = (int) thiz->segCount - 1; | 
|  | const USHORT *startCount = thiz->startCount; | 
|  | const USHORT *endCount = thiz->endCount; | 
|  | 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]; | 
|  | else | 
|  | { | 
|  | /* 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]; | 
|  | } | 
|  |  | 
|  | *glyph = gid & 0xFFFFu; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | const USHORT *endCount; | 
|  | const USHORT *startCount; | 
|  | const USHORT *idDelta; | 
|  | const USHORT *idRangeOffset; | 
|  | const USHORT *glyphIdArray; | 
|  | 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); | 
|  | } | 
|  |  | 
|  | 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 14. */ | 
|  | 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 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); | 
|  | } | 
|  | } | 
|  |  | 
|  | public: | 
|  | 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 */ |