sl@0: /* sl@0: * Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: * All rights reserved. sl@0: * This component and the accompanying materials are made available sl@0: * under the terms of "Eclipse Public License v1.0" sl@0: * which accompanies this distribution, and is available sl@0: * at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: * sl@0: * Initial Contributors: sl@0: * Nokia Corporation - initial contribution. sl@0: * sl@0: * Contributors: sl@0: * sl@0: * Description: sl@0: * Implementation of CIcuLayoutEngine sl@0: * This is a CShaper using the Icu Layout Engine. sl@0: * sl@0: */ sl@0: sl@0: // System includes sl@0: #include <e32base.h> sl@0: #include <gdi.h> sl@0: #include <e32math.h> sl@0: #include <openfont.h> sl@0: #include <fntstore.h> sl@0: sl@0: // ICU includes sl@0: #include "math.h" sl@0: #include "LETypes.h" sl@0: #include "LEScripts.h" sl@0: #include "LELanguages.h" sl@0: #include "LayoutEngine.h" sl@0: #include "SymbianFontInstance.h" sl@0: sl@0: #include "IcuLayoutEngine.h" sl@0: sl@0: // Icu namespace sl@0: U_NAMESPACE_USE sl@0: sl@0: // Convert from float to int rounding to the nearest integer sl@0: inline TInt FloatToInt(float a) sl@0: { sl@0: return a < 0? static_cast<TInt>(a - 0.5) : static_cast<TInt>(a + 0.5); sl@0: } sl@0: sl@0: /** Array of Script Codes that need to be translated into LEScriptCodes for the sl@0: construction of the LayoutEngine. */ sl@0: const LETag scriptCodes[] = { sl@0: zyyyScriptTag, /* 'zyyy' (COMMON) */ sl@0: qaaiScriptTag, /* 'qaai' (INHERITED) */ sl@0: arabScriptTag, /* 'arab' (ARABIC) */ sl@0: armnScriptTag, /* 'armn' (ARMENIAN) */ sl@0: bengScriptTag, /* 'beng' (BENGALI) */ sl@0: bopoScriptTag, /* 'bopo' (BOPOMOFO) */ sl@0: cherScriptTag, /* 'cher' (CHEROKEE) */ sl@0: qaacScriptTag, /* 'qaac' (COPTIC) */ sl@0: cyrlScriptTag, /* 'cyrl' (CYRILLIC) */ sl@0: dsrtScriptTag, /* 'dsrt' (DESERET) */ sl@0: devaScriptTag, /* 'deva' (DEVANAGARI) */ sl@0: ethiScriptTag, /* 'ethi' (ETHIOPIC) */ sl@0: georScriptTag, /* 'geor' (GEORGIAN) */ sl@0: gothScriptTag, /* 'goth' (GOTHIC) */ sl@0: grekScriptTag, /* 'grek' (GREEK) */ sl@0: gujrScriptTag, /* 'gujr' (GUJARATI) */ sl@0: guruScriptTag, /* 'guru' (GURMUKHI) */ sl@0: haniScriptTag, /* 'hani' (HAN) */ sl@0: hangScriptTag, /* 'hang' (HANGUL) */ sl@0: hebrScriptTag, /* 'hebr' (HEBREW) */ sl@0: hiraScriptTag, /* 'hira' (HIRAGANA) */ sl@0: kndaScriptTag, /* 'knda' (KANNADA) */ sl@0: kanaScriptTag, /* 'kana' (KATAKANA) */ sl@0: khmrScriptTag, /* 'khmr' (KHMER) */ sl@0: laooScriptTag, /* 'laoo' (LAO) */ sl@0: latnScriptTag, /* 'latn' (LATIN) */ sl@0: mlymScriptTag, /* 'mlym' (MALAYALAM) */ sl@0: mongScriptTag, /* 'mong' (MONGOLIAN) */ sl@0: mymrScriptTag, /* 'mymr' (MYANMAR) */ sl@0: ogamScriptTag, /* 'ogam' (OGHAM) */ sl@0: italScriptTag, /* 'ital' (OLD_ITALIC) */ sl@0: oryaScriptTag, /* 'orya' (ORIYA) */ sl@0: runrScriptTag, /* 'runr' (RUNIC) */ sl@0: sinhScriptTag, /* 'sinh' (SINHALA) */ sl@0: syrcScriptTag, /* 'syrc' (SYRIAC) */ sl@0: tamlScriptTag, /* 'taml' (TAMIL) */ sl@0: teluScriptTag, /* 'telu' (TELUGU) */ sl@0: thaaScriptTag, /* 'thaa' (THAANA) */ sl@0: thaiScriptTag, /* 'thai' (THAI) */ sl@0: tibtScriptTag, /* 'tibt' (TIBETAN) */ sl@0: cansScriptTag, /* 'cans' (CANADIAN_ABORIGINAL) */ sl@0: yiiiScriptTag, /* 'yiii' (YI) */ sl@0: tglgScriptTag, /* 'tglg' (TAGALOG) */ sl@0: hanoScriptTag, /* 'hano' (HANUNOO) */ sl@0: buhdScriptTag, /* 'buhd' (BUHID) */ sl@0: tagbScriptTag, /* 'tagb' (TAGBANWA) */ sl@0: braiScriptTag, /* 'brai' (BRAILLE) */ sl@0: cprtScriptTag, /* 'cprt' (CYPRIOT) */ sl@0: limbScriptTag, /* 'limb' (LIMBU) */ sl@0: linbScriptTag, /* 'linb' (LINEAR_B) */ sl@0: osmaScriptTag, /* 'osma' (OSMANYA) */ sl@0: shawScriptTag, /* 'shaw' (SHAVIAN) */ sl@0: taleScriptTag, /* 'tale' (TAI_LE) */ sl@0: ugarScriptTag, /* 'ugar' (UGARITIC) */ sl@0: hrktScriptTag /* 'hrkt' (KATAKANA_OR_HIRAGANA) */ sl@0: }; sl@0: sl@0: /** Array of Language Codes that need to be translated into LELanguageCodes for the sl@0: construction of the LayoutEngine. */ sl@0: const LETag languageCodes[] = { sl@0: nullLanguageTag, /* '' (null) */ sl@0: araLanguageTag, /* 'ARA' (Arabic) */ sl@0: asmLanguageTag, /* 'ASM' (Assamese) */ sl@0: benLanguageTag, /* 'BEN' (Bengali) */ sl@0: farLanguageTag, /* 'FAR' (Farsi) */ sl@0: gujLanguageTag, /* 'GUJ' (Gujarati) */ sl@0: hinLanguageTag, /* 'HIN' (Hindi) */ sl@0: iwrLanguageTag, /* 'IWR' (Hebrew) */ sl@0: jiiLanguageTag, /* 'JII' (Yiddish) */ sl@0: janLanguageTag, /* 'JAN' (Japanese) */ sl@0: kanLanguageTag, /* 'KAN' (Kannada) */ sl@0: kokLanguageTag, /* 'KOK' (Konkani) */ sl@0: korLanguageTag, /* 'KOR' (Korean) */ sl@0: kshLanguageTag, /* 'KSH' (Kashmiri) */ sl@0: malLanguageTag, /* 'MAL' (Malayalam (Traditional)) */ sl@0: marLanguageTag, /* 'MAR' (Marathi) */ sl@0: mlrLanguageTag, /* 'MLR' (Malayalam (Reformed)) */ sl@0: mniLanguageTag, /* 'MNI' (Manipuri) */ sl@0: oriLanguageTag, /* 'ORI' (Oriya) */ sl@0: sanLanguageTag, /* 'SAN' (Sanscrit) */ sl@0: sndLanguageTag, /* 'SND' (Sindhi) */ sl@0: snhLanguageTag, /* 'SNH' (Sinhalese) */ sl@0: syrLanguageTag, /* 'SYR' (Syriac) */ sl@0: tamLanguageTag, /* 'TAM' (Tamil) */ sl@0: telLanguageTag, /* 'TEL' (Telugu) */ sl@0: thaLanguageTag, /* 'THA' (Thai) */ sl@0: urdLanguageTag, /* 'URD' (Urdu) */ sl@0: zhpLanguageTag, /* 'ZHP' (Chinese (Phonetic)) */ sl@0: zhsLanguageTag, /* 'ZHS' (Chinese (Simplified)) */ sl@0: zhtLanguageTag /* 'ZHT' (Chinese (Traditional)) */ sl@0: }; sl@0: sl@0: /** sl@0: Translate the script code passed in to a LEScriptCode sl@0: */ sl@0: TInt ScriptCode(TUint32 aScript) sl@0: { sl@0: TInt scriptCode = -1; sl@0: for (TInt i= 0; i < scriptCodeCount; i++) sl@0: { sl@0: if (scriptCodes[i] == aScript) sl@0: { sl@0: scriptCode = i; sl@0: break; sl@0: } sl@0: } sl@0: return scriptCode; sl@0: } sl@0: sl@0: /** sl@0: Translate the language code passed in to a LELanguageCode sl@0: */ sl@0: TInt LanguageCode(TUint32 aLanguage) sl@0: { sl@0: TInt languageCode = -1; sl@0: for (TInt i= 0; i < languageCodeCount; i++) sl@0: { sl@0: if (languageCodes[i] == aLanguage) sl@0: { sl@0: languageCode = i; sl@0: break; sl@0: } sl@0: } sl@0: return languageCode; sl@0: } sl@0: sl@0: /** sl@0: Create a instance of CIcuLayoutEngine sl@0: @param aBitmapFont The required font. sl@0: @param aHeap The heap to be used for storage by the engine. sl@0: @return A pointer to the new CIcuLayoutEngine instance. sl@0: */ sl@0: CShaper * CIcuLayoutEngine::NewL(CBitmapFont* aBitmapFont, TInt aScript, TInt aLanguage, RHeap* aHeap) sl@0: { sl@0: CIcuLayoutEngine* newShaper = new(ELeave)CIcuLayoutEngine(aScript, aLanguage); sl@0: CleanupStack::PushL(newShaper); sl@0: newShaper->ConstructL(aBitmapFont, aScript, aLanguage, aHeap); sl@0: CleanupStack::Pop(); // newShaper sl@0: return newShaper; sl@0: } sl@0: sl@0: sl@0: /** sl@0: Construct an instance of CIcuLayoutEngine sl@0: @param aBitMapFont The required font sl@0: @param aHeap The heap to be used for storage by the engine sl@0: @return incase of exceptions with an Error Code sl@0: @see CShaper sl@0: */ sl@0: TInt CIcuLayoutEngine::ConstructL(CBitmapFont* aBitMapFont, TInt aScript, TInt aLanguage, RHeap* aHeap ) sl@0: { sl@0: // allocate a block of memory from aHeap for the layout engine sl@0: // which is accessed via the Umemory class sl@0: iClientHeap = aHeap; sl@0: sl@0: // this needs to be on the heap sl@0: LEErrorCode fontStatus = LE_NO_ERROR; sl@0: iFontInstance = new SymbianFontInstance(aBitMapFont, fontStatus, aScript == mlymScriptTag); sl@0: if(NULL == iFontInstance) sl@0: { sl@0: User::Leave(KErrGeneral); sl@0: } sl@0: if (fontStatus == LE_MEMORY_ALLOCATION_ERROR) sl@0: { sl@0: User::Leave(KErrNoMemory); sl@0: } sl@0: else if (fontStatus != LE_NO_ERROR) sl@0: { sl@0: //note this leave may actually be caused by OOM situations, sl@0: //due to the way errors codes are handled in the open source code sl@0: User::Leave(KErrGeneral); sl@0: } sl@0: sl@0: sl@0: // create and initialise a ICU layout Engine sl@0: // Note the engine is created using the Umemory heap sl@0: LEErrorCode success = LE_NO_ERROR; sl@0: sl@0: // Translate the script code into an LEScriptCode sl@0: TInt scriptCode = ScriptCode(aScript); sl@0: sl@0: // Translate the language code into an LELanguageCode sl@0: TInt languageCode = LanguageCode(aLanguage); sl@0: sl@0: // Finally instantiate the LayoutEngine sl@0: iEngine = LayoutEngine::layoutEngineFactory(iFontInstance, scriptCode, languageCode, success); sl@0: sl@0: // For debugging - check the total memory used by construction sl@0: #ifdef SHAPER_MEMORY_DEBUG sl@0: TInt totalAllocSize = 0; sl@0: TInt used = iHeap->AllocSize(totalAllocSize); sl@0: RDebug::Print(_L("shaper construction %d cells %d"), totalAllocSize, used); sl@0: #endif sl@0: sl@0: if (success == LE_MEMORY_ALLOCATION_ERROR) sl@0: { sl@0: User::Leave(KErrNoMemory); sl@0: } sl@0: else if (success != LE_NO_ERROR) sl@0: { sl@0: //note this leave may actually be caused by OOM situations, sl@0: //due to the way errors codes are handled in the open source code sl@0: User::Leave(KErrGeneral); sl@0: } sl@0: return KErrNone; sl@0: } sl@0: sl@0: CIcuLayoutEngine::CIcuLayoutEngine(TUint32 aScript, TUint32 aLanguage): sl@0: iScript(aScript), sl@0: iLanguage(aLanguage) sl@0: { sl@0: } sl@0: sl@0: /** sl@0: Frees all resources owned by ... sl@0: */ sl@0: CIcuLayoutEngine::~CIcuLayoutEngine() sl@0: { sl@0: // delete the engine instance sl@0: if ( iEngine ) sl@0: { sl@0: delete( iEngine ); sl@0: } sl@0: sl@0: // delete the font instance from client heap sl@0: delete( iFontInstance ); sl@0: } sl@0: sl@0: /** sl@0: Returns the script and the language this shaper has been instansiated with. sl@0: */ sl@0: void* CIcuLayoutEngine::ExtendedInterface (TUid aInterfaceId) sl@0: { sl@0: if (aInterfaceId == KUidShaperGetScript) sl@0: return (TUint32*)iScript; sl@0: else sl@0: if (aInterfaceId == KUidShaperGetLang) sl@0: return (TUint32*)iLanguage; sl@0: else sl@0: return CShaper::ExtendedInterface(aInterfaceId); sl@0: } sl@0: sl@0: /** This is implementation of CShaper::ShapeText for the Icu layout Engine sl@0: The data is taken from TInput and pass to the shaper. sl@0: A memory buffer is allocated on aHeapForOutput starting with TShapeHeader is allocated. sl@0: The results of the shaping are copied into this buffer and passed back via aOutput. sl@0: @param aOutput On success a new structure containing the results allocated on aHeapForOutput. sl@0: @param aInput The input text and other parameters. sl@0: @param aHeapForOutput On success, aOutput should be allocated from this and nothing else. sl@0: On failure, nothing should be allocated from it. sl@0: @return Error value from one of the system-wide error codes on failure, KErrNone on success. sl@0: @see CShaper::ShapeText sl@0: */ sl@0: TInt CIcuLayoutEngine::ShapeText(TShapeHeader*& aOutput, const TInput& aInput, RHeap* aHeapForOutput) sl@0: { sl@0: // For debugging - the heap before shaping sl@0: TInt totalAllocSize = 0; sl@0: TInt used; sl@0: #ifdef SHAPER_MEMORY_DEBUG sl@0: used = User::Heap().AllocSize(totalAllocSize); sl@0: RDebug::Print(_L("before shaping %d cells %d bytes used"), used, totalAllocSize); sl@0: #endif sl@0: sl@0: iFontInstance->SetSessionHandle(aInput.iSessionHandle); sl@0: TRAPD( error, IcuShapeTextL(aOutput, aInput, aHeapForOutput)); sl@0: if (error == KErrNoMemory) sl@0: { sl@0: used = User::Heap().AllocSize(totalAllocSize); sl@0: RDebug::Print(_L("shaper out of memory %d cells %d"), totalAllocSize, used); sl@0: } sl@0: sl@0: #ifdef SHAPER_MEMORY_DEBUG sl@0: used = User::Heap().AllocSize(totalAllocSize); sl@0: RDebug::Print(_L("Shaped %d characters %d glyphs "), aOutput->iCharacterCount, aOutput->iGlyphCount ); sl@0: RDebug::Print(_L("after shaping %d cells %d bytes used"), used, totalAllocSize); sl@0: #endif sl@0: sl@0: // hide the ICU error codes as KErrGeneral sl@0: if ((error == KErrNoMemory) || (error == KErrNone)) sl@0: return error; sl@0: else sl@0: return KErrGeneral; sl@0: } sl@0: sl@0: /** This calls the ICU Layout engine to shape the text. It allocates memory for the results sl@0: and then reads the results. This memory is freed by the caller. sl@0: This function can leave if OOM. sl@0: @param aOutput On success a new structure containing the results allocated on aHeapForOutput. sl@0: @param aInput The input text and other parameters. sl@0: @param aHeapForOutput On success, aOutput should be allocated from this and nothing else. sl@0: On failure, nothing should be allocated from it. sl@0: @return Error value from one of the system-wide error codes on failure, KErrNone on success. sl@0: @see CIcuLayoutEngine::ShapeText sl@0: */ sl@0: void CIcuLayoutEngine::IcuShapeTextL(TShapeHeader*& aOutput, const TInput& aInput, RHeap* aHeapForOutput) sl@0: { sl@0: LEErrorCode success = LE_NO_ERROR; sl@0: const LEUnicode * p = (LEUnicode *)aInput.iText->Ptr(); sl@0: TInt noChars = aInput.iEnd - aInput.iStart; sl@0: sl@0: // Call to layout sl@0: TInt noOfGlyphs = iEngine->layoutChars( sl@0: p, // chars - the input character context sl@0: aInput.iStart, // the offset of the first character to process sl@0: noChars, // count - the number of characters to process // offset sl@0: aInput.iText->Length(), // max - the number of characters in the input context // size of text sl@0: FALSE, // rightToLeft - TRUE if the characters are in a right to left directional run sl@0: 0, // start X sl@0: 0, // start Y sl@0: success); // result code sl@0: sl@0: if (success == LE_MEMORY_ALLOCATION_ERROR) sl@0: User::Leave(KErrNoMemory); sl@0: else if (success != LE_NO_ERROR) sl@0: { sl@0: User::Leave(KErrGeneral); sl@0: } sl@0: sl@0: sl@0: // Get some memory to pass into the layout engine for the results sl@0: TInt bufferSize = (sizeof(LEGlyphID) + sizeof(le_int32) + sizeof(float) * 2) sl@0: * noOfGlyphs + sizeof(float) * 2; sl@0: TUint8* buffer = reinterpret_cast<TUint8*>( User::AllocL(bufferSize) ); sl@0: CleanupStack::PushL(buffer); sl@0: LEGlyphID* glyphBuffer = reinterpret_cast<LEGlyphID*>(buffer); sl@0: le_int32* indexBuffer = reinterpret_cast<le_int32*>(glyphBuffer + noOfGlyphs); sl@0: float* positionBuffer = reinterpret_cast<float*>(indexBuffer + noOfGlyphs); sl@0: sl@0: sl@0: // now get results glyph codes, positions and indices sl@0: // from the layout engine sl@0: if (success == LE_NO_ERROR) sl@0: iEngine->getGlyphs(glyphBuffer, success); sl@0: sl@0: if (success == LE_NO_ERROR) sl@0: iEngine->getGlyphPositions(positionBuffer, success); sl@0: sl@0: if (success == LE_NO_ERROR) sl@0: iEngine->getCharIndices(indexBuffer, aInput.iStart, success); sl@0: if (success == LE_NO_ERROR) sl@0: // Reset the memory used by the IcuLayoutEngine sl@0: iEngine->reset(); sl@0: if (success == LE_MEMORY_ALLOCATION_ERROR) sl@0: User::Leave(KErrNoMemory); sl@0: else sl@0: if (success != LE_NO_ERROR) sl@0: { sl@0: User::Leave(KErrGeneral); sl@0: } sl@0: sl@0: // Some of the glyph codes are 0xFFFF and 0x0001. 0xFFFF is a value used by ICU layout sl@0: // engine to indicate no glyph, and 0x0001 is used to indicate a ZWJ or a ZWNJ. sl@0: // These should be stripped out now. A ZWJ or a ZWNJ glyph has no meaning here. Their sl@0: // effects on the precedig and proceding characters have already been taken into sl@0: // consideration during shaping, so we don't need them anymore. sl@0: // Also, their presence in the final glyph list was causing GDI to break with GDI:1 sl@0: // i.e. more than 8 glyphs in a glyph cluster. sl@0: LEGlyphID gidOfZWJ = iFontInstance->mapCharToGlyph(0x200D); // Added by Symbian: 1922 mlyl sl@0: TInt actualNoOfGlyphs = 0; sl@0: for (LEGlyphID* p = glyphBuffer + noOfGlyphs; p != glyphBuffer;) sl@0: { sl@0: --p; sl@0: if (*p != 0xFFFF && *p != 0x0001 && *p != gidOfZWJ) // Added by Symbian: 1922 mlyl sl@0: ++actualNoOfGlyphs; sl@0: } sl@0: sl@0: // get some memory to pass back the results, sl@0: // This needs to be big enough for a TShapeHeader sl@0: // plus 10 bytes for every glyph returned (-1 for the 1 byte allocated in TShapeHeader for iBuffer) sl@0: aOutput = reinterpret_cast<TShapeHeader*>( aHeapForOutput->AllocL( sl@0: sizeof(TShapeHeader) + (actualNoOfGlyphs * 10) + 3) ); sl@0: sl@0: // get the results into the shaper structure aOutput sl@0: aOutput->iGlyphCount = actualNoOfGlyphs; sl@0: aOutput->iCharacterCount = noChars; sl@0: aOutput->iReserved0 = 0; sl@0: aOutput->iReserved1 = 0; sl@0: sl@0: // iBuffer contains 10 bytes for every glyph sl@0: // the glyph code (4 bytes), position X(2 bytes) Y(2 bytes) and indices(2 bytes) sl@0: sl@0: // first is glyph count * 4 byte glyph codes sl@0: TUint32* glyphOut = reinterpret_cast<TUint32*>(aOutput->iBuffer); sl@0: TInt16* posOut = reinterpret_cast<TInt16*>(aOutput->iBuffer + sl@0: (4 * actualNoOfGlyphs)); sl@0: TInt16* indicesOut = reinterpret_cast<TInt16*>(aOutput->iBuffer + sl@0: (8 * actualNoOfGlyphs) + 4); sl@0: for (TInt i=0; i < noOfGlyphs; i++) sl@0: { sl@0: if (*glyphBuffer != 0xFFFF && *glyphBuffer != 0x0001 && *glyphBuffer != gidOfZWJ) // Added by Symbian: 1922 mlyl sl@0: { sl@0: *glyphOut++ = *glyphBuffer; sl@0: *posOut++ = FloatToInt( positionBuffer[0] ); sl@0: *posOut++ = FloatToInt( positionBuffer[1] ); sl@0: *indicesOut++ = *indexBuffer; sl@0: } sl@0: ++glyphBuffer; sl@0: positionBuffer += 2; sl@0: ++indexBuffer; sl@0: } sl@0: // There is an extra pair of positions: this is the total advance sl@0: posOut[0] = FloatToInt( positionBuffer[0] ); sl@0: posOut[1] = FloatToInt( positionBuffer[1] ); sl@0: sl@0: CleanupStack::PopAndDestroy(buffer); sl@0: sl@0: }