1
2/*
3 *
4 * (C) Copyright IBM Corp. 1998-2010 - All Rights Reserved
5 *
6 */
7
8#include "LETypes.h"
9#include "LEScripts.h"
10#include "LELanguages.h"
11
12#include "LayoutEngine.h"
13#include "CanonShaping.h"
14#include "OpenTypeLayoutEngine.h"
15#include "ScriptAndLanguageTags.h"
16#include "CharSubstitutionFilter.h"
17
18#include "GlyphSubstitutionTables.h"
19#include "GlyphDefinitionTables.h"
20#include "GlyphPositioningTables.h"
21
22#include "LEGlyphStorage.h"
23#include "GlyphPositionAdjustments.h"
24
25#include "GDEFMarkFilter.h"
26
27#include "KernTable.h"
28
29U_NAMESPACE_BEGIN
30
31UOBJECT_DEFINE_RTTI_IMPLEMENTATION(OpenTypeLayoutEngine)
32
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
41
42// 'dlig' not used at the moment
43#define dligFeatureTag 0x646C6967
44
45// 'palt'
46#define paltFeatureTag 0x70616C74
47
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
57
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)
62
63static const FeatureMap featureMap[] =
64{
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}
74};
75
76static const le_int32 featureMapCount = LE_ARRAY_SIZE(featureMap);
77
78OpenTypeLayoutEngine::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)
83{
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);
87
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;
94    default: break;
95    }
96
97    if (typoFlags & 0x80000000L) {
98        fSubstitutionFilter = new CharSubstitutionFilter(fontInstance);
99    }
100
101    setScriptAndLanguageTags();
102
103    fGDEFTable = (const GlyphDefinitionTableHeader *) getFontTable(gdefTableTag);
104
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;
109    }
110}
111
112void OpenTypeLayoutEngine::reset()
113{
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();
119}
120
121OpenTypeLayoutEngine::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)
125{
126    setScriptAndLanguageTags();
127}
128
129OpenTypeLayoutEngine::~OpenTypeLayoutEngine()
130{
131    if (fTypoFlags & 0x80000000L) {
132        delete fSubstitutionFilter;
133    }
134
135    reset();
136}
137
138LETag OpenTypeLayoutEngine::getScriptTag(le_int32 scriptCode)
139{
140    if (scriptCode < 0 || scriptCode >= scriptCodeCount) {
141        return 0xFFFFFFFF;
142    }
143    return scriptTags[scriptCode];
144}
145
146LETag OpenTypeLayoutEngine::getV2ScriptTag(le_int32 scriptCode)
147{
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;
159	}
160}
161
162LETag OpenTypeLayoutEngine::getLangSysTag(le_int32 languageCode)
163{
164    if (languageCode < 0 || languageCode >= languageCodeCount) {
165        return 0xFFFFFFFF;
166    }
167
168    return languageTags[languageCode];
169}
170
171void OpenTypeLayoutEngine::setScriptAndLanguageTags()
172{
173    fScriptTag  = getScriptTag(fScriptCode);
174    fScriptTagV2 = getV2ScriptTag(fScriptCode);
175    fLangSysTag = getLangSysTag(fLanguageCode);
176}
177
178le_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)
180{
181    if (LE_FAILURE(success)) {
182        return 0;
183    }
184
185    if (offset < 0 || count < 0 || max < 0 || offset >= max || offset + count > max) {
186        success = LE_ILLEGAL_ARGUMENT_ERROR;
187        return 0;
188    }
189
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);
197
198        if (outChars == NULL) {
199            success = LE_MEMORY_ALLOCATION_ERROR;
200            return 0;
201        }
202
203        if (LE_FAILURE(success)) {
204            LE_DELETE_ARRAY(outChars);
205            return 0;
206        }
207
208        CanonShaping::reorderMarks(&chars[offset], count, rightToLeft, outChars, glyphStorage);
209    }
210
211    if (LE_FAILURE(success)) {
212        return 0;
213    }
214
215    glyphStorage.allocateGlyphArray(count, rightToLeft, success);
216    glyphStorage.allocateAuxData(success);
217
218    for (le_int32 i = 0; i < count; i += 1) {
219        glyphStorage.setAuxData(i, fFeatureMask, success);
220    }
221
222    return count;
223}
224
225// Input: characters, tags
226// Output: glyphs, char indices
227le_int32 OpenTypeLayoutEngine::glyphProcessing(const LEUnicode chars[], le_int32 offset, le_int32 count, le_int32 max, le_bool rightToLeft,
228                                               LEGlyphStorage &glyphStorage, LEErrorCode &success)
229{
230    if (LE_FAILURE(success)) {
231        return 0;
232    }
233
234    if (chars == NULL || offset < 0 || count < 0 || max < 0 || offset >= max || offset + count > max) {
235        success = LE_ILLEGAL_ARGUMENT_ERROR;
236        return 0;
237    }
238
239    mapCharsToGlyphs(chars, offset, count, rightToLeft, rightToLeft, glyphStorage, success);
240
241    if (LE_FAILURE(success)) {
242        return 0;
243    }
244
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);
249
250        } else {
251        count = fGSUBTable->process(glyphStorage, rightToLeft, fScriptTag, fLangSysTag, fGDEFTable, fSubstitutionFilter,
252                                    fFeatureMap, fFeatureMapCount, fFeatureOrder, success);
253        }
254    }
255
256    return count;
257}
258// Input: characters, tags
259// Output: glyphs, char indices
260le_int32 OpenTypeLayoutEngine::glyphSubstitution(le_int32 count, le_int32 max, le_bool rightToLeft,
261                                               LEGlyphStorage &glyphStorage, LEErrorCode &success)
262{
263    if (LE_FAILURE(success)) {
264        return 0;
265    }
266
267    if ( count < 0 || max < 0 ) {
268        success = LE_ILLEGAL_ARGUMENT_ERROR;
269        return 0;
270    }
271
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);
276
277        } else {
278        count = fGSUBTable->process(glyphStorage, rightToLeft, fScriptTag, fLangSysTag, fGDEFTable, fSubstitutionFilter,
279                                    fFeatureMap, fFeatureMapCount, fFeatureOrder, success);
280        }
281    }
282
283    return count;
284}
285le_int32 OpenTypeLayoutEngine::glyphPostProcessing(LEGlyphStorage &tempGlyphStorage, LEGlyphStorage &glyphStorage, LEErrorCode &success)
286{
287    if (LE_FAILURE(success)) {
288        return 0;
289    }
290
291    glyphStorage.adoptGlyphArray(tempGlyphStorage);
292    glyphStorage.adoptCharIndicesArray(tempGlyphStorage);
293    glyphStorage.adoptAuxDataArray(tempGlyphStorage);
294    glyphStorage.adoptGlyphCount(tempGlyphStorage);
295
296    return glyphStorage.getGlyphCount();
297}
298
299le_int32 OpenTypeLayoutEngine::computeGlyphs(const LEUnicode chars[], le_int32 offset, le_int32 count, le_int32 max, le_bool rightToLeft, LEGlyphStorage &glyphStorage, LEErrorCode &success)
300{
301    LEUnicode *outChars = NULL;
302    LEGlyphStorage fakeGlyphStorage;
303    le_int32 outCharCount, outGlyphCount, fakeGlyphCount;
304
305    if (LE_FAILURE(success)) {
306        return 0;
307    }
308
309    if (chars == NULL || offset < 0 || count < 0 || max < 0 || offset >= max || offset + count > max) {
310        success = LE_ILLEGAL_ARGUMENT_ERROR;
311        return 0;
312    }
313
314    outCharCount = characterProcessing(chars, offset, count, max, rightToLeft, outChars, fakeGlyphStorage, success);
315
316    if (LE_FAILURE(success)) {
317        return 0;
318    }
319
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);
324    } else {
325        fakeGlyphCount = glyphProcessing(chars, offset, count, max, rightToLeft, fakeGlyphStorage, success);
326        //adjustGlyphs(chars, offset, count, rightToLeft, fakeGlyphs, fakeGlyphCount);
327    }
328
329    if (LE_FAILURE(success)) {
330        return 0;
331    }
332
333    outGlyphCount = glyphPostProcessing(fakeGlyphStorage, glyphStorage, success);
334
335    return outGlyphCount;
336}
337
338// apply GPOS table, if any
339void OpenTypeLayoutEngine::adjustGlyphPositions(const LEUnicode chars[], le_int32 offset, le_int32 count, le_bool reverse,
340                                                LEGlyphStorage &glyphStorage, LEErrorCode &success)
341{
342    if (LE_FAILURE(success)) {
343        return;
344    }
345
346    if (chars == NULL || offset < 0 || count < 0) {
347        success = LE_ILLEGAL_ARGUMENT_ERROR;
348        return;
349    }
350
351    le_int32 glyphCount = glyphStorage.getGlyphCount();
352    if (glyphCount == 0) {
353        return;
354    }
355
356    if (fGPOSTable != NULL) {
357        GlyphPositionAdjustments *adjustments = new GlyphPositionAdjustments(glyphCount);
358        le_int32 i;
359
360        if (adjustments == NULL) {
361            success = LE_MEMORY_ALLOCATION_ERROR;
362            return;
363        }
364
365#if 0
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);
371
372            adjustments->setXAdvance(i, 0);
373            adjustments->setYAdvance(i, 0);
374
375            adjustments->setBaseOffset(i, -1);
376        }
377#endif
378
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);
383
384            } else {
385                fGPOSTable->process(glyphStorage, adjustments, reverse, fScriptTag, fLangSysTag, fGDEFTable, success, fFontInstance,
386                                fFeatureMap, fFeatureMapCount, fFeatureOrder);
387            }
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);
392        }
393
394        float xAdjust = 0, yAdjust = 0;
395
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;
401
402
403#if 0
404            // This is where separate kerning adjustments
405            // should get applied.
406            xAdjust += xKerning;
407            yAdjust += yKerning;
408#endif
409
410            for (le_int32 base = i; base >= 0; base = adjustments->getBaseOffset(base)) {
411                xPlacement += adjustments->getXPlacement(base);
412                yPlacement += adjustments->getYPlacement(base);
413            }
414
415            xPlacement = fFontInstance->xUnitsToPoints(xPlacement);
416            yPlacement = fFontInstance->yUnitsToPoints(yPlacement);
417            glyphStorage.adjustPosition(i, xAdjust + xPlacement, -(yAdjust + yPlacement), success);
418
419            xAdjust += fFontInstance->xUnitsToPoints(xAdvance);
420            yAdjust += fFontInstance->yUnitsToPoints(yAdvance);
421        }
422
423        glyphStorage.adjustPosition(glyphCount, xAdjust, -yAdjust, success);
424
425        delete adjustments;
426    } else {
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);
429    }
430
431    LEGlyphID zwnj  = fFontInstance->mapCharToGlyph(0x200C);
432
433    if (zwnj != 0x0000) {
434        for (le_int32 g = 0; g < glyphCount; g += 1) {
435            LEGlyphID glyph = glyphStorage[g];
436
437            if (glyph == zwnj) {
438                glyphStorage[g] = LE_SET_GLYPH(glyph, 0xFFFF);
439            }
440        }
441    }
442
443#if 0
444    // Don't know why this is here...
445    LE_DELETE_ARRAY(fFeatureTags);
446    fFeatureTags = NULL;
447#endif
448}
449
450U_NAMESPACE_END
451