Fri, 16 Jan 2015 18:13:44 +0100
Integrate suggestion from review to improve consistency with existing code.
michael@0 | 1 | /* |
michael@0 | 2 | * Copyright © 2007,2008,2009 Red Hat, Inc. |
michael@0 | 3 | * Copyright © 2010,2012 Google, Inc. |
michael@0 | 4 | * |
michael@0 | 5 | * This is part of HarfBuzz, a text shaping library. |
michael@0 | 6 | * |
michael@0 | 7 | * Permission is hereby granted, without written agreement and without |
michael@0 | 8 | * license or royalty fees, to use, copy, modify, and distribute this |
michael@0 | 9 | * software and its documentation for any purpose, provided that the |
michael@0 | 10 | * above copyright notice and the following two paragraphs appear in |
michael@0 | 11 | * all copies of this software. |
michael@0 | 12 | * |
michael@0 | 13 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR |
michael@0 | 14 | * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES |
michael@0 | 15 | * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN |
michael@0 | 16 | * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH |
michael@0 | 17 | * DAMAGE. |
michael@0 | 18 | * |
michael@0 | 19 | * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, |
michael@0 | 20 | * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND |
michael@0 | 21 | * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS |
michael@0 | 22 | * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO |
michael@0 | 23 | * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. |
michael@0 | 24 | * |
michael@0 | 25 | * Red Hat Author(s): Behdad Esfahbod |
michael@0 | 26 | * Google Author(s): Behdad Esfahbod |
michael@0 | 27 | */ |
michael@0 | 28 | |
michael@0 | 29 | #ifndef HB_OT_LAYOUT_COMMON_PRIVATE_HH |
michael@0 | 30 | #define HB_OT_LAYOUT_COMMON_PRIVATE_HH |
michael@0 | 31 | |
michael@0 | 32 | #include "hb-ot-layout-private.hh" |
michael@0 | 33 | #include "hb-open-type-private.hh" |
michael@0 | 34 | #include "hb-set-private.hh" |
michael@0 | 35 | |
michael@0 | 36 | |
michael@0 | 37 | namespace OT { |
michael@0 | 38 | |
michael@0 | 39 | |
michael@0 | 40 | #define NOT_COVERED ((unsigned int) -1) |
michael@0 | 41 | #define MAX_NESTING_LEVEL 8 |
michael@0 | 42 | #define MAX_CONTEXT_LENGTH 64 |
michael@0 | 43 | |
michael@0 | 44 | |
michael@0 | 45 | |
michael@0 | 46 | /* |
michael@0 | 47 | * |
michael@0 | 48 | * OpenType Layout Common Table Formats |
michael@0 | 49 | * |
michael@0 | 50 | */ |
michael@0 | 51 | |
michael@0 | 52 | |
michael@0 | 53 | /* |
michael@0 | 54 | * Script, ScriptList, LangSys, Feature, FeatureList, Lookup, LookupList |
michael@0 | 55 | */ |
michael@0 | 56 | |
michael@0 | 57 | template <typename Type> |
michael@0 | 58 | struct Record |
michael@0 | 59 | { |
michael@0 | 60 | inline int cmp (hb_tag_t a) const { |
michael@0 | 61 | return tag.cmp (a); |
michael@0 | 62 | } |
michael@0 | 63 | |
michael@0 | 64 | struct sanitize_closure_t { |
michael@0 | 65 | hb_tag_t tag; |
michael@0 | 66 | void *list_base; |
michael@0 | 67 | }; |
michael@0 | 68 | inline bool sanitize (hb_sanitize_context_t *c, void *base) { |
michael@0 | 69 | TRACE_SANITIZE (this); |
michael@0 | 70 | const sanitize_closure_t closure = {tag, base}; |
michael@0 | 71 | return TRACE_RETURN (c->check_struct (this) && offset.sanitize (c, base, &closure)); |
michael@0 | 72 | } |
michael@0 | 73 | |
michael@0 | 74 | Tag tag; /* 4-byte Tag identifier */ |
michael@0 | 75 | OffsetTo<Type> |
michael@0 | 76 | offset; /* Offset from beginning of object holding |
michael@0 | 77 | * the Record */ |
michael@0 | 78 | public: |
michael@0 | 79 | DEFINE_SIZE_STATIC (6); |
michael@0 | 80 | }; |
michael@0 | 81 | |
michael@0 | 82 | template <typename Type> |
michael@0 | 83 | struct RecordArrayOf : SortedArrayOf<Record<Type> > { |
michael@0 | 84 | inline const Tag& get_tag (unsigned int i) const |
michael@0 | 85 | { |
michael@0 | 86 | /* We cheat slightly and don't define separate Null objects |
michael@0 | 87 | * for Record types. Instead, we return the correct Null(Tag) |
michael@0 | 88 | * here. */ |
michael@0 | 89 | if (unlikely (i >= this->len)) return Null(Tag); |
michael@0 | 90 | return (*this)[i].tag; |
michael@0 | 91 | } |
michael@0 | 92 | inline unsigned int get_tags (unsigned int start_offset, |
michael@0 | 93 | unsigned int *record_count /* IN/OUT */, |
michael@0 | 94 | hb_tag_t *record_tags /* OUT */) const |
michael@0 | 95 | { |
michael@0 | 96 | if (record_count) { |
michael@0 | 97 | const Record<Type> *arr = this->sub_array (start_offset, record_count); |
michael@0 | 98 | unsigned int count = *record_count; |
michael@0 | 99 | for (unsigned int i = 0; i < count; i++) |
michael@0 | 100 | record_tags[i] = arr[i].tag; |
michael@0 | 101 | } |
michael@0 | 102 | return this->len; |
michael@0 | 103 | } |
michael@0 | 104 | inline bool find_index (hb_tag_t tag, unsigned int *index) const |
michael@0 | 105 | { |
michael@0 | 106 | int i = this->search (tag); |
michael@0 | 107 | if (i != -1) { |
michael@0 | 108 | if (index) *index = i; |
michael@0 | 109 | return true; |
michael@0 | 110 | } else { |
michael@0 | 111 | if (index) *index = Index::NOT_FOUND_INDEX; |
michael@0 | 112 | return false; |
michael@0 | 113 | } |
michael@0 | 114 | } |
michael@0 | 115 | }; |
michael@0 | 116 | |
michael@0 | 117 | template <typename Type> |
michael@0 | 118 | struct RecordListOf : RecordArrayOf<Type> |
michael@0 | 119 | { |
michael@0 | 120 | inline const Type& operator [] (unsigned int i) const |
michael@0 | 121 | { return this+RecordArrayOf<Type>::operator [](i).offset; } |
michael@0 | 122 | |
michael@0 | 123 | inline bool sanitize (hb_sanitize_context_t *c) { |
michael@0 | 124 | TRACE_SANITIZE (this); |
michael@0 | 125 | return TRACE_RETURN (RecordArrayOf<Type>::sanitize (c, this)); |
michael@0 | 126 | } |
michael@0 | 127 | }; |
michael@0 | 128 | |
michael@0 | 129 | |
michael@0 | 130 | struct RangeRecord |
michael@0 | 131 | { |
michael@0 | 132 | inline int cmp (hb_codepoint_t g) const { |
michael@0 | 133 | return g < start ? -1 : g <= end ? 0 : +1 ; |
michael@0 | 134 | } |
michael@0 | 135 | |
michael@0 | 136 | inline bool sanitize (hb_sanitize_context_t *c) { |
michael@0 | 137 | TRACE_SANITIZE (this); |
michael@0 | 138 | return TRACE_RETURN (c->check_struct (this)); |
michael@0 | 139 | } |
michael@0 | 140 | |
michael@0 | 141 | inline bool intersects (const hb_set_t *glyphs) const { |
michael@0 | 142 | return glyphs->intersects (start, end); |
michael@0 | 143 | } |
michael@0 | 144 | |
michael@0 | 145 | template <typename set_t> |
michael@0 | 146 | inline void add_coverage (set_t *glyphs) const { |
michael@0 | 147 | glyphs->add_range (start, end); |
michael@0 | 148 | } |
michael@0 | 149 | |
michael@0 | 150 | GlyphID start; /* First GlyphID in the range */ |
michael@0 | 151 | GlyphID end; /* Last GlyphID in the range */ |
michael@0 | 152 | USHORT value; /* Value */ |
michael@0 | 153 | public: |
michael@0 | 154 | DEFINE_SIZE_STATIC (6); |
michael@0 | 155 | }; |
michael@0 | 156 | DEFINE_NULL_DATA (RangeRecord, "\000\001"); |
michael@0 | 157 | |
michael@0 | 158 | |
michael@0 | 159 | struct IndexArray : ArrayOf<Index> |
michael@0 | 160 | { |
michael@0 | 161 | inline unsigned int get_indexes (unsigned int start_offset, |
michael@0 | 162 | unsigned int *_count /* IN/OUT */, |
michael@0 | 163 | unsigned int *_indexes /* OUT */) const |
michael@0 | 164 | { |
michael@0 | 165 | if (_count) { |
michael@0 | 166 | const USHORT *arr = this->sub_array (start_offset, _count); |
michael@0 | 167 | unsigned int count = *_count; |
michael@0 | 168 | for (unsigned int i = 0; i < count; i++) |
michael@0 | 169 | _indexes[i] = arr[i]; |
michael@0 | 170 | } |
michael@0 | 171 | return this->len; |
michael@0 | 172 | } |
michael@0 | 173 | }; |
michael@0 | 174 | |
michael@0 | 175 | |
michael@0 | 176 | struct Script; |
michael@0 | 177 | struct LangSys; |
michael@0 | 178 | struct Feature; |
michael@0 | 179 | |
michael@0 | 180 | |
michael@0 | 181 | struct LangSys |
michael@0 | 182 | { |
michael@0 | 183 | inline unsigned int get_feature_count (void) const |
michael@0 | 184 | { return featureIndex.len; } |
michael@0 | 185 | inline hb_tag_t get_feature_index (unsigned int i) const |
michael@0 | 186 | { return featureIndex[i]; } |
michael@0 | 187 | inline unsigned int get_feature_indexes (unsigned int start_offset, |
michael@0 | 188 | unsigned int *feature_count /* IN/OUT */, |
michael@0 | 189 | unsigned int *feature_indexes /* OUT */) const |
michael@0 | 190 | { return featureIndex.get_indexes (start_offset, feature_count, feature_indexes); } |
michael@0 | 191 | |
michael@0 | 192 | inline bool has_required_feature (void) const { return reqFeatureIndex != 0xffff; } |
michael@0 | 193 | inline unsigned int get_required_feature_index (void) const |
michael@0 | 194 | { |
michael@0 | 195 | if (reqFeatureIndex == 0xffff) |
michael@0 | 196 | return Index::NOT_FOUND_INDEX; |
michael@0 | 197 | return reqFeatureIndex;; |
michael@0 | 198 | } |
michael@0 | 199 | |
michael@0 | 200 | inline bool sanitize (hb_sanitize_context_t *c, |
michael@0 | 201 | const Record<LangSys>::sanitize_closure_t * = NULL) { |
michael@0 | 202 | TRACE_SANITIZE (this); |
michael@0 | 203 | return TRACE_RETURN (c->check_struct (this) && featureIndex.sanitize (c)); |
michael@0 | 204 | } |
michael@0 | 205 | |
michael@0 | 206 | Offset lookupOrder; /* = Null (reserved for an offset to a |
michael@0 | 207 | * reordering table) */ |
michael@0 | 208 | USHORT reqFeatureIndex;/* Index of a feature required for this |
michael@0 | 209 | * language system--if no required features |
michael@0 | 210 | * = 0xFFFF */ |
michael@0 | 211 | IndexArray featureIndex; /* Array of indices into the FeatureList */ |
michael@0 | 212 | public: |
michael@0 | 213 | DEFINE_SIZE_ARRAY (6, featureIndex); |
michael@0 | 214 | }; |
michael@0 | 215 | DEFINE_NULL_DATA (LangSys, "\0\0\xFF\xFF"); |
michael@0 | 216 | |
michael@0 | 217 | |
michael@0 | 218 | struct Script |
michael@0 | 219 | { |
michael@0 | 220 | inline unsigned int get_lang_sys_count (void) const |
michael@0 | 221 | { return langSys.len; } |
michael@0 | 222 | inline const Tag& get_lang_sys_tag (unsigned int i) const |
michael@0 | 223 | { return langSys.get_tag (i); } |
michael@0 | 224 | inline unsigned int get_lang_sys_tags (unsigned int start_offset, |
michael@0 | 225 | unsigned int *lang_sys_count /* IN/OUT */, |
michael@0 | 226 | hb_tag_t *lang_sys_tags /* OUT */) const |
michael@0 | 227 | { return langSys.get_tags (start_offset, lang_sys_count, lang_sys_tags); } |
michael@0 | 228 | inline const LangSys& get_lang_sys (unsigned int i) const |
michael@0 | 229 | { |
michael@0 | 230 | if (i == Index::NOT_FOUND_INDEX) return get_default_lang_sys (); |
michael@0 | 231 | return this+langSys[i].offset; |
michael@0 | 232 | } |
michael@0 | 233 | inline bool find_lang_sys_index (hb_tag_t tag, unsigned int *index) const |
michael@0 | 234 | { return langSys.find_index (tag, index); } |
michael@0 | 235 | |
michael@0 | 236 | inline bool has_default_lang_sys (void) const { return defaultLangSys != 0; } |
michael@0 | 237 | inline const LangSys& get_default_lang_sys (void) const { return this+defaultLangSys; } |
michael@0 | 238 | |
michael@0 | 239 | inline bool sanitize (hb_sanitize_context_t *c, |
michael@0 | 240 | const Record<Script>::sanitize_closure_t * = NULL) { |
michael@0 | 241 | TRACE_SANITIZE (this); |
michael@0 | 242 | return TRACE_RETURN (defaultLangSys.sanitize (c, this) && langSys.sanitize (c, this)); |
michael@0 | 243 | } |
michael@0 | 244 | |
michael@0 | 245 | protected: |
michael@0 | 246 | OffsetTo<LangSys> |
michael@0 | 247 | defaultLangSys; /* Offset to DefaultLangSys table--from |
michael@0 | 248 | * beginning of Script table--may be Null */ |
michael@0 | 249 | RecordArrayOf<LangSys> |
michael@0 | 250 | langSys; /* Array of LangSysRecords--listed |
michael@0 | 251 | * alphabetically by LangSysTag */ |
michael@0 | 252 | public: |
michael@0 | 253 | DEFINE_SIZE_ARRAY (4, langSys); |
michael@0 | 254 | }; |
michael@0 | 255 | |
michael@0 | 256 | typedef RecordListOf<Script> ScriptList; |
michael@0 | 257 | |
michael@0 | 258 | |
michael@0 | 259 | /* http://www.microsoft.com/typography/otspec/features_pt.htm#size */ |
michael@0 | 260 | struct FeatureParamsSize |
michael@0 | 261 | { |
michael@0 | 262 | inline bool sanitize (hb_sanitize_context_t *c) { |
michael@0 | 263 | TRACE_SANITIZE (this); |
michael@0 | 264 | if (unlikely (!c->check_struct (this))) return TRACE_RETURN (false); |
michael@0 | 265 | |
michael@0 | 266 | /* This subtable has some "history", if you will. Some earlier versions of |
michael@0 | 267 | * Adobe tools calculated the offset of the FeatureParams sutable from the |
michael@0 | 268 | * beginning of the FeatureList table! Now, that is dealt with in the |
michael@0 | 269 | * Feature implementation. But we still need to be able to tell junk from |
michael@0 | 270 | * real data. Note: We don't check that the nameID actually exists. |
michael@0 | 271 | * |
michael@0 | 272 | * Read Roberts wrote on 9/15/06 on opentype-list@indx.co.uk : |
michael@0 | 273 | * |
michael@0 | 274 | * Yes, it is correct that a new version of the AFDKO (version 2.0) will be |
michael@0 | 275 | * coming out soon, and that the makeotf program will build a font with a |
michael@0 | 276 | * 'size' feature that is correct by the specification. |
michael@0 | 277 | * |
michael@0 | 278 | * The specification for this feature tag is in the "OpenType Layout Tag |
michael@0 | 279 | * Registry". You can see a copy of this at: |
michael@0 | 280 | * http://partners.adobe.com/public/developer/opentype/index_tag8.html#size |
michael@0 | 281 | * |
michael@0 | 282 | * Here is one set of rules to determine if the 'size' feature is built |
michael@0 | 283 | * correctly, or as by the older versions of MakeOTF. You may be able to do |
michael@0 | 284 | * better. |
michael@0 | 285 | * |
michael@0 | 286 | * Assume that the offset to the size feature is according to specification, |
michael@0 | 287 | * and make the following value checks. If it fails, assume the the size |
michael@0 | 288 | * feature is calculated as versions of MakeOTF before the AFDKO 2.0 built it. |
michael@0 | 289 | * If this fails, reject the 'size' feature. The older makeOTF's calculated the |
michael@0 | 290 | * offset from the beginning of the FeatureList table, rather than from the |
michael@0 | 291 | * beginning of the 'size' Feature table. |
michael@0 | 292 | * |
michael@0 | 293 | * If "design size" == 0: |
michael@0 | 294 | * fails check |
michael@0 | 295 | * |
michael@0 | 296 | * Else if ("subfamily identifier" == 0 and |
michael@0 | 297 | * "range start" == 0 and |
michael@0 | 298 | * "range end" == 0 and |
michael@0 | 299 | * "range start" == 0 and |
michael@0 | 300 | * "menu name ID" == 0) |
michael@0 | 301 | * passes check: this is the format used when there is a design size |
michael@0 | 302 | * specified, but there is no recommended size range. |
michael@0 | 303 | * |
michael@0 | 304 | * Else if ("design size" < "range start" or |
michael@0 | 305 | * "design size" > "range end" or |
michael@0 | 306 | * "range end" <= "range start" or |
michael@0 | 307 | * "menu name ID" < 256 or |
michael@0 | 308 | * "menu name ID" > 32767 or |
michael@0 | 309 | * menu name ID is not a name ID which is actually in the name table) |
michael@0 | 310 | * fails test |
michael@0 | 311 | * Else |
michael@0 | 312 | * passes test. |
michael@0 | 313 | */ |
michael@0 | 314 | |
michael@0 | 315 | if (!designSize) |
michael@0 | 316 | return TRACE_RETURN (false); |
michael@0 | 317 | else if (subfamilyID == 0 && |
michael@0 | 318 | subfamilyNameID == 0 && |
michael@0 | 319 | rangeStart == 0 && |
michael@0 | 320 | rangeEnd == 0) |
michael@0 | 321 | return TRACE_RETURN (true); |
michael@0 | 322 | else if (designSize < rangeStart || |
michael@0 | 323 | designSize > rangeEnd || |
michael@0 | 324 | subfamilyNameID < 256 || |
michael@0 | 325 | subfamilyNameID > 32767) |
michael@0 | 326 | return TRACE_RETURN (false); |
michael@0 | 327 | else |
michael@0 | 328 | return TRACE_RETURN (true); |
michael@0 | 329 | } |
michael@0 | 330 | |
michael@0 | 331 | USHORT designSize; /* Represents the design size in 720/inch |
michael@0 | 332 | * units (decipoints). The design size entry |
michael@0 | 333 | * must be non-zero. When there is a design |
michael@0 | 334 | * size but no recommended size range, the |
michael@0 | 335 | * rest of the array will consist of zeros. */ |
michael@0 | 336 | USHORT subfamilyID; /* Has no independent meaning, but serves |
michael@0 | 337 | * as an identifier that associates fonts |
michael@0 | 338 | * in a subfamily. All fonts which share a |
michael@0 | 339 | * Preferred or Font Family name and which |
michael@0 | 340 | * differ only by size range shall have the |
michael@0 | 341 | * same subfamily value, and no fonts which |
michael@0 | 342 | * differ in weight or style shall have the |
michael@0 | 343 | * same subfamily value. If this value is |
michael@0 | 344 | * zero, the remaining fields in the array |
michael@0 | 345 | * will be ignored. */ |
michael@0 | 346 | USHORT subfamilyNameID;/* If the preceding value is non-zero, this |
michael@0 | 347 | * value must be set in the range 256 - 32767 |
michael@0 | 348 | * (inclusive). It records the value of a |
michael@0 | 349 | * field in the name table, which must |
michael@0 | 350 | * contain English-language strings encoded |
michael@0 | 351 | * in Windows Unicode and Macintosh Roman, |
michael@0 | 352 | * and may contain additional strings |
michael@0 | 353 | * localized to other scripts and languages. |
michael@0 | 354 | * Each of these strings is the name an |
michael@0 | 355 | * application should use, in combination |
michael@0 | 356 | * with the family name, to represent the |
michael@0 | 357 | * subfamily in a menu. Applications will |
michael@0 | 358 | * choose the appropriate version based on |
michael@0 | 359 | * their selection criteria. */ |
michael@0 | 360 | USHORT rangeStart; /* Large end of the recommended usage range |
michael@0 | 361 | * (inclusive), stored in 720/inch units |
michael@0 | 362 | * (decipoints). */ |
michael@0 | 363 | USHORT rangeEnd; /* Small end of the recommended usage range |
michael@0 | 364 | (exclusive), stored in 720/inch units |
michael@0 | 365 | * (decipoints). */ |
michael@0 | 366 | public: |
michael@0 | 367 | DEFINE_SIZE_STATIC (10); |
michael@0 | 368 | }; |
michael@0 | 369 | |
michael@0 | 370 | /* http://www.microsoft.com/typography/otspec/features_pt.htm#ssxx */ |
michael@0 | 371 | struct FeatureParamsStylisticSet |
michael@0 | 372 | { |
michael@0 | 373 | inline bool sanitize (hb_sanitize_context_t *c) { |
michael@0 | 374 | TRACE_SANITIZE (this); |
michael@0 | 375 | /* Right now minorVersion is at zero. Which means, any table supports |
michael@0 | 376 | * the uiNameID field. */ |
michael@0 | 377 | return TRACE_RETURN (c->check_struct (this)); |
michael@0 | 378 | } |
michael@0 | 379 | |
michael@0 | 380 | USHORT version; /* (set to 0): This corresponds to a “minor” |
michael@0 | 381 | * version number. Additional data may be |
michael@0 | 382 | * added to the end of this Feature Parameters |
michael@0 | 383 | * table in the future. */ |
michael@0 | 384 | |
michael@0 | 385 | USHORT uiNameID; /* The 'name' table name ID that specifies a |
michael@0 | 386 | * string (or strings, for multiple languages) |
michael@0 | 387 | * for a user-interface label for this |
michael@0 | 388 | * feature. The values of uiLabelNameId and |
michael@0 | 389 | * sampleTextNameId are expected to be in the |
michael@0 | 390 | * font-specific name ID range (256-32767), |
michael@0 | 391 | * though that is not a requirement in this |
michael@0 | 392 | * Feature Parameters specification. The |
michael@0 | 393 | * user-interface label for the feature can |
michael@0 | 394 | * be provided in multiple languages. An |
michael@0 | 395 | * English string should be included as a |
michael@0 | 396 | * fallback. The string should be kept to a |
michael@0 | 397 | * minimal length to fit comfortably with |
michael@0 | 398 | * different application interfaces. */ |
michael@0 | 399 | public: |
michael@0 | 400 | DEFINE_SIZE_STATIC (4); |
michael@0 | 401 | }; |
michael@0 | 402 | |
michael@0 | 403 | /* http://www.microsoft.com/typography/otspec/features_ae.htm#cv01-cv99 */ |
michael@0 | 404 | struct FeatureParamsCharacterVariants |
michael@0 | 405 | { |
michael@0 | 406 | inline bool sanitize (hb_sanitize_context_t *c) { |
michael@0 | 407 | TRACE_SANITIZE (this); |
michael@0 | 408 | return TRACE_RETURN (c->check_struct (this) && |
michael@0 | 409 | characters.sanitize (c)); |
michael@0 | 410 | } |
michael@0 | 411 | |
michael@0 | 412 | USHORT format; /* Format number is set to 0. */ |
michael@0 | 413 | USHORT featUILableNameID; /* The ‘name’ table name ID that |
michael@0 | 414 | * specifies a string (or strings, |
michael@0 | 415 | * for multiple languages) for a |
michael@0 | 416 | * user-interface label for this |
michael@0 | 417 | * feature. (May be NULL.) */ |
michael@0 | 418 | USHORT featUITooltipTextNameID;/* The ‘name’ table name ID that |
michael@0 | 419 | * specifies a string (or strings, |
michael@0 | 420 | * for multiple languages) that an |
michael@0 | 421 | * application can use for tooltip |
michael@0 | 422 | * text for this feature. (May be |
michael@0 | 423 | * NULL.) */ |
michael@0 | 424 | USHORT sampleTextNameID; /* The ‘name’ table name ID that |
michael@0 | 425 | * specifies sample text that |
michael@0 | 426 | * illustrates the effect of this |
michael@0 | 427 | * feature. (May be NULL.) */ |
michael@0 | 428 | USHORT numNamedParameters; /* Number of named parameters. (May |
michael@0 | 429 | * be zero.) */ |
michael@0 | 430 | USHORT firstParamUILabelNameID;/* The first ‘name’ table name ID |
michael@0 | 431 | * used to specify strings for |
michael@0 | 432 | * user-interface labels for the |
michael@0 | 433 | * feature parameters. (Must be zero |
michael@0 | 434 | * if numParameters is zero.) */ |
michael@0 | 435 | ArrayOf<UINT24> |
michael@0 | 436 | characters; /* Array of the Unicode Scalar Value |
michael@0 | 437 | * of the characters for which this |
michael@0 | 438 | * feature provides glyph variants. |
michael@0 | 439 | * (May be zero.) */ |
michael@0 | 440 | public: |
michael@0 | 441 | DEFINE_SIZE_ARRAY (14, characters); |
michael@0 | 442 | }; |
michael@0 | 443 | |
michael@0 | 444 | struct FeatureParams |
michael@0 | 445 | { |
michael@0 | 446 | inline bool sanitize (hb_sanitize_context_t *c, hb_tag_t tag) { |
michael@0 | 447 | TRACE_SANITIZE (this); |
michael@0 | 448 | if (tag == HB_TAG ('s','i','z','e')) |
michael@0 | 449 | return TRACE_RETURN (u.size.sanitize (c)); |
michael@0 | 450 | if ((tag & 0xFFFF0000) == HB_TAG ('s','s','\0','\0')) /* ssXX */ |
michael@0 | 451 | return TRACE_RETURN (u.stylisticSet.sanitize (c)); |
michael@0 | 452 | if ((tag & 0xFFFF0000) == HB_TAG ('c','v','\0','\0')) /* cvXX */ |
michael@0 | 453 | return TRACE_RETURN (u.characterVariants.sanitize (c)); |
michael@0 | 454 | return TRACE_RETURN (true); |
michael@0 | 455 | } |
michael@0 | 456 | |
michael@0 | 457 | inline const FeatureParamsSize& get_size_params (hb_tag_t tag) const |
michael@0 | 458 | { |
michael@0 | 459 | if (tag == HB_TAG ('s','i','z','e')) |
michael@0 | 460 | return u.size; |
michael@0 | 461 | return Null(FeatureParamsSize); |
michael@0 | 462 | } |
michael@0 | 463 | |
michael@0 | 464 | private: |
michael@0 | 465 | union { |
michael@0 | 466 | FeatureParamsSize size; |
michael@0 | 467 | FeatureParamsStylisticSet stylisticSet; |
michael@0 | 468 | FeatureParamsCharacterVariants characterVariants; |
michael@0 | 469 | } u; |
michael@0 | 470 | DEFINE_SIZE_STATIC (17); |
michael@0 | 471 | }; |
michael@0 | 472 | |
michael@0 | 473 | struct Feature |
michael@0 | 474 | { |
michael@0 | 475 | inline unsigned int get_lookup_count (void) const |
michael@0 | 476 | { return lookupIndex.len; } |
michael@0 | 477 | inline hb_tag_t get_lookup_index (unsigned int i) const |
michael@0 | 478 | { return lookupIndex[i]; } |
michael@0 | 479 | inline unsigned int get_lookup_indexes (unsigned int start_index, |
michael@0 | 480 | unsigned int *lookup_count /* IN/OUT */, |
michael@0 | 481 | unsigned int *lookup_tags /* OUT */) const |
michael@0 | 482 | { return lookupIndex.get_indexes (start_index, lookup_count, lookup_tags); } |
michael@0 | 483 | |
michael@0 | 484 | inline const FeatureParams &get_feature_params (void) const |
michael@0 | 485 | { return this+featureParams; } |
michael@0 | 486 | |
michael@0 | 487 | inline bool sanitize (hb_sanitize_context_t *c, |
michael@0 | 488 | const Record<Feature>::sanitize_closure_t *closure) { |
michael@0 | 489 | TRACE_SANITIZE (this); |
michael@0 | 490 | if (unlikely (!(c->check_struct (this) && lookupIndex.sanitize (c)))) |
michael@0 | 491 | return TRACE_RETURN (false); |
michael@0 | 492 | |
michael@0 | 493 | /* Some earlier versions of Adobe tools calculated the offset of the |
michael@0 | 494 | * FeatureParams subtable from the beginning of the FeatureList table! |
michael@0 | 495 | * |
michael@0 | 496 | * If sanitizing "failed" for the FeatureParams subtable, try it with the |
michael@0 | 497 | * alternative location. We would know sanitize "failed" if old value |
michael@0 | 498 | * of the offset was non-zero, but it's zeroed now. |
michael@0 | 499 | * |
michael@0 | 500 | * Only do this for the 'size' feature, since at the time of the faulty |
michael@0 | 501 | * Adobe tools, only the 'size' feature had FeatureParams defined. |
michael@0 | 502 | */ |
michael@0 | 503 | |
michael@0 | 504 | Offset orig_offset = featureParams; |
michael@0 | 505 | if (unlikely (!featureParams.sanitize (c, this, closure ? closure->tag : HB_TAG_NONE))) |
michael@0 | 506 | return TRACE_RETURN (false); |
michael@0 | 507 | |
michael@0 | 508 | if (likely (!orig_offset)) |
michael@0 | 509 | return TRACE_RETURN (true); |
michael@0 | 510 | |
michael@0 | 511 | if (featureParams == 0 && closure && |
michael@0 | 512 | closure->tag == HB_TAG ('s','i','z','e') && |
michael@0 | 513 | closure->list_base && closure->list_base < this) |
michael@0 | 514 | { |
michael@0 | 515 | unsigned int new_offset_int = (unsigned int) orig_offset - |
michael@0 | 516 | ((char *) this - (char *) closure->list_base); |
michael@0 | 517 | |
michael@0 | 518 | Offset new_offset; |
michael@0 | 519 | /* Check that it did not overflow. */ |
michael@0 | 520 | new_offset.set (new_offset_int); |
michael@0 | 521 | if (new_offset == new_offset_int && |
michael@0 | 522 | featureParams.try_set (c, new_offset) && |
michael@0 | 523 | !featureParams.sanitize (c, this, closure ? closure->tag : HB_TAG_NONE)) |
michael@0 | 524 | return TRACE_RETURN (false); |
michael@0 | 525 | } |
michael@0 | 526 | |
michael@0 | 527 | return TRACE_RETURN (true); |
michael@0 | 528 | } |
michael@0 | 529 | |
michael@0 | 530 | OffsetTo<FeatureParams> |
michael@0 | 531 | featureParams; /* Offset to Feature Parameters table (if one |
michael@0 | 532 | * has been defined for the feature), relative |
michael@0 | 533 | * to the beginning of the Feature Table; = Null |
michael@0 | 534 | * if not required */ |
michael@0 | 535 | IndexArray lookupIndex; /* Array of LookupList indices */ |
michael@0 | 536 | public: |
michael@0 | 537 | DEFINE_SIZE_ARRAY (4, lookupIndex); |
michael@0 | 538 | }; |
michael@0 | 539 | |
michael@0 | 540 | typedef RecordListOf<Feature> FeatureList; |
michael@0 | 541 | |
michael@0 | 542 | |
michael@0 | 543 | struct LookupFlag : USHORT |
michael@0 | 544 | { |
michael@0 | 545 | enum Flags { |
michael@0 | 546 | RightToLeft = 0x0001u, |
michael@0 | 547 | IgnoreBaseGlyphs = 0x0002u, |
michael@0 | 548 | IgnoreLigatures = 0x0004u, |
michael@0 | 549 | IgnoreMarks = 0x0008u, |
michael@0 | 550 | IgnoreFlags = 0x000Eu, |
michael@0 | 551 | UseMarkFilteringSet = 0x0010u, |
michael@0 | 552 | Reserved = 0x00E0u, |
michael@0 | 553 | MarkAttachmentType = 0xFF00u |
michael@0 | 554 | }; |
michael@0 | 555 | public: |
michael@0 | 556 | DEFINE_SIZE_STATIC (2); |
michael@0 | 557 | }; |
michael@0 | 558 | |
michael@0 | 559 | struct Lookup |
michael@0 | 560 | { |
michael@0 | 561 | inline unsigned int get_subtable_count (void) const { return subTable.len; } |
michael@0 | 562 | |
michael@0 | 563 | inline unsigned int get_type (void) const { return lookupType; } |
michael@0 | 564 | |
michael@0 | 565 | /* lookup_props is a 32-bit integer where the lower 16-bit is LookupFlag and |
michael@0 | 566 | * higher 16-bit is mark-filtering-set if the lookup uses one. |
michael@0 | 567 | * Not to be confused with glyph_props which is very similar. */ |
michael@0 | 568 | inline uint32_t get_props (void) const |
michael@0 | 569 | { |
michael@0 | 570 | unsigned int flag = lookupFlag; |
michael@0 | 571 | if (unlikely (flag & LookupFlag::UseMarkFilteringSet)) |
michael@0 | 572 | { |
michael@0 | 573 | const USHORT &markFilteringSet = StructAfter<USHORT> (subTable); |
michael@0 | 574 | flag += (markFilteringSet << 16); |
michael@0 | 575 | } |
michael@0 | 576 | return flag; |
michael@0 | 577 | } |
michael@0 | 578 | |
michael@0 | 579 | inline bool serialize (hb_serialize_context_t *c, |
michael@0 | 580 | unsigned int lookup_type, |
michael@0 | 581 | uint32_t lookup_props, |
michael@0 | 582 | unsigned int num_subtables) |
michael@0 | 583 | { |
michael@0 | 584 | TRACE_SERIALIZE (this); |
michael@0 | 585 | if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false); |
michael@0 | 586 | lookupType.set (lookup_type); |
michael@0 | 587 | lookupFlag.set (lookup_props & 0xFFFF); |
michael@0 | 588 | if (unlikely (!subTable.serialize (c, num_subtables))) return TRACE_RETURN (false); |
michael@0 | 589 | if (lookupFlag & LookupFlag::UseMarkFilteringSet) |
michael@0 | 590 | { |
michael@0 | 591 | USHORT &markFilteringSet = StructAfter<USHORT> (subTable); |
michael@0 | 592 | markFilteringSet.set (lookup_props >> 16); |
michael@0 | 593 | } |
michael@0 | 594 | return TRACE_RETURN (true); |
michael@0 | 595 | } |
michael@0 | 596 | |
michael@0 | 597 | inline bool sanitize (hb_sanitize_context_t *c) { |
michael@0 | 598 | TRACE_SANITIZE (this); |
michael@0 | 599 | /* Real sanitize of the subtables is done by GSUB/GPOS/... */ |
michael@0 | 600 | if (!(c->check_struct (this) && subTable.sanitize (c))) return TRACE_RETURN (false); |
michael@0 | 601 | if (lookupFlag & LookupFlag::UseMarkFilteringSet) |
michael@0 | 602 | { |
michael@0 | 603 | USHORT &markFilteringSet = StructAfter<USHORT> (subTable); |
michael@0 | 604 | if (!markFilteringSet.sanitize (c)) return TRACE_RETURN (false); |
michael@0 | 605 | } |
michael@0 | 606 | return TRACE_RETURN (true); |
michael@0 | 607 | } |
michael@0 | 608 | |
michael@0 | 609 | USHORT lookupType; /* Different enumerations for GSUB and GPOS */ |
michael@0 | 610 | USHORT lookupFlag; /* Lookup qualifiers */ |
michael@0 | 611 | ArrayOf<Offset> |
michael@0 | 612 | subTable; /* Array of SubTables */ |
michael@0 | 613 | USHORT markFilteringSetX[VAR]; /* Index (base 0) into GDEF mark glyph sets |
michael@0 | 614 | * structure. This field is only present if bit |
michael@0 | 615 | * UseMarkFilteringSet of lookup flags is set. */ |
michael@0 | 616 | public: |
michael@0 | 617 | DEFINE_SIZE_ARRAY2 (6, subTable, markFilteringSetX); |
michael@0 | 618 | }; |
michael@0 | 619 | |
michael@0 | 620 | typedef OffsetListOf<Lookup> LookupList; |
michael@0 | 621 | |
michael@0 | 622 | |
michael@0 | 623 | /* |
michael@0 | 624 | * Coverage Table |
michael@0 | 625 | */ |
michael@0 | 626 | |
michael@0 | 627 | struct CoverageFormat1 |
michael@0 | 628 | { |
michael@0 | 629 | friend struct Coverage; |
michael@0 | 630 | |
michael@0 | 631 | private: |
michael@0 | 632 | inline unsigned int get_coverage (hb_codepoint_t glyph_id) const |
michael@0 | 633 | { |
michael@0 | 634 | int i = glyphArray.search (glyph_id); |
michael@0 | 635 | ASSERT_STATIC (((unsigned int) -1) == NOT_COVERED); |
michael@0 | 636 | return i; |
michael@0 | 637 | } |
michael@0 | 638 | |
michael@0 | 639 | inline bool serialize (hb_serialize_context_t *c, |
michael@0 | 640 | Supplier<GlyphID> &glyphs, |
michael@0 | 641 | unsigned int num_glyphs) |
michael@0 | 642 | { |
michael@0 | 643 | TRACE_SERIALIZE (this); |
michael@0 | 644 | if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false); |
michael@0 | 645 | glyphArray.len.set (num_glyphs); |
michael@0 | 646 | if (unlikely (!c->extend (glyphArray))) return TRACE_RETURN (false); |
michael@0 | 647 | for (unsigned int i = 0; i < num_glyphs; i++) |
michael@0 | 648 | glyphArray[i] = glyphs[i]; |
michael@0 | 649 | glyphs.advance (num_glyphs); |
michael@0 | 650 | return TRACE_RETURN (true); |
michael@0 | 651 | } |
michael@0 | 652 | |
michael@0 | 653 | inline bool sanitize (hb_sanitize_context_t *c) { |
michael@0 | 654 | TRACE_SANITIZE (this); |
michael@0 | 655 | return TRACE_RETURN (glyphArray.sanitize (c)); |
michael@0 | 656 | } |
michael@0 | 657 | |
michael@0 | 658 | inline bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const { |
michael@0 | 659 | return glyphs->has (glyphArray[index]); |
michael@0 | 660 | } |
michael@0 | 661 | |
michael@0 | 662 | template <typename set_t> |
michael@0 | 663 | inline void add_coverage (set_t *glyphs) const { |
michael@0 | 664 | unsigned int count = glyphArray.len; |
michael@0 | 665 | for (unsigned int i = 0; i < count; i++) |
michael@0 | 666 | glyphs->add (glyphArray[i]); |
michael@0 | 667 | } |
michael@0 | 668 | |
michael@0 | 669 | public: |
michael@0 | 670 | /* Older compilers need this to be public. */ |
michael@0 | 671 | struct Iter { |
michael@0 | 672 | inline void init (const struct CoverageFormat1 &c_) { c = &c_; i = 0; }; |
michael@0 | 673 | inline bool more (void) { return i < c->glyphArray.len; } |
michael@0 | 674 | inline void next (void) { i++; } |
michael@0 | 675 | inline uint16_t get_glyph (void) { return c->glyphArray[i]; } |
michael@0 | 676 | inline uint16_t get_coverage (void) { return i; } |
michael@0 | 677 | |
michael@0 | 678 | private: |
michael@0 | 679 | const struct CoverageFormat1 *c; |
michael@0 | 680 | unsigned int i; |
michael@0 | 681 | }; |
michael@0 | 682 | private: |
michael@0 | 683 | |
michael@0 | 684 | protected: |
michael@0 | 685 | USHORT coverageFormat; /* Format identifier--format = 1 */ |
michael@0 | 686 | SortedArrayOf<GlyphID> |
michael@0 | 687 | glyphArray; /* Array of GlyphIDs--in numerical order */ |
michael@0 | 688 | public: |
michael@0 | 689 | DEFINE_SIZE_ARRAY (4, glyphArray); |
michael@0 | 690 | }; |
michael@0 | 691 | |
michael@0 | 692 | struct CoverageFormat2 |
michael@0 | 693 | { |
michael@0 | 694 | friend struct Coverage; |
michael@0 | 695 | |
michael@0 | 696 | private: |
michael@0 | 697 | inline unsigned int get_coverage (hb_codepoint_t glyph_id) const |
michael@0 | 698 | { |
michael@0 | 699 | int i = rangeRecord.search (glyph_id); |
michael@0 | 700 | if (i != -1) { |
michael@0 | 701 | const RangeRecord &range = rangeRecord[i]; |
michael@0 | 702 | return (unsigned int) range.value + (glyph_id - range.start); |
michael@0 | 703 | } |
michael@0 | 704 | return NOT_COVERED; |
michael@0 | 705 | } |
michael@0 | 706 | |
michael@0 | 707 | inline bool serialize (hb_serialize_context_t *c, |
michael@0 | 708 | Supplier<GlyphID> &glyphs, |
michael@0 | 709 | unsigned int num_glyphs) |
michael@0 | 710 | { |
michael@0 | 711 | TRACE_SERIALIZE (this); |
michael@0 | 712 | if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false); |
michael@0 | 713 | |
michael@0 | 714 | if (unlikely (!num_glyphs)) return TRACE_RETURN (true); |
michael@0 | 715 | |
michael@0 | 716 | unsigned int num_ranges = 1; |
michael@0 | 717 | for (unsigned int i = 1; i < num_glyphs; i++) |
michael@0 | 718 | if (glyphs[i - 1] + 1 != glyphs[i]) |
michael@0 | 719 | num_ranges++; |
michael@0 | 720 | rangeRecord.len.set (num_ranges); |
michael@0 | 721 | if (unlikely (!c->extend (rangeRecord))) return TRACE_RETURN (false); |
michael@0 | 722 | |
michael@0 | 723 | unsigned int range = 0; |
michael@0 | 724 | rangeRecord[range].start = glyphs[0]; |
michael@0 | 725 | rangeRecord[range].value.set (0); |
michael@0 | 726 | for (unsigned int i = 1; i < num_glyphs; i++) |
michael@0 | 727 | if (glyphs[i - 1] + 1 != glyphs[i]) { |
michael@0 | 728 | range++; |
michael@0 | 729 | rangeRecord[range].start = glyphs[i]; |
michael@0 | 730 | rangeRecord[range].value.set (i); |
michael@0 | 731 | rangeRecord[range].end = glyphs[i]; |
michael@0 | 732 | } else { |
michael@0 | 733 | rangeRecord[range].end = glyphs[i]; |
michael@0 | 734 | } |
michael@0 | 735 | glyphs.advance (num_glyphs); |
michael@0 | 736 | return TRACE_RETURN (true); |
michael@0 | 737 | } |
michael@0 | 738 | |
michael@0 | 739 | inline bool sanitize (hb_sanitize_context_t *c) { |
michael@0 | 740 | TRACE_SANITIZE (this); |
michael@0 | 741 | return TRACE_RETURN (rangeRecord.sanitize (c)); |
michael@0 | 742 | } |
michael@0 | 743 | |
michael@0 | 744 | inline bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const { |
michael@0 | 745 | unsigned int i; |
michael@0 | 746 | unsigned int count = rangeRecord.len; |
michael@0 | 747 | for (i = 0; i < count; i++) { |
michael@0 | 748 | const RangeRecord &range = rangeRecord[i]; |
michael@0 | 749 | if (range.value <= index && |
michael@0 | 750 | index < (unsigned int) range.value + (range.end - range.start) && |
michael@0 | 751 | range.intersects (glyphs)) |
michael@0 | 752 | return true; |
michael@0 | 753 | else if (index < range.value) |
michael@0 | 754 | return false; |
michael@0 | 755 | } |
michael@0 | 756 | return false; |
michael@0 | 757 | } |
michael@0 | 758 | |
michael@0 | 759 | template <typename set_t> |
michael@0 | 760 | inline void add_coverage (set_t *glyphs) const { |
michael@0 | 761 | unsigned int count = rangeRecord.len; |
michael@0 | 762 | for (unsigned int i = 0; i < count; i++) |
michael@0 | 763 | rangeRecord[i].add_coverage (glyphs); |
michael@0 | 764 | } |
michael@0 | 765 | |
michael@0 | 766 | public: |
michael@0 | 767 | /* Older compilers need this to be public. */ |
michael@0 | 768 | struct Iter { |
michael@0 | 769 | inline void init (const CoverageFormat2 &c_) { |
michael@0 | 770 | c = &c_; |
michael@0 | 771 | coverage = 0; |
michael@0 | 772 | i = 0; |
michael@0 | 773 | j = c->rangeRecord.len ? c_.rangeRecord[0].start : 0; |
michael@0 | 774 | } |
michael@0 | 775 | inline bool more (void) { return i < c->rangeRecord.len; } |
michael@0 | 776 | inline void next (void) { |
michael@0 | 777 | coverage++; |
michael@0 | 778 | if (j == c->rangeRecord[i].end) { |
michael@0 | 779 | i++; |
michael@0 | 780 | if (more ()) |
michael@0 | 781 | j = c->rangeRecord[i].start; |
michael@0 | 782 | return; |
michael@0 | 783 | } |
michael@0 | 784 | j++; |
michael@0 | 785 | } |
michael@0 | 786 | inline uint16_t get_glyph (void) { return j; } |
michael@0 | 787 | inline uint16_t get_coverage (void) { return coverage; } |
michael@0 | 788 | |
michael@0 | 789 | private: |
michael@0 | 790 | const struct CoverageFormat2 *c; |
michael@0 | 791 | unsigned int i, j, coverage; |
michael@0 | 792 | }; |
michael@0 | 793 | private: |
michael@0 | 794 | |
michael@0 | 795 | protected: |
michael@0 | 796 | USHORT coverageFormat; /* Format identifier--format = 2 */ |
michael@0 | 797 | SortedArrayOf<RangeRecord> |
michael@0 | 798 | rangeRecord; /* Array of glyph ranges--ordered by |
michael@0 | 799 | * Start GlyphID. rangeCount entries |
michael@0 | 800 | * long */ |
michael@0 | 801 | public: |
michael@0 | 802 | DEFINE_SIZE_ARRAY (4, rangeRecord); |
michael@0 | 803 | }; |
michael@0 | 804 | |
michael@0 | 805 | struct Coverage |
michael@0 | 806 | { |
michael@0 | 807 | inline unsigned int get_coverage (hb_codepoint_t glyph_id) const |
michael@0 | 808 | { |
michael@0 | 809 | switch (u.format) { |
michael@0 | 810 | case 1: return u.format1.get_coverage(glyph_id); |
michael@0 | 811 | case 2: return u.format2.get_coverage(glyph_id); |
michael@0 | 812 | default:return NOT_COVERED; |
michael@0 | 813 | } |
michael@0 | 814 | } |
michael@0 | 815 | |
michael@0 | 816 | inline bool serialize (hb_serialize_context_t *c, |
michael@0 | 817 | Supplier<GlyphID> &glyphs, |
michael@0 | 818 | unsigned int num_glyphs) |
michael@0 | 819 | { |
michael@0 | 820 | TRACE_SERIALIZE (this); |
michael@0 | 821 | if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false); |
michael@0 | 822 | unsigned int num_ranges = 1; |
michael@0 | 823 | for (unsigned int i = 1; i < num_glyphs; i++) |
michael@0 | 824 | if (glyphs[i - 1] + 1 != glyphs[i]) |
michael@0 | 825 | num_ranges++; |
michael@0 | 826 | u.format.set (num_glyphs * 2 < num_ranges * 3 ? 1 : 2); |
michael@0 | 827 | switch (u.format) { |
michael@0 | 828 | case 1: return TRACE_RETURN (u.format1.serialize (c, glyphs, num_glyphs)); |
michael@0 | 829 | case 2: return TRACE_RETURN (u.format2.serialize (c, glyphs, num_glyphs)); |
michael@0 | 830 | default:return TRACE_RETURN (false); |
michael@0 | 831 | } |
michael@0 | 832 | } |
michael@0 | 833 | |
michael@0 | 834 | inline bool sanitize (hb_sanitize_context_t *c) { |
michael@0 | 835 | TRACE_SANITIZE (this); |
michael@0 | 836 | if (!u.format.sanitize (c)) return TRACE_RETURN (false); |
michael@0 | 837 | switch (u.format) { |
michael@0 | 838 | case 1: return TRACE_RETURN (u.format1.sanitize (c)); |
michael@0 | 839 | case 2: return TRACE_RETURN (u.format2.sanitize (c)); |
michael@0 | 840 | default:return TRACE_RETURN (true); |
michael@0 | 841 | } |
michael@0 | 842 | } |
michael@0 | 843 | |
michael@0 | 844 | inline bool intersects (const hb_set_t *glyphs) const { |
michael@0 | 845 | /* TODO speed this up */ |
michael@0 | 846 | Coverage::Iter iter; |
michael@0 | 847 | for (iter.init (*this); iter.more (); iter.next ()) { |
michael@0 | 848 | if (glyphs->has (iter.get_glyph ())) |
michael@0 | 849 | return true; |
michael@0 | 850 | } |
michael@0 | 851 | return false; |
michael@0 | 852 | } |
michael@0 | 853 | |
michael@0 | 854 | inline bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const { |
michael@0 | 855 | switch (u.format) { |
michael@0 | 856 | case 1: return u.format1.intersects_coverage (glyphs, index); |
michael@0 | 857 | case 2: return u.format2.intersects_coverage (glyphs, index); |
michael@0 | 858 | default:return false; |
michael@0 | 859 | } |
michael@0 | 860 | } |
michael@0 | 861 | |
michael@0 | 862 | template <typename set_t> |
michael@0 | 863 | inline void add_coverage (set_t *glyphs) const { |
michael@0 | 864 | switch (u.format) { |
michael@0 | 865 | case 1: u.format1.add_coverage (glyphs); break; |
michael@0 | 866 | case 2: u.format2.add_coverage (glyphs); break; |
michael@0 | 867 | default: break; |
michael@0 | 868 | } |
michael@0 | 869 | } |
michael@0 | 870 | |
michael@0 | 871 | struct Iter { |
michael@0 | 872 | Iter (void) : format (0) {}; |
michael@0 | 873 | inline void init (const Coverage &c_) { |
michael@0 | 874 | format = c_.u.format; |
michael@0 | 875 | switch (format) { |
michael@0 | 876 | case 1: u.format1.init (c_.u.format1); return; |
michael@0 | 877 | case 2: u.format2.init (c_.u.format2); return; |
michael@0 | 878 | default: return; |
michael@0 | 879 | } |
michael@0 | 880 | } |
michael@0 | 881 | inline bool more (void) { |
michael@0 | 882 | switch (format) { |
michael@0 | 883 | case 1: return u.format1.more (); |
michael@0 | 884 | case 2: return u.format2.more (); |
michael@0 | 885 | default:return false; |
michael@0 | 886 | } |
michael@0 | 887 | } |
michael@0 | 888 | inline void next (void) { |
michael@0 | 889 | switch (format) { |
michael@0 | 890 | case 1: u.format1.next (); break; |
michael@0 | 891 | case 2: u.format2.next (); break; |
michael@0 | 892 | default: break; |
michael@0 | 893 | } |
michael@0 | 894 | } |
michael@0 | 895 | inline uint16_t get_glyph (void) { |
michael@0 | 896 | switch (format) { |
michael@0 | 897 | case 1: return u.format1.get_glyph (); |
michael@0 | 898 | case 2: return u.format2.get_glyph (); |
michael@0 | 899 | default:return 0; |
michael@0 | 900 | } |
michael@0 | 901 | } |
michael@0 | 902 | inline uint16_t get_coverage (void) { |
michael@0 | 903 | switch (format) { |
michael@0 | 904 | case 1: return u.format1.get_coverage (); |
michael@0 | 905 | case 2: return u.format2.get_coverage (); |
michael@0 | 906 | default:return -1; |
michael@0 | 907 | } |
michael@0 | 908 | } |
michael@0 | 909 | |
michael@0 | 910 | private: |
michael@0 | 911 | unsigned int format; |
michael@0 | 912 | union { |
michael@0 | 913 | CoverageFormat1::Iter format1; |
michael@0 | 914 | CoverageFormat2::Iter format2; |
michael@0 | 915 | } u; |
michael@0 | 916 | }; |
michael@0 | 917 | |
michael@0 | 918 | protected: |
michael@0 | 919 | union { |
michael@0 | 920 | USHORT format; /* Format identifier */ |
michael@0 | 921 | CoverageFormat1 format1; |
michael@0 | 922 | CoverageFormat2 format2; |
michael@0 | 923 | } u; |
michael@0 | 924 | public: |
michael@0 | 925 | DEFINE_SIZE_UNION (2, format); |
michael@0 | 926 | }; |
michael@0 | 927 | |
michael@0 | 928 | |
michael@0 | 929 | /* |
michael@0 | 930 | * Class Definition Table |
michael@0 | 931 | */ |
michael@0 | 932 | |
michael@0 | 933 | struct ClassDefFormat1 |
michael@0 | 934 | { |
michael@0 | 935 | friend struct ClassDef; |
michael@0 | 936 | |
michael@0 | 937 | private: |
michael@0 | 938 | inline unsigned int get_class (hb_codepoint_t glyph_id) const |
michael@0 | 939 | { |
michael@0 | 940 | if (unlikely ((unsigned int) (glyph_id - startGlyph) < classValue.len)) |
michael@0 | 941 | return classValue[glyph_id - startGlyph]; |
michael@0 | 942 | return 0; |
michael@0 | 943 | } |
michael@0 | 944 | |
michael@0 | 945 | inline bool sanitize (hb_sanitize_context_t *c) { |
michael@0 | 946 | TRACE_SANITIZE (this); |
michael@0 | 947 | return TRACE_RETURN (c->check_struct (this) && classValue.sanitize (c)); |
michael@0 | 948 | } |
michael@0 | 949 | |
michael@0 | 950 | template <typename set_t> |
michael@0 | 951 | inline void add_class (set_t *glyphs, unsigned int klass) const { |
michael@0 | 952 | unsigned int count = classValue.len; |
michael@0 | 953 | for (unsigned int i = 0; i < count; i++) |
michael@0 | 954 | if (classValue[i] == klass) |
michael@0 | 955 | glyphs->add (startGlyph + i); |
michael@0 | 956 | } |
michael@0 | 957 | |
michael@0 | 958 | inline bool intersects_class (const hb_set_t *glyphs, unsigned int klass) const { |
michael@0 | 959 | unsigned int count = classValue.len; |
michael@0 | 960 | if (klass == 0) |
michael@0 | 961 | { |
michael@0 | 962 | /* Match if there's any glyph that is not listed! */ |
michael@0 | 963 | hb_codepoint_t g = -1; |
michael@0 | 964 | if (!hb_set_next (glyphs, &g)) |
michael@0 | 965 | return false; |
michael@0 | 966 | if (g < startGlyph) |
michael@0 | 967 | return true; |
michael@0 | 968 | g = startGlyph + count - 1; |
michael@0 | 969 | if (hb_set_next (glyphs, &g)) |
michael@0 | 970 | return true; |
michael@0 | 971 | /* Fall through. */ |
michael@0 | 972 | } |
michael@0 | 973 | for (unsigned int i = 0; i < count; i++) |
michael@0 | 974 | if (classValue[i] == klass && glyphs->has (startGlyph + i)) |
michael@0 | 975 | return true; |
michael@0 | 976 | return false; |
michael@0 | 977 | } |
michael@0 | 978 | |
michael@0 | 979 | protected: |
michael@0 | 980 | USHORT classFormat; /* Format identifier--format = 1 */ |
michael@0 | 981 | GlyphID startGlyph; /* First GlyphID of the classValueArray */ |
michael@0 | 982 | ArrayOf<USHORT> |
michael@0 | 983 | classValue; /* Array of Class Values--one per GlyphID */ |
michael@0 | 984 | public: |
michael@0 | 985 | DEFINE_SIZE_ARRAY (6, classValue); |
michael@0 | 986 | }; |
michael@0 | 987 | |
michael@0 | 988 | struct ClassDefFormat2 |
michael@0 | 989 | { |
michael@0 | 990 | friend struct ClassDef; |
michael@0 | 991 | |
michael@0 | 992 | private: |
michael@0 | 993 | inline unsigned int get_class (hb_codepoint_t glyph_id) const |
michael@0 | 994 | { |
michael@0 | 995 | int i = rangeRecord.search (glyph_id); |
michael@0 | 996 | if (i != -1) |
michael@0 | 997 | return rangeRecord[i].value; |
michael@0 | 998 | return 0; |
michael@0 | 999 | } |
michael@0 | 1000 | |
michael@0 | 1001 | inline bool sanitize (hb_sanitize_context_t *c) { |
michael@0 | 1002 | TRACE_SANITIZE (this); |
michael@0 | 1003 | return TRACE_RETURN (rangeRecord.sanitize (c)); |
michael@0 | 1004 | } |
michael@0 | 1005 | |
michael@0 | 1006 | template <typename set_t> |
michael@0 | 1007 | inline void add_class (set_t *glyphs, unsigned int klass) const { |
michael@0 | 1008 | unsigned int count = rangeRecord.len; |
michael@0 | 1009 | for (unsigned int i = 0; i < count; i++) |
michael@0 | 1010 | if (rangeRecord[i].value == klass) |
michael@0 | 1011 | rangeRecord[i].add_coverage (glyphs); |
michael@0 | 1012 | } |
michael@0 | 1013 | |
michael@0 | 1014 | inline bool intersects_class (const hb_set_t *glyphs, unsigned int klass) const { |
michael@0 | 1015 | unsigned int count = rangeRecord.len; |
michael@0 | 1016 | if (klass == 0) |
michael@0 | 1017 | { |
michael@0 | 1018 | /* Match if there's any glyph that is not listed! */ |
michael@0 | 1019 | hb_codepoint_t g = (hb_codepoint_t) -1; |
michael@0 | 1020 | for (unsigned int i = 0; i < count; i++) |
michael@0 | 1021 | { |
michael@0 | 1022 | if (!hb_set_next (glyphs, &g)) |
michael@0 | 1023 | break; |
michael@0 | 1024 | if (g < rangeRecord[i].start) |
michael@0 | 1025 | return true; |
michael@0 | 1026 | g = rangeRecord[i].end; |
michael@0 | 1027 | } |
michael@0 | 1028 | if (g != (hb_codepoint_t) -1 && hb_set_next (glyphs, &g)) |
michael@0 | 1029 | return true; |
michael@0 | 1030 | /* Fall through. */ |
michael@0 | 1031 | } |
michael@0 | 1032 | for (unsigned int i = 0; i < count; i++) |
michael@0 | 1033 | if (rangeRecord[i].value == klass && rangeRecord[i].intersects (glyphs)) |
michael@0 | 1034 | return true; |
michael@0 | 1035 | return false; |
michael@0 | 1036 | } |
michael@0 | 1037 | |
michael@0 | 1038 | protected: |
michael@0 | 1039 | USHORT classFormat; /* Format identifier--format = 2 */ |
michael@0 | 1040 | SortedArrayOf<RangeRecord> |
michael@0 | 1041 | rangeRecord; /* Array of glyph ranges--ordered by |
michael@0 | 1042 | * Start GlyphID */ |
michael@0 | 1043 | public: |
michael@0 | 1044 | DEFINE_SIZE_ARRAY (4, rangeRecord); |
michael@0 | 1045 | }; |
michael@0 | 1046 | |
michael@0 | 1047 | struct ClassDef |
michael@0 | 1048 | { |
michael@0 | 1049 | inline unsigned int get_class (hb_codepoint_t glyph_id) const |
michael@0 | 1050 | { |
michael@0 | 1051 | switch (u.format) { |
michael@0 | 1052 | case 1: return u.format1.get_class(glyph_id); |
michael@0 | 1053 | case 2: return u.format2.get_class(glyph_id); |
michael@0 | 1054 | default:return 0; |
michael@0 | 1055 | } |
michael@0 | 1056 | } |
michael@0 | 1057 | |
michael@0 | 1058 | inline bool sanitize (hb_sanitize_context_t *c) { |
michael@0 | 1059 | TRACE_SANITIZE (this); |
michael@0 | 1060 | if (!u.format.sanitize (c)) return TRACE_RETURN (false); |
michael@0 | 1061 | switch (u.format) { |
michael@0 | 1062 | case 1: return TRACE_RETURN (u.format1.sanitize (c)); |
michael@0 | 1063 | case 2: return TRACE_RETURN (u.format2.sanitize (c)); |
michael@0 | 1064 | default:return TRACE_RETURN (true); |
michael@0 | 1065 | } |
michael@0 | 1066 | } |
michael@0 | 1067 | |
michael@0 | 1068 | inline void add_class (hb_set_t *glyphs, unsigned int klass) const { |
michael@0 | 1069 | switch (u.format) { |
michael@0 | 1070 | case 1: u.format1.add_class (glyphs, klass); return; |
michael@0 | 1071 | case 2: u.format2.add_class (glyphs, klass); return; |
michael@0 | 1072 | default:return; |
michael@0 | 1073 | } |
michael@0 | 1074 | } |
michael@0 | 1075 | |
michael@0 | 1076 | inline bool intersects_class (const hb_set_t *glyphs, unsigned int klass) const { |
michael@0 | 1077 | switch (u.format) { |
michael@0 | 1078 | case 1: return u.format1.intersects_class (glyphs, klass); |
michael@0 | 1079 | case 2: return u.format2.intersects_class (glyphs, klass); |
michael@0 | 1080 | default:return false; |
michael@0 | 1081 | } |
michael@0 | 1082 | } |
michael@0 | 1083 | |
michael@0 | 1084 | protected: |
michael@0 | 1085 | union { |
michael@0 | 1086 | USHORT format; /* Format identifier */ |
michael@0 | 1087 | ClassDefFormat1 format1; |
michael@0 | 1088 | ClassDefFormat2 format2; |
michael@0 | 1089 | } u; |
michael@0 | 1090 | public: |
michael@0 | 1091 | DEFINE_SIZE_UNION (2, format); |
michael@0 | 1092 | }; |
michael@0 | 1093 | |
michael@0 | 1094 | |
michael@0 | 1095 | /* |
michael@0 | 1096 | * Device Tables |
michael@0 | 1097 | */ |
michael@0 | 1098 | |
michael@0 | 1099 | struct Device |
michael@0 | 1100 | { |
michael@0 | 1101 | |
michael@0 | 1102 | inline hb_position_t get_x_delta (hb_font_t *font) const |
michael@0 | 1103 | { return get_delta (font->x_ppem, font->x_scale); } |
michael@0 | 1104 | |
michael@0 | 1105 | inline hb_position_t get_y_delta (hb_font_t *font) const |
michael@0 | 1106 | { return get_delta (font->y_ppem, font->y_scale); } |
michael@0 | 1107 | |
michael@0 | 1108 | inline int get_delta (unsigned int ppem, int scale) const |
michael@0 | 1109 | { |
michael@0 | 1110 | if (!ppem) return 0; |
michael@0 | 1111 | |
michael@0 | 1112 | int pixels = get_delta_pixels (ppem); |
michael@0 | 1113 | |
michael@0 | 1114 | if (!pixels) return 0; |
michael@0 | 1115 | |
michael@0 | 1116 | return (int) (pixels * (int64_t) scale / ppem); |
michael@0 | 1117 | } |
michael@0 | 1118 | |
michael@0 | 1119 | |
michael@0 | 1120 | inline int get_delta_pixels (unsigned int ppem_size) const |
michael@0 | 1121 | { |
michael@0 | 1122 | unsigned int f = deltaFormat; |
michael@0 | 1123 | if (unlikely (f < 1 || f > 3)) |
michael@0 | 1124 | return 0; |
michael@0 | 1125 | |
michael@0 | 1126 | if (ppem_size < startSize || ppem_size > endSize) |
michael@0 | 1127 | return 0; |
michael@0 | 1128 | |
michael@0 | 1129 | unsigned int s = ppem_size - startSize; |
michael@0 | 1130 | |
michael@0 | 1131 | unsigned int byte = deltaValue[s >> (4 - f)]; |
michael@0 | 1132 | unsigned int bits = (byte >> (16 - (((s & ((1 << (4 - f)) - 1)) + 1) << f))); |
michael@0 | 1133 | unsigned int mask = (0xFFFF >> (16 - (1 << f))); |
michael@0 | 1134 | |
michael@0 | 1135 | int delta = bits & mask; |
michael@0 | 1136 | |
michael@0 | 1137 | if ((unsigned int) delta >= ((mask + 1) >> 1)) |
michael@0 | 1138 | delta -= mask + 1; |
michael@0 | 1139 | |
michael@0 | 1140 | return delta; |
michael@0 | 1141 | } |
michael@0 | 1142 | |
michael@0 | 1143 | inline unsigned int get_size (void) const |
michael@0 | 1144 | { |
michael@0 | 1145 | unsigned int f = deltaFormat; |
michael@0 | 1146 | if (unlikely (f < 1 || f > 3 || startSize > endSize)) return 3 * USHORT::static_size; |
michael@0 | 1147 | return USHORT::static_size * (4 + ((endSize - startSize) >> (4 - f))); |
michael@0 | 1148 | } |
michael@0 | 1149 | |
michael@0 | 1150 | inline bool sanitize (hb_sanitize_context_t *c) { |
michael@0 | 1151 | TRACE_SANITIZE (this); |
michael@0 | 1152 | return TRACE_RETURN (c->check_struct (this) && c->check_range (this, this->get_size ())); |
michael@0 | 1153 | } |
michael@0 | 1154 | |
michael@0 | 1155 | protected: |
michael@0 | 1156 | USHORT startSize; /* Smallest size to correct--in ppem */ |
michael@0 | 1157 | USHORT endSize; /* Largest size to correct--in ppem */ |
michael@0 | 1158 | USHORT deltaFormat; /* Format of DeltaValue array data: 1, 2, or 3 |
michael@0 | 1159 | * 1 Signed 2-bit value, 8 values per uint16 |
michael@0 | 1160 | * 2 Signed 4-bit value, 4 values per uint16 |
michael@0 | 1161 | * 3 Signed 8-bit value, 2 values per uint16 |
michael@0 | 1162 | */ |
michael@0 | 1163 | USHORT deltaValue[VAR]; /* Array of compressed data */ |
michael@0 | 1164 | public: |
michael@0 | 1165 | DEFINE_SIZE_ARRAY (6, deltaValue); |
michael@0 | 1166 | }; |
michael@0 | 1167 | |
michael@0 | 1168 | |
michael@0 | 1169 | } /* namespace OT */ |
michael@0 | 1170 | |
michael@0 | 1171 | |
michael@0 | 1172 | #endif /* HB_OT_LAYOUT_COMMON_PRIVATE_HH */ |