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