4 * (C) Copyright IBM Corp. and others 1998-2013 - All Rights Reserved
10 #include "LELanguages.h"
12 #include "LayoutEngine.h"
13 #include "CanonShaping.h"
14 #include "OpenTypeLayoutEngine.h"
15 #include "ScriptAndLanguageTags.h"
16 #include "CharSubstitutionFilter.h"
18 #include "GlyphSubstitutionTables.h"
19 #include "GlyphDefinitionTables.h"
20 #include "GlyphPositioningTables.h"
22 #include "LEGlyphStorage.h"
23 #include "GlyphPositionAdjustments.h"
25 #include "GDEFMarkFilter.h"
27 #include "KernTable.h"
31 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(OpenTypeLayoutEngine
)
33 #define ccmpFeatureTag LE_CCMP_FEATURE_TAG
34 #define ligaFeatureTag LE_LIGA_FEATURE_TAG
35 #define cligFeatureTag LE_CLIG_FEATURE_TAG
36 #define kernFeatureTag LE_KERN_FEATURE_TAG
37 #define markFeatureTag LE_MARK_FEATURE_TAG
38 #define mkmkFeatureTag LE_MKMK_FEATURE_TAG
39 #define loclFeatureTag LE_LOCL_FEATURE_TAG
40 #define caltFeatureTag LE_CALT_FEATURE_TAG
42 #define dligFeatureTag LE_DLIG_FEATURE_TAG
43 #define rligFeatureTag LE_RLIG_FEATURE_TAG
44 #define paltFeatureTag LE_PALT_FEATURE_TAG
46 #define hligFeatureTag LE_HLIG_FEATURE_TAG
47 #define smcpFeatureTag LE_SMCP_FEATURE_TAG
48 #define fracFeatureTag LE_FRAC_FEATURE_TAG
49 #define afrcFeatureTag LE_AFRC_FEATURE_TAG
50 #define zeroFeatureTag LE_ZERO_FEATURE_TAG
51 #define swshFeatureTag LE_SWSH_FEATURE_TAG
52 #define cswhFeatureTag LE_CSWH_FEATURE_TAG
53 #define saltFeatureTag LE_SALT_FEATURE_TAG
54 #define naltFeatureTag LE_NALT_FEATURE_TAG
55 #define rubyFeatureTag LE_RUBY_FEATURE_TAG
56 #define ss01FeatureTag LE_SS01_FEATURE_TAG
57 #define ss02FeatureTag LE_SS02_FEATURE_TAG
58 #define ss03FeatureTag LE_SS03_FEATURE_TAG
59 #define ss04FeatureTag LE_SS04_FEATURE_TAG
60 #define ss05FeatureTag LE_SS05_FEATURE_TAG
61 #define ss06FeatureTag LE_SS06_FEATURE_TAG
62 #define ss07FeatureTag LE_SS07_FEATURE_TAG
64 #define ccmpFeatureMask 0x80000000UL
65 #define ligaFeatureMask 0x40000000UL
66 #define cligFeatureMask 0x20000000UL
67 #define kernFeatureMask 0x10000000UL
68 #define paltFeatureMask 0x08000000UL
69 #define markFeatureMask 0x04000000UL
70 #define mkmkFeatureMask 0x02000000UL
71 #define loclFeatureMask 0x01000000UL
72 #define caltFeatureMask 0x00800000UL
74 #define dligFeatureMask 0x00400000UL
75 #define rligFeatureMask 0x00200000UL
76 #define hligFeatureMask 0x00100000UL
77 #define smcpFeatureMask 0x00080000UL
78 #define fracFeatureMask 0x00040000UL
79 #define afrcFeatureMask 0x00020000UL
80 #define zeroFeatureMask 0x00010000UL
81 #define swshFeatureMask 0x00008000UL
82 #define cswhFeatureMask 0x00004000UL
83 #define saltFeatureMask 0x00002000UL
84 #define naltFeatureMask 0x00001000UL
85 #define rubyFeatureMask 0x00000800UL
86 #define ss01FeatureMask 0x00000400UL
87 #define ss02FeatureMask 0x00000200UL
88 #define ss03FeatureMask 0x00000100UL
89 #define ss04FeatureMask 0x00000080UL
90 #define ss05FeatureMask 0x00000040UL
91 #define ss06FeatureMask 0x00000020UL
92 #define ss07FeatureMask 0x00000010UL
94 #define minimalFeatures (ccmpFeatureMask | markFeatureMask | mkmkFeatureMask | loclFeatureMask | caltFeatureMask)
96 static const FeatureMap featureMap
[] =
98 {ccmpFeatureTag
, ccmpFeatureMask
},
99 {ligaFeatureTag
, ligaFeatureMask
},
100 {cligFeatureTag
, cligFeatureMask
},
101 {kernFeatureTag
, kernFeatureMask
},
102 {paltFeatureTag
, paltFeatureMask
},
103 {markFeatureTag
, markFeatureMask
},
104 {mkmkFeatureTag
, mkmkFeatureMask
},
105 {loclFeatureTag
, loclFeatureMask
},
106 {caltFeatureTag
, caltFeatureMask
},
107 {hligFeatureTag
, hligFeatureMask
},
108 {smcpFeatureTag
, smcpFeatureMask
},
109 {fracFeatureTag
, fracFeatureMask
},
110 {afrcFeatureTag
, afrcFeatureMask
},
111 {zeroFeatureTag
, zeroFeatureMask
},
112 {swshFeatureTag
, swshFeatureMask
},
113 {cswhFeatureTag
, cswhFeatureMask
},
114 {saltFeatureTag
, saltFeatureMask
},
115 {naltFeatureTag
, naltFeatureMask
},
116 {rubyFeatureTag
, rubyFeatureMask
},
117 {ss01FeatureTag
, ss01FeatureMask
},
118 {ss02FeatureTag
, ss02FeatureMask
},
119 {ss03FeatureTag
, ss03FeatureMask
},
120 {ss04FeatureTag
, ss04FeatureMask
},
121 {ss05FeatureTag
, ss05FeatureMask
},
122 {ss06FeatureTag
, ss06FeatureMask
},
123 {ss07FeatureTag
, ss07FeatureMask
}
126 static const le_int32 featureMapCount
= LE_ARRAY_SIZE(featureMap
);
128 OpenTypeLayoutEngine::OpenTypeLayoutEngine(const LEFontInstance
*fontInstance
, le_int32 scriptCode
, le_int32 languageCode
,
129 le_int32 typoFlags
, const LEReferenceTo
<GlyphSubstitutionTableHeader
> &gsubTable
, LEErrorCode
&success
)
130 : LayoutEngine(fontInstance
, scriptCode
, languageCode
, typoFlags
, success
), fFeatureMask(minimalFeatures
),
131 fFeatureMap(featureMap
), fFeatureMapCount(featureMapCount
), fFeatureOrder(FALSE
),
132 fGSUBTable(gsubTable
),
133 fGDEFTable(fontInstance
, LE_GDEF_TABLE_TAG
, success
),
134 fGPOSTable(fontInstance
, LE_GPOS_TABLE_TAG
, success
), fSubstitutionFilter(NULL
)
138 setScriptAndLanguageTags();
140 // JK patch, 2008-05-30 - see Sinhala bug report and LKLUG font
141 // if (gposTable != NULL && gposTable->coversScriptAndLanguage(fScriptTag, fLangSysTag)) {
142 if (!fGPOSTable
.isEmpty()&& !fGPOSTable
->coversScript(fGPOSTable
, fScriptTag
, success
)) {
143 fGPOSTable
.clear(); // already loaded
147 void OpenTypeLayoutEngine::applyTypoFlags() {
148 const le_int32
& typoFlags
= fTypoFlags
;
149 const LEFontInstance
*fontInstance
= fFontInstance
;
151 switch (typoFlags
& (LE_SS01_FEATURE_FLAG
152 | LE_SS02_FEATURE_FLAG
153 | LE_SS03_FEATURE_FLAG
154 | LE_SS04_FEATURE_FLAG
155 | LE_SS05_FEATURE_FLAG
156 | LE_SS06_FEATURE_FLAG
157 | LE_SS07_FEATURE_FLAG
)) {
158 case LE_SS01_FEATURE_FLAG
:
159 fFeatureMask
|= ss01FeatureMask
;
161 case LE_SS02_FEATURE_FLAG
:
162 fFeatureMask
|= ss02FeatureMask
;
164 case LE_SS03_FEATURE_FLAG
:
165 fFeatureMask
|= ss03FeatureMask
;
167 case LE_SS04_FEATURE_FLAG
:
168 fFeatureMask
|= ss04FeatureMask
;
170 case LE_SS05_FEATURE_FLAG
:
171 fFeatureMask
|= ss05FeatureMask
;
173 case LE_SS06_FEATURE_FLAG
:
174 fFeatureMask
|= ss06FeatureMask
;
176 case LE_SS07_FEATURE_FLAG
:
177 fFeatureMask
|= ss07FeatureMask
;
181 if (typoFlags
& LE_Kerning_FEATURE_FLAG
) {
182 fFeatureMask
|= (kernFeatureMask
| paltFeatureMask
);
185 if (typoFlags
& LE_Ligatures_FEATURE_FLAG
) {
186 fFeatureMask
|= (ligaFeatureMask
| cligFeatureMask
);
187 // Convenience TODO: should add: .. dligFeatureMask | rligFeatureMask ?
189 if (typoFlags
& LE_CLIG_FEATURE_FLAG
) fFeatureMask
|= cligFeatureMask
;
190 if (typoFlags
& LE_DLIG_FEATURE_FLAG
) fFeatureMask
|= dligFeatureMask
;
191 if (typoFlags
& LE_HLIG_FEATURE_FLAG
) fFeatureMask
|= hligFeatureMask
;
192 if (typoFlags
& LE_LIGA_FEATURE_FLAG
) fFeatureMask
|= ligaFeatureMask
;
193 if (typoFlags
& LE_RLIG_FEATURE_FLAG
) fFeatureMask
|= rligFeatureMask
;
194 if (typoFlags
& LE_SMCP_FEATURE_FLAG
) fFeatureMask
|= smcpFeatureMask
;
195 if (typoFlags
& LE_FRAC_FEATURE_FLAG
) fFeatureMask
|= fracFeatureMask
;
196 if (typoFlags
& LE_AFRC_FEATURE_FLAG
) fFeatureMask
|= afrcFeatureMask
;
197 if (typoFlags
& LE_ZERO_FEATURE_FLAG
) fFeatureMask
|= zeroFeatureMask
;
198 if (typoFlags
& LE_SWSH_FEATURE_FLAG
) fFeatureMask
|= swshFeatureMask
;
199 if (typoFlags
& LE_CSWH_FEATURE_FLAG
) fFeatureMask
|= cswhFeatureMask
;
200 if (typoFlags
& LE_SALT_FEATURE_FLAG
) fFeatureMask
|= saltFeatureMask
;
201 if (typoFlags
& LE_RUBY_FEATURE_FLAG
) fFeatureMask
|= rubyFeatureMask
;
202 if (typoFlags
& LE_NALT_FEATURE_FLAG
) {
203 // Mutually exclusive with ALL other features. http://www.microsoft.com/typography/otspec/features_ko.htm
204 fFeatureMask
= naltFeatureMask
;
207 if (typoFlags
& LE_CHAR_FILTER_FEATURE_FLAG
) {
208 // This isn't a font feature, but requests a Char Substitution Filter
209 fSubstitutionFilter
= new CharSubstitutionFilter(fontInstance
);
214 void OpenTypeLayoutEngine::reset()
216 // NOTE: if we're called from
217 // the destructor, LayoutEngine;:reset()
218 // will have been called already by
219 // LayoutEngine::~LayoutEngine()
220 LayoutEngine::reset();
223 OpenTypeLayoutEngine::OpenTypeLayoutEngine(const LEFontInstance
*fontInstance
, le_int32 scriptCode
, le_int32 languageCode
,
224 le_int32 typoFlags
, LEErrorCode
&success
)
225 : LayoutEngine(fontInstance
, scriptCode
, languageCode
, typoFlags
, success
), fFeatureOrder(FALSE
),
226 fGSUBTable(), fGDEFTable(), fGPOSTable(), fSubstitutionFilter(NULL
)
229 setScriptAndLanguageTags();
232 OpenTypeLayoutEngine::~OpenTypeLayoutEngine()
234 if (fTypoFlags
& LE_CHAR_FILTER_FEATURE_FLAG
) {
235 delete fSubstitutionFilter
;
236 fSubstitutionFilter
= NULL
;
242 LETag
OpenTypeLayoutEngine::getScriptTag(le_int32 scriptCode
)
244 if (scriptCode
< 0 || scriptCode
>= scriptCodeCount
) {
247 return scriptTags
[scriptCode
];
250 LETag
OpenTypeLayoutEngine::getV2ScriptTag(le_int32 scriptCode
)
252 switch (scriptCode
) {
253 case bengScriptCode
: return bng2ScriptTag
;
254 case devaScriptCode
: return dev2ScriptTag
;
255 case gujrScriptCode
: return gjr2ScriptTag
;
256 case guruScriptCode
: return gur2ScriptTag
;
257 case kndaScriptCode
: return knd2ScriptTag
;
258 case mlymScriptCode
: return mlm2ScriptTag
;
259 case oryaScriptCode
: return ory2ScriptTag
;
260 case tamlScriptCode
: return tml2ScriptTag
;
261 case teluScriptCode
: return tel2ScriptTag
;
262 default: return nullScriptTag
;
266 LETag
OpenTypeLayoutEngine::getLangSysTag(le_int32 languageCode
)
268 if (languageCode
< 0 || languageCode
>= languageCodeCount
) {
272 return languageTags
[languageCode
];
275 void OpenTypeLayoutEngine::setScriptAndLanguageTags()
277 fScriptTag
= getScriptTag(fScriptCode
);
278 fScriptTagV2
= getV2ScriptTag(fScriptCode
);
279 fLangSysTag
= getLangSysTag(fLanguageCode
);
282 le_int32
OpenTypeLayoutEngine::characterProcessing(const LEUnicode chars
[], le_int32 offset
, le_int32 count
, le_int32 max
, le_bool rightToLeft
,
283 LEUnicode
*&outChars
, LEGlyphStorage
&glyphStorage
, LEErrorCode
&success
)
285 if (LE_FAILURE(success
)) {
289 if (offset
< 0 || count
< 0 || max
< 0 || offset
>= max
|| offset
+ count
> max
) {
290 success
= LE_ILLEGAL_ARGUMENT_ERROR
;
294 // This is the cheapest way to get mark reordering only for Hebrew.
295 // We could just do the mark reordering for all scripts, but most
296 // of them probably don't need it... Another option would be to
297 // add a HebrewOpenTypeLayoutEngine subclass, but the only thing it
298 // would need to do is mark reordering, so that seems like overkill.
299 if (fScriptCode
== hebrScriptCode
) {
300 outChars
= LE_NEW_ARRAY(LEUnicode
, count
);
302 if (outChars
== NULL
) {
303 success
= LE_MEMORY_ALLOCATION_ERROR
;
307 if (LE_FAILURE(success
)) {
308 LE_DELETE_ARRAY(outChars
);
312 CanonShaping::reorderMarks(&chars
[offset
], count
, rightToLeft
, outChars
, glyphStorage
);
315 if (LE_FAILURE(success
)) {
319 glyphStorage
.allocateGlyphArray(count
, rightToLeft
, success
);
320 glyphStorage
.allocateAuxData(success
);
322 for (le_int32 i
= 0; i
< count
; i
+= 1) {
323 glyphStorage
.setAuxData(i
, fFeatureMask
, success
);
329 // Input: characters, tags
330 // Output: glyphs, char indices
331 le_int32
OpenTypeLayoutEngine::glyphProcessing(const LEUnicode chars
[], le_int32 offset
, le_int32 count
, le_int32 max
, le_bool rightToLeft
,
332 LEGlyphStorage
&glyphStorage
, LEErrorCode
&success
)
334 if (LE_FAILURE(success
)) {
338 if (chars
== NULL
|| offset
< 0 || count
< 0 || max
< 0 || offset
>= max
|| offset
+ count
> max
) {
339 success
= LE_ILLEGAL_ARGUMENT_ERROR
;
343 mapCharsToGlyphs(chars
, offset
, count
, rightToLeft
, rightToLeft
, glyphStorage
, success
);
345 if (LE_FAILURE(success
)) {
349 if (fGSUBTable
.isValid()) {
350 if (fScriptTagV2
!= nullScriptTag
&& fGSUBTable
->coversScriptAndLanguage(fGSUBTable
, fScriptTagV2
, fLangSysTag
, success
)) {
351 count
= fGSUBTable
->process(fGSUBTable
, glyphStorage
, rightToLeft
, fScriptTagV2
, fLangSysTag
, fGDEFTable
, fSubstitutionFilter
,
352 fFeatureMap
, fFeatureMapCount
, fFeatureOrder
, success
);
355 count
= fGSUBTable
->process(fGSUBTable
, glyphStorage
, rightToLeft
, fScriptTag
, fLangSysTag
, fGDEFTable
, fSubstitutionFilter
,
356 fFeatureMap
, fFeatureMapCount
, fFeatureOrder
, success
);
362 // Input: characters, tags
363 // Output: glyphs, char indices
364 le_int32
OpenTypeLayoutEngine::glyphSubstitution(le_int32 count
, le_int32 max
, le_bool rightToLeft
,
365 LEGlyphStorage
&glyphStorage
, LEErrorCode
&success
)
367 if (LE_FAILURE(success
)) {
371 if ( count
< 0 || max
< 0 ) {
372 success
= LE_ILLEGAL_ARGUMENT_ERROR
;
376 if (fGSUBTable
.isValid()) {
377 if (fScriptTagV2
!= nullScriptTag
&& fGSUBTable
->coversScriptAndLanguage(fGSUBTable
,fScriptTagV2
,fLangSysTag
,success
)) {
378 count
= fGSUBTable
->process(fGSUBTable
, glyphStorage
, rightToLeft
, fScriptTagV2
, fLangSysTag
, fGDEFTable
, fSubstitutionFilter
,
379 fFeatureMap
, fFeatureMapCount
, fFeatureOrder
, success
);
382 count
= fGSUBTable
->process(fGSUBTable
, glyphStorage
, rightToLeft
, fScriptTag
, fLangSysTag
, fGDEFTable
, fSubstitutionFilter
,
383 fFeatureMap
, fFeatureMapCount
, fFeatureOrder
, success
);
389 le_int32
OpenTypeLayoutEngine::glyphPostProcessing(LEGlyphStorage
&tempGlyphStorage
, LEGlyphStorage
&glyphStorage
, LEErrorCode
&success
)
391 if (LE_FAILURE(success
)) {
395 glyphStorage
.adoptGlyphArray(tempGlyphStorage
);
396 glyphStorage
.adoptCharIndicesArray(tempGlyphStorage
);
397 glyphStorage
.adoptAuxDataArray(tempGlyphStorage
);
398 glyphStorage
.adoptGlyphCount(tempGlyphStorage
);
400 return glyphStorage
.getGlyphCount();
403 le_int32
OpenTypeLayoutEngine::computeGlyphs(const LEUnicode chars
[], le_int32 offset
, le_int32 count
, le_int32 max
, le_bool rightToLeft
, LEGlyphStorage
&glyphStorage
, LEErrorCode
&success
)
405 LEUnicode
*outChars
= NULL
;
406 LEGlyphStorage fakeGlyphStorage
;
407 le_int32 outCharCount
, outGlyphCount
;
409 if (LE_FAILURE(success
)) {
413 if (chars
== NULL
|| offset
< 0 || count
< 0 || max
< 0 || offset
>= max
|| offset
+ count
> max
) {
414 success
= LE_ILLEGAL_ARGUMENT_ERROR
;
418 outCharCount
= characterProcessing(chars
, offset
, count
, max
, rightToLeft
, outChars
, fakeGlyphStorage
, success
);
420 if (LE_FAILURE(success
)) {
424 if (outChars
!= NULL
) {
425 // le_int32 fakeGlyphCount =
426 glyphProcessing(outChars
, 0, outCharCount
, outCharCount
, rightToLeft
, fakeGlyphStorage
, success
);
427 LE_DELETE_ARRAY(outChars
); // FIXME: a subclass may have allocated this, in which case this delete might not work...
428 //adjustGlyphs(outChars, 0, outCharCount, rightToLeft, fakeGlyphs, fakeGlyphCount);
430 // le_int32 fakeGlyphCount =
431 glyphProcessing(chars
, offset
, count
, max
, rightToLeft
, fakeGlyphStorage
, success
);
432 //adjustGlyphs(chars, offset, count, rightToLeft, fakeGlyphs, fakeGlyphCount);
435 if (LE_FAILURE(success
)) {
439 outGlyphCount
= glyphPostProcessing(fakeGlyphStorage
, glyphStorage
, success
);
441 return outGlyphCount
;
444 // apply GPOS table, if any
445 void OpenTypeLayoutEngine::adjustGlyphPositions(const LEUnicode chars
[], le_int32 offset
, le_int32 count
, le_bool reverse
,
446 LEGlyphStorage
&glyphStorage
, LEErrorCode
&success
)
448 if (LE_FAILURE(success
)) {
452 if (chars
== NULL
|| offset
< 0 || count
< 0) {
453 success
= LE_ILLEGAL_ARGUMENT_ERROR
;
457 le_int32 glyphCount
= glyphStorage
.getGlyphCount();
458 if (glyphCount
== 0) {
462 if (!fGPOSTable
.isEmpty()) {
463 GlyphPositionAdjustments
*adjustments
= new GlyphPositionAdjustments(glyphCount
);
466 if (adjustments
== NULL
) {
467 success
= LE_MEMORY_ALLOCATION_ERROR
;
472 // Don't need to do this if we allocate
473 // the adjustments array w/ new...
474 for (i
= 0; i
< glyphCount
; i
+= 1) {
475 adjustments
->setXPlacement(i
, 0);
476 adjustments
->setYPlacement(i
, 0);
478 adjustments
->setXAdvance(i
, 0);
479 adjustments
->setYAdvance(i
, 0);
481 adjustments
->setBaseOffset(i
, -1);
485 if (!fGPOSTable
.isEmpty()) {
486 if (fScriptTagV2
!= nullScriptTag
&&
487 fGPOSTable
->coversScriptAndLanguage(fGPOSTable
, fScriptTagV2
,fLangSysTag
,success
)) {
488 fGPOSTable
->process(fGPOSTable
, glyphStorage
, adjustments
, reverse
, fScriptTagV2
, fLangSysTag
,
489 fGDEFTable
, success
, fFontInstance
, fFeatureMap
, fFeatureMapCount
, fFeatureOrder
);
492 fGPOSTable
->process(fGPOSTable
, glyphStorage
, adjustments
, reverse
, fScriptTag
, fLangSysTag
,
493 fGDEFTable
, success
, fFontInstance
, fFeatureMap
, fFeatureMapCount
, fFeatureOrder
);
495 } else if (fTypoFlags
& LE_Kerning_FEATURE_FLAG
) { /* kerning enabled */
496 LETableReference
kernTable(fFontInstance
, LE_KERN_TABLE_TAG
, success
);
497 KernTable
kt(kernTable
, success
);
498 kt
.process(glyphStorage
, success
);
501 float xAdjust
= 0, yAdjust
= 0;
503 for (i
= 0; i
< glyphCount
; i
+= 1) {
504 float xAdvance
= adjustments
->getXAdvance(i
);
505 float yAdvance
= adjustments
->getYAdvance(i
);
506 float xPlacement
= 0;
507 float yPlacement
= 0;
511 // This is where separate kerning adjustments
512 // should get applied.
517 for (le_int32 base
= i
; base
>= 0; base
= adjustments
->getBaseOffset(base
)) {
518 xPlacement
+= adjustments
->getXPlacement(base
);
519 yPlacement
+= adjustments
->getYPlacement(base
);
522 xPlacement
= fFontInstance
->xUnitsToPoints(xPlacement
);
523 yPlacement
= fFontInstance
->yUnitsToPoints(yPlacement
);
524 glyphStorage
.adjustPosition(i
, xAdjust
+ xPlacement
, -(yAdjust
+ yPlacement
), success
);
526 xAdjust
+= fFontInstance
->xUnitsToPoints(xAdvance
);
527 yAdjust
+= fFontInstance
->yUnitsToPoints(yAdvance
);
530 glyphStorage
.adjustPosition(glyphCount
, xAdjust
, -yAdjust
, success
);
534 // if there was no GPOS table, maybe there's non-OpenType kerning we can use
535 LayoutEngine::adjustGlyphPositions(chars
, offset
, count
, reverse
, glyphStorage
, success
);
538 LEGlyphID zwnj
= fFontInstance
->mapCharToGlyph(0x200C);
540 if (zwnj
!= 0x0000) {
541 for (le_int32 g
= 0; g
< glyphCount
; g
+= 1) {
542 LEGlyphID glyph
= glyphStorage
[g
];
545 glyphStorage
[g
] = LE_SET_GLYPH(glyph
, 0xFFFF);
551 // Don't know why this is here...
552 LE_DELETE_ARRAY(fFeatureTags
);