fontservices/textshaperplugin/source/IcuLayoutEngine.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Wed, 09 Jun 2010 11:40:52 +0300
branchRCL_3
changeset 36 f902e87c146f
parent 0 1fb32624e06b
permissions -rw-r--r--
Revision: 201021 Kit: 2010123

/*
* 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);	
	
	}