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 GlyphSubstitutionTableHeader
*gsubTable
, LEErrorCode
&success
)
130 : LayoutEngine(fontInstance
, scriptCode
, languageCode
, typoFlags
, success
), fFeatureMask(minimalFeatures
),
131 fFeatureMap(featureMap
), fFeatureMapCount(featureMapCount
), fFeatureOrder(FALSE
),
132 fGSUBTable(gsubTable
), fGDEFTable(NULL
), fGPOSTable(NULL
), fSubstitutionFilter(NULL
)
134 static const le_uint32 gdefTableTag
= LE_GDEF_TABLE_TAG
;
135 static const le_uint32 gposTableTag
= LE_GPOS_TABLE_TAG
;
136 const GlyphPositioningTableHeader
*gposTable
= (const GlyphPositioningTableHeader
*) getFontTable(gposTableTag
);
138 switch (typoFlags
& (LE_SS01_FEATURE_FLAG
139 | LE_SS02_FEATURE_FLAG
140 | LE_SS03_FEATURE_FLAG
141 | LE_SS04_FEATURE_FLAG
142 | LE_SS05_FEATURE_FLAG
143 | LE_SS06_FEATURE_FLAG
144 | LE_SS07_FEATURE_FLAG
)) {
145 case LE_SS01_FEATURE_FLAG
:
146 fFeatureMask
|= ss01FeatureMask
;
148 case LE_SS02_FEATURE_FLAG
:
149 fFeatureMask
|= ss02FeatureMask
;
151 case LE_SS03_FEATURE_FLAG
:
152 fFeatureMask
|= ss03FeatureMask
;
154 case LE_SS04_FEATURE_FLAG
:
155 fFeatureMask
|= ss04FeatureMask
;
157 case LE_SS05_FEATURE_FLAG
:
158 fFeatureMask
|= ss05FeatureMask
;
160 case LE_SS06_FEATURE_FLAG
:
161 fFeatureMask
|= ss06FeatureMask
;
163 case LE_SS07_FEATURE_FLAG
:
164 fFeatureMask
|= ss07FeatureMask
;
168 if (typoFlags
& LE_Kerning_FEATURE_FLAG
) {
169 fFeatureMask
|= (kernFeatureMask
| paltFeatureMask
);
172 if (typoFlags
& LE_Ligatures_FEATURE_FLAG
) {
173 fFeatureMask
|= (ligaFeatureMask
| cligFeatureMask
);
174 // Convenience TODO: should add: .. dligFeatureMask | rligFeatureMask ?
176 if (typoFlags
& LE_CLIG_FEATURE_FLAG
) fFeatureMask
|= cligFeatureMask
;
177 if (typoFlags
& LE_DLIG_FEATURE_FLAG
) fFeatureMask
|= dligFeatureMask
;
178 if (typoFlags
& LE_HLIG_FEATURE_FLAG
) fFeatureMask
|= hligFeatureMask
;
179 if (typoFlags
& LE_LIGA_FEATURE_FLAG
) fFeatureMask
|= ligaFeatureMask
;
180 if (typoFlags
& LE_RLIG_FEATURE_FLAG
) fFeatureMask
|= rligFeatureMask
;
181 if (typoFlags
& LE_SMCP_FEATURE_FLAG
) fFeatureMask
|= smcpFeatureMask
;
182 if (typoFlags
& LE_FRAC_FEATURE_FLAG
) fFeatureMask
|= fracFeatureMask
;
183 if (typoFlags
& LE_AFRC_FEATURE_FLAG
) fFeatureMask
|= afrcFeatureMask
;
184 if (typoFlags
& LE_ZERO_FEATURE_FLAG
) fFeatureMask
|= zeroFeatureMask
;
185 if (typoFlags
& LE_SWSH_FEATURE_FLAG
) fFeatureMask
|= swshFeatureMask
;
186 if (typoFlags
& LE_CSWH_FEATURE_FLAG
) fFeatureMask
|= cswhFeatureMask
;
187 if (typoFlags
& LE_SALT_FEATURE_FLAG
) fFeatureMask
|= saltFeatureMask
;
188 if (typoFlags
& LE_RUBY_FEATURE_FLAG
) fFeatureMask
|= rubyFeatureMask
;
189 if (typoFlags
& LE_NALT_FEATURE_FLAG
) {
190 // Mutually exclusive with ALL other features. http://www.microsoft.com/typography/otspec/features_ko.htm
191 fFeatureMask
= naltFeatureMask
;
194 if (typoFlags
& LE_CHAR_FILTER_FEATURE_FLAG
) {
195 // This isn't a font feature, but requests a Char Substitution Filter
196 fSubstitutionFilter
= new CharSubstitutionFilter(fontInstance
);
199 setScriptAndLanguageTags();
201 fGDEFTable
= (const GlyphDefinitionTableHeader
*) getFontTable(gdefTableTag
);
203 // JK patch, 2008-05-30 - see Sinhala bug report and LKLUG font
204 // if (gposTable != NULL && gposTable->coversScriptAndLanguage(fScriptTag, fLangSysTag)) {
205 if (gposTable
!= NULL
&& gposTable
->coversScript(fScriptTag
)) {
206 fGPOSTable
= gposTable
;
210 void OpenTypeLayoutEngine::reset()
212 // NOTE: if we're called from
213 // the destructor, LayoutEngine;:reset()
214 // will have been called already by
215 // LayoutEngine::~LayoutEngine()
216 LayoutEngine::reset();
219 OpenTypeLayoutEngine::OpenTypeLayoutEngine(const LEFontInstance
*fontInstance
, le_int32 scriptCode
, le_int32 languageCode
,
220 le_int32 typoFlags
, LEErrorCode
&success
)
221 : LayoutEngine(fontInstance
, scriptCode
, languageCode
, typoFlags
, success
), fFeatureOrder(FALSE
),
222 fGSUBTable(NULL
), fGDEFTable(NULL
), fGPOSTable(NULL
), fSubstitutionFilter(NULL
)
224 setScriptAndLanguageTags();
227 OpenTypeLayoutEngine::~OpenTypeLayoutEngine()
229 if (fTypoFlags
& 0x80000000L
) {
230 delete fSubstitutionFilter
;
236 LETag
OpenTypeLayoutEngine::getScriptTag(le_int32 scriptCode
)
238 if (scriptCode
< 0 || scriptCode
>= scriptCodeCount
) {
241 return scriptTags
[scriptCode
];
244 LETag
OpenTypeLayoutEngine::getV2ScriptTag(le_int32 scriptCode
)
246 switch (scriptCode
) {
247 case bengScriptCode
: return bng2ScriptTag
;
248 case devaScriptCode
: return dev2ScriptTag
;
249 case gujrScriptCode
: return gjr2ScriptTag
;
250 case guruScriptCode
: return gur2ScriptTag
;
251 case kndaScriptCode
: return knd2ScriptTag
;
252 case mlymScriptCode
: return mlm2ScriptTag
;
253 case oryaScriptCode
: return ory2ScriptTag
;
254 case tamlScriptCode
: return tml2ScriptTag
;
255 case teluScriptCode
: return tel2ScriptTag
;
256 default: return nullScriptTag
;
260 LETag
OpenTypeLayoutEngine::getLangSysTag(le_int32 languageCode
)
262 if (languageCode
< 0 || languageCode
>= languageCodeCount
) {
266 return languageTags
[languageCode
];
269 void OpenTypeLayoutEngine::setScriptAndLanguageTags()
271 fScriptTag
= getScriptTag(fScriptCode
);
272 fScriptTagV2
= getV2ScriptTag(fScriptCode
);
273 fLangSysTag
= getLangSysTag(fLanguageCode
);
276 le_int32
OpenTypeLayoutEngine::characterProcessing(const LEUnicode chars
[], le_int32 offset
, le_int32 count
, le_int32 max
, le_bool rightToLeft
,
277 LEUnicode
*&outChars
, LEGlyphStorage
&glyphStorage
, LEErrorCode
&success
)
279 if (LE_FAILURE(success
)) {
283 if (offset
< 0 || count
< 0 || max
< 0 || offset
>= max
|| offset
+ count
> max
) {
284 success
= LE_ILLEGAL_ARGUMENT_ERROR
;
288 // This is the cheapest way to get mark reordering only for Hebrew.
289 // We could just do the mark reordering for all scripts, but most
290 // of them probably don't need it... Another option would be to
291 // add a HebrewOpenTypeLayoutEngine subclass, but the only thing it
292 // would need to do is mark reordering, so that seems like overkill.
293 if (fScriptCode
== hebrScriptCode
) {
294 outChars
= LE_NEW_ARRAY(LEUnicode
, count
);
296 if (outChars
== NULL
) {
297 success
= LE_MEMORY_ALLOCATION_ERROR
;
301 if (LE_FAILURE(success
)) {
302 LE_DELETE_ARRAY(outChars
);
306 CanonShaping::reorderMarks(&chars
[offset
], count
, rightToLeft
, outChars
, glyphStorage
);
309 if (LE_FAILURE(success
)) {
313 glyphStorage
.allocateGlyphArray(count
, rightToLeft
, success
);
314 glyphStorage
.allocateAuxData(success
);
316 for (le_int32 i
= 0; i
< count
; i
+= 1) {
317 glyphStorage
.setAuxData(i
, fFeatureMask
, success
);
323 // Input: characters, tags
324 // Output: glyphs, char indices
325 le_int32
OpenTypeLayoutEngine::glyphProcessing(const LEUnicode chars
[], le_int32 offset
, le_int32 count
, le_int32 max
, le_bool rightToLeft
,
326 LEGlyphStorage
&glyphStorage
, LEErrorCode
&success
)
328 if (LE_FAILURE(success
)) {
332 if (chars
== NULL
|| offset
< 0 || count
< 0 || max
< 0 || offset
>= max
|| offset
+ count
> max
) {
333 success
= LE_ILLEGAL_ARGUMENT_ERROR
;
337 mapCharsToGlyphs(chars
, offset
, count
, rightToLeft
, rightToLeft
, glyphStorage
, success
);
339 if (LE_FAILURE(success
)) {
343 if (fGSUBTable
!= NULL
) {
344 if (fScriptTagV2
!= nullScriptTag
&& fGSUBTable
->coversScriptAndLanguage(fScriptTagV2
,fLangSysTag
)) {
345 count
= fGSUBTable
->process(glyphStorage
, rightToLeft
, fScriptTagV2
, fLangSysTag
, fGDEFTable
, fSubstitutionFilter
,
346 fFeatureMap
, fFeatureMapCount
, fFeatureOrder
, success
);
349 count
= fGSUBTable
->process(glyphStorage
, rightToLeft
, fScriptTag
, fLangSysTag
, fGDEFTable
, fSubstitutionFilter
,
350 fFeatureMap
, fFeatureMapCount
, fFeatureOrder
, success
);
356 // Input: characters, tags
357 // Output: glyphs, char indices
358 le_int32
OpenTypeLayoutEngine::glyphSubstitution(le_int32 count
, le_int32 max
, le_bool rightToLeft
,
359 LEGlyphStorage
&glyphStorage
, LEErrorCode
&success
)
361 if (LE_FAILURE(success
)) {
365 if ( count
< 0 || max
< 0 ) {
366 success
= LE_ILLEGAL_ARGUMENT_ERROR
;
370 if (fGSUBTable
!= NULL
) {
371 if (fScriptTagV2
!= nullScriptTag
&& fGSUBTable
->coversScriptAndLanguage(fScriptTagV2
,fLangSysTag
)) {
372 count
= fGSUBTable
->process(glyphStorage
, rightToLeft
, fScriptTagV2
, fLangSysTag
, fGDEFTable
, fSubstitutionFilter
,
373 fFeatureMap
, fFeatureMapCount
, fFeatureOrder
, success
);
376 count
= fGSUBTable
->process(glyphStorage
, rightToLeft
, fScriptTag
, fLangSysTag
, fGDEFTable
, fSubstitutionFilter
,
377 fFeatureMap
, fFeatureMapCount
, fFeatureOrder
, success
);
383 le_int32
OpenTypeLayoutEngine::glyphPostProcessing(LEGlyphStorage
&tempGlyphStorage
, LEGlyphStorage
&glyphStorage
, LEErrorCode
&success
)
385 if (LE_FAILURE(success
)) {
389 glyphStorage
.adoptGlyphArray(tempGlyphStorage
);
390 glyphStorage
.adoptCharIndicesArray(tempGlyphStorage
);
391 glyphStorage
.adoptAuxDataArray(tempGlyphStorage
);
392 glyphStorage
.adoptGlyphCount(tempGlyphStorage
);
394 return glyphStorage
.getGlyphCount();
397 le_int32
OpenTypeLayoutEngine::computeGlyphs(const LEUnicode chars
[], le_int32 offset
, le_int32 count
, le_int32 max
, le_bool rightToLeft
, LEGlyphStorage
&glyphStorage
, LEErrorCode
&success
)
399 LEUnicode
*outChars
= NULL
;
400 LEGlyphStorage fakeGlyphStorage
;
401 le_int32 outCharCount
, outGlyphCount
;
403 if (LE_FAILURE(success
)) {
407 if (chars
== NULL
|| offset
< 0 || count
< 0 || max
< 0 || offset
>= max
|| offset
+ count
> max
) {
408 success
= LE_ILLEGAL_ARGUMENT_ERROR
;
412 outCharCount
= characterProcessing(chars
, offset
, count
, max
, rightToLeft
, outChars
, fakeGlyphStorage
, success
);
414 if (LE_FAILURE(success
)) {
418 if (outChars
!= NULL
) {
419 // le_int32 fakeGlyphCount =
420 glyphProcessing(outChars
, 0, outCharCount
, outCharCount
, rightToLeft
, fakeGlyphStorage
, success
);
421 LE_DELETE_ARRAY(outChars
); // FIXME: a subclass may have allocated this, in which case this delete might not work...
422 //adjustGlyphs(outChars, 0, outCharCount, rightToLeft, fakeGlyphs, fakeGlyphCount);
424 // le_int32 fakeGlyphCount =
425 glyphProcessing(chars
, offset
, count
, max
, rightToLeft
, fakeGlyphStorage
, success
);
426 //adjustGlyphs(chars, offset, count, rightToLeft, fakeGlyphs, fakeGlyphCount);
429 if (LE_FAILURE(success
)) {
433 outGlyphCount
= glyphPostProcessing(fakeGlyphStorage
, glyphStorage
, success
);
435 return outGlyphCount
;
438 // apply GPOS table, if any
439 void OpenTypeLayoutEngine::adjustGlyphPositions(const LEUnicode chars
[], le_int32 offset
, le_int32 count
, le_bool reverse
,
440 LEGlyphStorage
&glyphStorage
, LEErrorCode
&success
)
442 if (LE_FAILURE(success
)) {
446 if (chars
== NULL
|| offset
< 0 || count
< 0) {
447 success
= LE_ILLEGAL_ARGUMENT_ERROR
;
451 le_int32 glyphCount
= glyphStorage
.getGlyphCount();
452 if (glyphCount
== 0) {
456 if (fGPOSTable
!= NULL
) {
457 GlyphPositionAdjustments
*adjustments
= new GlyphPositionAdjustments(glyphCount
);
460 if (adjustments
== NULL
) {
461 success
= LE_MEMORY_ALLOCATION_ERROR
;
466 // Don't need to do this if we allocate
467 // the adjustments array w/ new...
468 for (i
= 0; i
< glyphCount
; i
+= 1) {
469 adjustments
->setXPlacement(i
, 0);
470 adjustments
->setYPlacement(i
, 0);
472 adjustments
->setXAdvance(i
, 0);
473 adjustments
->setYAdvance(i
, 0);
475 adjustments
->setBaseOffset(i
, -1);
479 if (fGPOSTable
!= NULL
) {
480 if (fScriptTagV2
!= nullScriptTag
&& fGPOSTable
->coversScriptAndLanguage(fScriptTagV2
,fLangSysTag
)) {
481 fGPOSTable
->process(glyphStorage
, adjustments
, reverse
, fScriptTagV2
, fLangSysTag
, fGDEFTable
, success
, fFontInstance
,
482 fFeatureMap
, fFeatureMapCount
, fFeatureOrder
);
485 fGPOSTable
->process(glyphStorage
, adjustments
, reverse
, fScriptTag
, fLangSysTag
, fGDEFTable
, success
, fFontInstance
,
486 fFeatureMap
, fFeatureMapCount
, fFeatureOrder
);
488 } else if ( fTypoFlags
& 0x1 ) {
489 static const le_uint32 kernTableTag
= LE_KERN_TABLE_TAG
;
490 KernTable
kt(fFontInstance
, getFontTable(kernTableTag
));
491 kt
.process(glyphStorage
);
494 float xAdjust
= 0, yAdjust
= 0;
496 for (i
= 0; i
< glyphCount
; i
+= 1) {
497 float xAdvance
= adjustments
->getXAdvance(i
);
498 float yAdvance
= adjustments
->getYAdvance(i
);
499 float xPlacement
= 0;
500 float yPlacement
= 0;
504 // This is where separate kerning adjustments
505 // should get applied.
510 for (le_int32 base
= i
; base
>= 0; base
= adjustments
->getBaseOffset(base
)) {
511 xPlacement
+= adjustments
->getXPlacement(base
);
512 yPlacement
+= adjustments
->getYPlacement(base
);
515 xPlacement
= fFontInstance
->xUnitsToPoints(xPlacement
);
516 yPlacement
= fFontInstance
->yUnitsToPoints(yPlacement
);
517 glyphStorage
.adjustPosition(i
, xAdjust
+ xPlacement
, -(yAdjust
+ yPlacement
), success
);
519 xAdjust
+= fFontInstance
->xUnitsToPoints(xAdvance
);
520 yAdjust
+= fFontInstance
->yUnitsToPoints(yAdvance
);
523 glyphStorage
.adjustPosition(glyphCount
, xAdjust
, -yAdjust
, success
);
527 // if there was no GPOS table, maybe there's non-OpenType kerning we can use
528 LayoutEngine::adjustGlyphPositions(chars
, offset
, count
, reverse
, glyphStorage
, success
);
531 LEGlyphID zwnj
= fFontInstance
->mapCharToGlyph(0x200C);
533 if (zwnj
!= 0x0000) {
534 for (le_int32 g
= 0; g
< glyphCount
; g
+= 1) {
535 LEGlyphID glyph
= glyphStorage
[g
];
538 glyphStorage
[g
] = LE_SET_GLYPH(glyph
, 0xFFFF);
544 // Don't know why this is here...
545 LE_DELETE_ARRAY(fFeatureTags
);