4 * (C) Copyright IBM Corp. 1998-2010 - 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 // 'dlig' not used at the moment
43 #define dligFeatureTag 0x646C6967
46 #define paltFeatureTag 0x70616C74
48 #define ccmpFeatureMask 0x80000000UL
49 #define ligaFeatureMask 0x40000000UL
50 #define cligFeatureMask 0x20000000UL
51 #define kernFeatureMask 0x10000000UL
52 #define paltFeatureMask 0x08000000UL
53 #define markFeatureMask 0x04000000UL
54 #define mkmkFeatureMask 0x02000000UL
55 #define loclFeatureMask 0x01000000UL
56 #define caltFeatureMask 0x00800000UL
58 #define minimalFeatures (ccmpFeatureMask | markFeatureMask | mkmkFeatureMask | loclFeatureMask | caltFeatureMask)
59 #define ligaFeatures (ligaFeatureMask | cligFeatureMask | minimalFeatures)
60 #define kernFeatures (kernFeatureMask | paltFeatureMask | minimalFeatures)
61 #define kernAndLigaFeatures (ligaFeatures | kernFeatures)
63 static const FeatureMap featureMap
[] =
65 {ccmpFeatureTag
, ccmpFeatureMask
},
66 {ligaFeatureTag
, ligaFeatureMask
},
67 {cligFeatureTag
, cligFeatureMask
},
68 {kernFeatureTag
, kernFeatureMask
},
69 {paltFeatureTag
, paltFeatureMask
},
70 {markFeatureTag
, markFeatureMask
},
71 {mkmkFeatureTag
, mkmkFeatureMask
},
72 {loclFeatureTag
, loclFeatureMask
},
73 {caltFeatureTag
, caltFeatureMask
}
76 static const le_int32 featureMapCount
= LE_ARRAY_SIZE(featureMap
);
78 OpenTypeLayoutEngine::OpenTypeLayoutEngine(const LEFontInstance
*fontInstance
, le_int32 scriptCode
, le_int32 languageCode
,
79 le_int32 typoFlags
, const GlyphSubstitutionTableHeader
*gsubTable
, LEErrorCode
&success
)
80 : LayoutEngine(fontInstance
, scriptCode
, languageCode
, typoFlags
, success
), fFeatureMask(minimalFeatures
),
81 fFeatureMap(featureMap
), fFeatureMapCount(featureMapCount
), fFeatureOrder(FALSE
),
82 fGSUBTable(gsubTable
), fGDEFTable(NULL
), fGPOSTable(NULL
), fSubstitutionFilter(NULL
)
84 static const le_uint32 gdefTableTag
= LE_GDEF_TABLE_TAG
;
85 static const le_uint32 gposTableTag
= LE_GPOS_TABLE_TAG
;
86 const GlyphPositioningTableHeader
*gposTable
= (const GlyphPositioningTableHeader
*) getFontTable(gposTableTag
);
88 // todo: switch to more flags and bitfield rather than list of feature tags?
89 switch (typoFlags
& ~0x80000000L
) {
90 case 0: break; // default
91 case 1: fFeatureMask
= kernFeatures
; break;
92 case 2: fFeatureMask
= ligaFeatures
; break;
93 case 3: fFeatureMask
= kernAndLigaFeatures
; break;
97 if (typoFlags
& 0x80000000L
) {
98 fSubstitutionFilter
= new CharSubstitutionFilter(fontInstance
);
101 setScriptAndLanguageTags();
103 fGDEFTable
= (const GlyphDefinitionTableHeader
*) getFontTable(gdefTableTag
);
105 // JK patch, 2008-05-30 - see Sinhala bug report and LKLUG font
106 // if (gposTable != NULL && gposTable->coversScriptAndLanguage(fScriptTag, fLangSysTag)) {
107 if (gposTable
!= NULL
&& gposTable
->coversScript(fScriptTag
)) {
108 fGPOSTable
= gposTable
;
112 void OpenTypeLayoutEngine::reset()
114 // NOTE: if we're called from
115 // the destructor, LayoutEngine;:reset()
116 // will have been called already by
117 // LayoutEngine::~LayoutEngine()
118 LayoutEngine::reset();
121 OpenTypeLayoutEngine::OpenTypeLayoutEngine(const LEFontInstance
*fontInstance
, le_int32 scriptCode
, le_int32 languageCode
,
122 le_int32 typoFlags
, LEErrorCode
&success
)
123 : LayoutEngine(fontInstance
, scriptCode
, languageCode
, typoFlags
, success
), fFeatureOrder(FALSE
),
124 fGSUBTable(NULL
), fGDEFTable(NULL
), fGPOSTable(NULL
), fSubstitutionFilter(NULL
)
126 setScriptAndLanguageTags();
129 OpenTypeLayoutEngine::~OpenTypeLayoutEngine()
131 if (fTypoFlags
& 0x80000000L
) {
132 delete fSubstitutionFilter
;
138 LETag
OpenTypeLayoutEngine::getScriptTag(le_int32 scriptCode
)
140 if (scriptCode
< 0 || scriptCode
>= scriptCodeCount
) {
143 return scriptTags
[scriptCode
];
146 LETag
OpenTypeLayoutEngine::getV2ScriptTag(le_int32 scriptCode
)
148 switch (scriptCode
) {
149 case bengScriptCode
: return bng2ScriptTag
;
150 case devaScriptCode
: return dev2ScriptTag
;
151 case gujrScriptCode
: return gjr2ScriptTag
;
152 case guruScriptCode
: return gur2ScriptTag
;
153 case kndaScriptCode
: return knd2ScriptTag
;
154 case mlymScriptCode
: return mlm2ScriptTag
;
155 case oryaScriptCode
: return ory2ScriptTag
;
156 case tamlScriptCode
: return tml2ScriptTag
;
157 case teluScriptCode
: return tel2ScriptTag
;
158 default: return nullScriptTag
;
162 LETag
OpenTypeLayoutEngine::getLangSysTag(le_int32 languageCode
)
164 if (languageCode
< 0 || languageCode
>= languageCodeCount
) {
168 return languageTags
[languageCode
];
171 void OpenTypeLayoutEngine::setScriptAndLanguageTags()
173 fScriptTag
= getScriptTag(fScriptCode
);
174 fScriptTagV2
= getV2ScriptTag(fScriptCode
);
175 fLangSysTag
= getLangSysTag(fLanguageCode
);
178 le_int32
OpenTypeLayoutEngine::characterProcessing(const LEUnicode chars
[], le_int32 offset
, le_int32 count
, le_int32 max
, le_bool rightToLeft
,
179 LEUnicode
*&outChars
, LEGlyphStorage
&glyphStorage
, LEErrorCode
&success
)
181 if (LE_FAILURE(success
)) {
185 if (offset
< 0 || count
< 0 || max
< 0 || offset
>= max
|| offset
+ count
> max
) {
186 success
= LE_ILLEGAL_ARGUMENT_ERROR
;
190 // This is the cheapest way to get mark reordering only for Hebrew.
191 // We could just do the mark reordering for all scripts, but most
192 // of them probably don't need it... Another option would be to
193 // add a HebrewOpenTypeLayoutEngine subclass, but the only thing it
194 // would need to do is mark reordering, so that seems like overkill.
195 if (fScriptCode
== hebrScriptCode
) {
196 outChars
= LE_NEW_ARRAY(LEUnicode
, count
);
198 if (outChars
== NULL
) {
199 success
= LE_MEMORY_ALLOCATION_ERROR
;
203 if (LE_FAILURE(success
)) {
204 LE_DELETE_ARRAY(outChars
);
208 CanonShaping::reorderMarks(&chars
[offset
], count
, rightToLeft
, outChars
, glyphStorage
);
211 if (LE_FAILURE(success
)) {
215 glyphStorage
.allocateGlyphArray(count
, rightToLeft
, success
);
216 glyphStorage
.allocateAuxData(success
);
218 for (le_int32 i
= 0; i
< count
; i
+= 1) {
219 glyphStorage
.setAuxData(i
, fFeatureMask
, success
);
225 // Input: characters, tags
226 // Output: glyphs, char indices
227 le_int32
OpenTypeLayoutEngine::glyphProcessing(const LEUnicode chars
[], le_int32 offset
, le_int32 count
, le_int32 max
, le_bool rightToLeft
,
228 LEGlyphStorage
&glyphStorage
, LEErrorCode
&success
)
230 if (LE_FAILURE(success
)) {
234 if (chars
== NULL
|| offset
< 0 || count
< 0 || max
< 0 || offset
>= max
|| offset
+ count
> max
) {
235 success
= LE_ILLEGAL_ARGUMENT_ERROR
;
239 mapCharsToGlyphs(chars
, offset
, count
, rightToLeft
, rightToLeft
, glyphStorage
, success
);
241 if (LE_FAILURE(success
)) {
245 if (fGSUBTable
!= NULL
) {
246 if (fScriptTagV2
!= nullScriptTag
&& fGSUBTable
->coversScriptAndLanguage(fScriptTagV2
,fLangSysTag
)) {
247 count
= fGSUBTable
->process(glyphStorage
, rightToLeft
, fScriptTagV2
, fLangSysTag
, fGDEFTable
, fSubstitutionFilter
,
248 fFeatureMap
, fFeatureMapCount
, fFeatureOrder
, success
);
251 count
= fGSUBTable
->process(glyphStorage
, rightToLeft
, fScriptTag
, fLangSysTag
, fGDEFTable
, fSubstitutionFilter
,
252 fFeatureMap
, fFeatureMapCount
, fFeatureOrder
, success
);
258 // Input: characters, tags
259 // Output: glyphs, char indices
260 le_int32
OpenTypeLayoutEngine::glyphSubstitution(le_int32 count
, le_int32 max
, le_bool rightToLeft
,
261 LEGlyphStorage
&glyphStorage
, LEErrorCode
&success
)
263 if (LE_FAILURE(success
)) {
267 if ( count
< 0 || max
< 0 ) {
268 success
= LE_ILLEGAL_ARGUMENT_ERROR
;
272 if (fGSUBTable
!= NULL
) {
273 if (fScriptTagV2
!= nullScriptTag
&& fGSUBTable
->coversScriptAndLanguage(fScriptTagV2
,fLangSysTag
)) {
274 count
= fGSUBTable
->process(glyphStorage
, rightToLeft
, fScriptTagV2
, fLangSysTag
, fGDEFTable
, fSubstitutionFilter
,
275 fFeatureMap
, fFeatureMapCount
, fFeatureOrder
, success
);
278 count
= fGSUBTable
->process(glyphStorage
, rightToLeft
, fScriptTag
, fLangSysTag
, fGDEFTable
, fSubstitutionFilter
,
279 fFeatureMap
, fFeatureMapCount
, fFeatureOrder
, success
);
285 le_int32
OpenTypeLayoutEngine::glyphPostProcessing(LEGlyphStorage
&tempGlyphStorage
, LEGlyphStorage
&glyphStorage
, LEErrorCode
&success
)
287 if (LE_FAILURE(success
)) {
291 glyphStorage
.adoptGlyphArray(tempGlyphStorage
);
292 glyphStorage
.adoptCharIndicesArray(tempGlyphStorage
);
293 glyphStorage
.adoptAuxDataArray(tempGlyphStorage
);
294 glyphStorage
.adoptGlyphCount(tempGlyphStorage
);
296 return glyphStorage
.getGlyphCount();
299 le_int32
OpenTypeLayoutEngine::computeGlyphs(const LEUnicode chars
[], le_int32 offset
, le_int32 count
, le_int32 max
, le_bool rightToLeft
, LEGlyphStorage
&glyphStorage
, LEErrorCode
&success
)
301 LEUnicode
*outChars
= NULL
;
302 LEGlyphStorage fakeGlyphStorage
;
303 le_int32 outCharCount
, outGlyphCount
, fakeGlyphCount
;
305 if (LE_FAILURE(success
)) {
309 if (chars
== NULL
|| offset
< 0 || count
< 0 || max
< 0 || offset
>= max
|| offset
+ count
> max
) {
310 success
= LE_ILLEGAL_ARGUMENT_ERROR
;
314 outCharCount
= characterProcessing(chars
, offset
, count
, max
, rightToLeft
, outChars
, fakeGlyphStorage
, success
);
316 if (LE_FAILURE(success
)) {
320 if (outChars
!= NULL
) {
321 fakeGlyphCount
= glyphProcessing(outChars
, 0, outCharCount
, outCharCount
, rightToLeft
, fakeGlyphStorage
, success
);
322 LE_DELETE_ARRAY(outChars
); // FIXME: a subclass may have allocated this, in which case this delete might not work...
323 //adjustGlyphs(outChars, 0, outCharCount, rightToLeft, fakeGlyphs, fakeGlyphCount);
325 fakeGlyphCount
= glyphProcessing(chars
, offset
, count
, max
, rightToLeft
, fakeGlyphStorage
, success
);
326 //adjustGlyphs(chars, offset, count, rightToLeft, fakeGlyphs, fakeGlyphCount);
329 if (LE_FAILURE(success
)) {
333 outGlyphCount
= glyphPostProcessing(fakeGlyphStorage
, glyphStorage
, success
);
335 return outGlyphCount
;
338 // apply GPOS table, if any
339 void OpenTypeLayoutEngine::adjustGlyphPositions(const LEUnicode chars
[], le_int32 offset
, le_int32 count
, le_bool reverse
,
340 LEGlyphStorage
&glyphStorage
, LEErrorCode
&success
)
342 if (LE_FAILURE(success
)) {
346 if (chars
== NULL
|| offset
< 0 || count
< 0) {
347 success
= LE_ILLEGAL_ARGUMENT_ERROR
;
351 le_int32 glyphCount
= glyphStorage
.getGlyphCount();
352 if (glyphCount
== 0) {
356 if (fGPOSTable
!= NULL
) {
357 GlyphPositionAdjustments
*adjustments
= new GlyphPositionAdjustments(glyphCount
);
360 if (adjustments
== NULL
) {
361 success
= LE_MEMORY_ALLOCATION_ERROR
;
366 // Don't need to do this if we allocate
367 // the adjustments array w/ new...
368 for (i
= 0; i
< glyphCount
; i
+= 1) {
369 adjustments
->setXPlacement(i
, 0);
370 adjustments
->setYPlacement(i
, 0);
372 adjustments
->setXAdvance(i
, 0);
373 adjustments
->setYAdvance(i
, 0);
375 adjustments
->setBaseOffset(i
, -1);
379 if (fGPOSTable
!= NULL
) {
380 if (fScriptTagV2
!= nullScriptTag
&& fGPOSTable
->coversScriptAndLanguage(fScriptTagV2
,fLangSysTag
)) {
381 fGPOSTable
->process(glyphStorage
, adjustments
, reverse
, fScriptTagV2
, fLangSysTag
, fGDEFTable
, success
, fFontInstance
,
382 fFeatureMap
, fFeatureMapCount
, fFeatureOrder
);
385 fGPOSTable
->process(glyphStorage
, adjustments
, reverse
, fScriptTag
, fLangSysTag
, fGDEFTable
, success
, fFontInstance
,
386 fFeatureMap
, fFeatureMapCount
, fFeatureOrder
);
388 } else if ( fTypoFlags
& 0x1 ) {
389 static const le_uint32 kernTableTag
= LE_KERN_TABLE_TAG
;
390 KernTable
kt(fFontInstance
, getFontTable(kernTableTag
));
391 kt
.process(glyphStorage
);
394 float xAdjust
= 0, yAdjust
= 0;
396 for (i
= 0; i
< glyphCount
; i
+= 1) {
397 float xAdvance
= adjustments
->getXAdvance(i
);
398 float yAdvance
= adjustments
->getYAdvance(i
);
399 float xPlacement
= 0;
400 float yPlacement
= 0;
404 // This is where separate kerning adjustments
405 // should get applied.
410 for (le_int32 base
= i
; base
>= 0; base
= adjustments
->getBaseOffset(base
)) {
411 xPlacement
+= adjustments
->getXPlacement(base
);
412 yPlacement
+= adjustments
->getYPlacement(base
);
415 xPlacement
= fFontInstance
->xUnitsToPoints(xPlacement
);
416 yPlacement
= fFontInstance
->yUnitsToPoints(yPlacement
);
417 glyphStorage
.adjustPosition(i
, xAdjust
+ xPlacement
, -(yAdjust
+ yPlacement
), success
);
419 xAdjust
+= fFontInstance
->xUnitsToPoints(xAdvance
);
420 yAdjust
+= fFontInstance
->yUnitsToPoints(yAdvance
);
423 glyphStorage
.adjustPosition(glyphCount
, xAdjust
, -yAdjust
, success
);
427 // if there was no GPOS table, maybe there's non-OpenType kerning we can use
428 LayoutEngine::adjustGlyphPositions(chars
, offset
, count
, reverse
, glyphStorage
, success
);
431 LEGlyphID zwnj
= fFontInstance
->mapCharToGlyph(0x200C);
433 if (zwnj
!= 0x0000) {
434 for (le_int32 g
= 0; g
< glyphCount
; g
+= 1) {
435 LEGlyphID glyph
= glyphStorage
[g
];
438 glyphStorage
[g
] = LE_SET_GLYPH(glyph
, 0xFFFF);
444 // Don't know why this is here...
445 LE_DELETE_ARRAY(fFeatureTags
);