diff -r 000000000000 -r 1fb32624e06b fontservices/freetypefontrasteriser/src/FTRAST2.CPP --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fontservices/freetypefontrasteriser/src/FTRAST2.CPP Tue Feb 02 02:02:46 2010 +0200 @@ -0,0 +1,2096 @@ +/* +* Copyright (c) 1998-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: +* FTRAST.CPP +* Wrapper code to connect FreeType with the EPOC rasterizer framework. +* This version uses FreeType 2.2.1 +* Secure Rasterizer Plugin. The rasterizer is now implemented as +* an ECOM Plugin. Secure FBServ uses ECOM APIs provided to load the +* plugin. +* +*/ + + +#include +#include +#include +#include +#include +#include +#include FT_FREETYPE_H +#include FT_GLYPH_H +#include FT_INTERNAL_TRUETYPE_TYPES_H +#include +#include +#include +#include +#include +#include +#include +#include + +static const TInt KOneIn16Dot16FixedPointFormat = 65536; + +class CFreeTypeContext; + +class CFreeTypeRasterizer : public COpenFontRasterizer + { +public: + static COpenFontRasterizer* NewL(); + virtual ~CFreeTypeRasterizer(); + // COpenFontRasterizer implementation + COpenFontFile* NewFontFileL(TInt aUid, const TDesC& aFileName, RFs& aFileSession); +private: + CFreeTypeRasterizer(); +private: + CFreeTypeContext* iContext; + }; + +NONSHARABLE_CLASS(CFaceListItem) : public CBase + { + friend class CFaceList; +public: + virtual ~CFaceListItem(); + static CFaceListItem* NewL(CFreeTypeContext* aContext, const TText8* aFileName, TInt aFaceIndex); + void SetFaceNamesL(TOpenFontFaceAttrib& aAttrib) const; + void GetBiggestGlyphsFromSampleL( + TInt aScript, TInt& aUnicodeAscender, TInt& aUnicodeDescender) const; + TBool LoadGlyphAndGetAscenderDescender( + TUint32 aCode, TInt32& aAscender, TInt32& aDescender) const; + TInt DeriveDesignHeightFromMaxHeightWithGlyphSampleL( + TInt aMaxHeightInPixel, TInt aUnicodeAscender, TInt aUnicodeDescender) const; + TInt DeriveDesignHeightFromMaxHeightWithBBoxL(TInt aMaxHeightInPixel) const; + FT_Face Face(void) const; +private: + static void GetDesiredLocalLanguageAndMask(TUint16& aDesiredLocalLanguage, TUint16& aLocalLanguageMask); + static TInt CopyName(TInt aDestMaxLen, TUint16* aDest, const TUint8* aSrc); + CFaceListItem(); + void ConstructL(CFreeTypeContext* aContext, const TText8* aFileName, TInt aFaceIndex); + TInt GetFamilyAndStyleNames(TInt aDestMaxLen, TUint16* aDest, TInt& aFamilyNameLen) const; + void SetTTFFaceNamesL(TOpenFontFaceAttrib& aAttrib) const; +private: + CFaceListItem* iNext; + const TText8* iFileName; // owned by the CFreeTypeFontFile object + TInt iFaceIndex; + FT_Face iFace; + }; + +NONSHARABLE_CLASS(CFaceList) : public CBase + { +public: + CFaceList(); + virtual ~CFaceList(); + CFaceListItem* LoadFaceL(CFreeTypeContext* aContext, const TText8* aFileName, TInt aFaceIndex); + void DeleteFace(const TText8* aFileName, TInt aFaceIndex); +private: + static TInt MemoryUsed(void); + void DeleteLastFace(void); +private: + /* + The memory threshold is not an absolute limit because we must allow + at least one typeface to be loaded, but if it is exceeded typefaces + will be unloaded until just one is left (the one being used) or until + the memory available no longer exceeds the threshold. + */ + enum + { + EMemoryThreshold = 1024 * 1024 + }; + + CFaceListItem* iFirstItem; + }; + +NONSHARABLE_CLASS(CFreeTypeContext) : public COpenFontRasterizerContext + { +public: + static CFreeTypeContext* NewL(); + virtual ~CFreeTypeContext(); + void TranslateMonochromeGlyphBitmap (FT_GlyphSlot aGlyphSlot, TOpenFontGlyphData* aGlyphData); + void TranslateAntiAliasedGlyphBitmap(FT_GlyphSlot aGlyphSlot, TOpenFontGlyphData* aGlyphData); + FT_Library Library(void) const; + CFaceListItem* LoadFaceL(const TText8* aFileName, TInt aFaceIndex); + void DeleteFace(const TText8* aFileName, TInt aFaceIndex); +private: + void ConstructL(void); + void WriteMonoData(TUint8 aData, TInt aBitCount); +private: + FT_Library iLibrary; + CFaceList iFaceList; + }; + +NONSHARABLE_CLASS(CFreeTypeFontFile) : public COpenFontFile + { +public: + static CFreeTypeFontFile* NewL( + CFreeTypeContext* aContext, TInt aUid, const TDesC& aFileName, RFs& aFileSession); + virtual ~CFreeTypeFontFile(); + CFaceListItem* LoadFaceL(TInt aFaceIndex); + CFaceListItem* LoadFaceAndSetTransformL(TInt aFaceIndex, + TInt aSizeInPixels, TInt32 aWidthFactor, TInt32 aSlantFactor); + void RasterizeL( + TInt aCode, TInt aFaceIndex, TInt aSizeinPixels, TInt32 aWidthFactor, + TInt32 aSlantFactor, TGlyphBitmapType aBitmapType, TOpenFontGlyphData* aGlyphData); + void RasterizeGlyphL( + TInt aCode, TInt aFaceIndex, TInt aSizeInPixels, TInt32 aWidthFactor, + TInt32 aSlantFactor, TGlyphBitmapType aBitmapType, TOpenFontGlyphData* aGlyphData); + // COpenFontFile implementation + void GetNearestFontInPixelsL( + RHeap* aHeap, COpenFontSessionCacheList* aSessionCacheList, + const TOpenFontSpec& aDesiredFontSpec, TInt aPixelWidth, TInt aPixelHeight, + COpenFont*& aFont, TOpenFontSpec& aActualFontSpec); + void GetNearestFontToDesignHeightInPixelsL( + RHeap* aHeap, COpenFontSessionCacheList* aSessionCacheList, + const TOpenFontSpec& aDesiredFontSpec, TInt aPixelWidth, TInt aPixelHeight, + COpenFont*& aFont, TOpenFontSpec& aActualFontSpec); + void GetNearestFontToMaxHeightInPixelsL( + RHeap* aHeap, COpenFontSessionCacheList* aSessionCacheList, + const TOpenFontSpec& aDesiredFontSpec, TInt aPixelWidth, TInt aPixelHeight, + COpenFont*& aFont, TOpenFontSpec& aActualFontSpec, TInt aMaxHeight); + TBool HasUnicodeCharacterL(TInt aFaceIndex, TInt aCode) const; + TAny* GetTrueTypeTable(TInt& aError, TInt aFaceIndex, TUint32 aTag, TInt* aLength); + TInt GetGlyphOutline(TInt aFaceIndex, TUint aCode, + TBool aIsGlyphId, TBool, TAny*& aOutline, TInt &aLength); + +private: + static void SetGlyphMetrics(FT_GlyphSlot aGlyphSlot, TOpenFontGlyphData* aGlyphData); + void GetNearestFontInPixelsL( + RHeap* aHeap, COpenFontSessionCacheList* aSessionCacheList, + const TOpenFontSpec& aDesiredFontSpec, TInt aPixelWidth, TInt aPixelHeight, + COpenFont*& aFont, TOpenFontSpec& aActualFontSpec, TInt aMaxHeight); + CFreeTypeFontFile(TInt aUid, const TDesC& aFileName); + void ConstructL(CFreeTypeContext* aContext, const TDesC& aFileName, RFs& aFileSession); + void CreateFilenameInUTF8L(const TDesC& aFileName, RFs& aFileSession); + static TUint32 TagHash(const TUint32& aTag); + static TBool TagId(const TUint32& aTag1, const TUint32& aTag2); + + void DoRasterizeGlyphL(TInt aCode, const CFaceListItem* aFace, + TGlyphBitmapType aBitmapType, TOpenFontGlyphData* aGlyphData); + +private: + CFreeTypeContext* iContext; + TText8* iFileName; // in null-terminated UTF8 so that it can be passed to fopen + RHashMap iTableStore; + }; + +NONSHARABLE_CLASS(CFreeTypeFont) : public COpenFont, public MOpenFontShapingExtension, + public MOpenFontTrueTypeExtension, public MOpenFontGlyphOutlineExtension + { +public: + static CFreeTypeFont* NewL( + RHeap* aHeap, COpenFontSessionCacheList* aSessionCacheList, CFreeTypeFontFile* aFontFile, + TInt aFaceIndex, TInt aSizeInPixels, TInt32 aWidthFactor, TInt32 aSlantFactor, + TGlyphBitmapType aBitmapType, TInt aMaxHeight, TInt aScript); + // COpenFont implementation + void RasterizeL(TInt aCode, TOpenFontGlyphData* aGlyphData); +private: + CFreeTypeFont( + RHeap* aHeap, COpenFontSessionCacheList* aSessionCacheList, CFreeTypeFontFile* aFontFile, + TInt aFaceIndex, TInt32 aWidthFactor, TInt32 aSlantFactor, TGlyphBitmapType aBitmapType); + void ConstructL(TInt aSizeInPixels, TInt aMaxHeight, TInt aScript); + void ComputeMetrics(const FT_Face& aFace, TInt aSizeInPixels, TInt aUnicodeAscender, TInt aUnicodeDescender); + void ComputeMaxWidth(const FT_Face& aFace); + + // Overload to access extended API + void ExtendedInterface(TUid aUid, TAny*& aParam); + + // virtual functions from MOpenFontShapingExtension + void RasterizeGlyphL(TInt aCode,TOpenFontGlyphData* aGlyphData); + TInt GlyphIndex(TInt aUnicodeCharacter) const; + TBool GlyphPointInHintedPixels(TInt aGlyphIndex, TInt aPointNumber, + TReal& aX, TReal& aY) const; + TBool GlyphPointInFontUnits(TInt aGlyphIndex, TInt aPointNumber, + TInt& aX, TInt& aY) const; + void GetExtensionFontMetrics( + MOpenFontShapingExtension::TExtensionFontMetrics& aOut); + + // virtual functions from MOpenFontTrueTypeExtension + TAny* GetTrueTypeTable(TInt& aError, TUint32 aTag, TInt* aLength); + TInt ReleaseTrueTypeTable(TAny* aTable); + TBool HasTrueTypeTable(TUint32 aTag); + TInt GetGlyphOutline(TUint aCode, TBool aIsGlyphId, + TBool aHinted, TAny*& aOutline, TInt &aLength); + + inline TAny* operator new(TUint aSize, TAny* aBase) __NO_THROW; + inline TAny* operator new(TUint aSize) __NO_THROW; + inline void operator delete(void*, TAny*) __NO_THROW; + inline void operator delete(void*) __NO_THROW; + +private: + TInt32 iWidthFactor; // algorithmic width factor as a 16.16 fixed-point number + TInt32 iSlantFactor; // algorithmic slant factor as a 16.16 fixed-point number + TGlyphBitmapType iBitmapType; + }; + +#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER +#warning Using patented technology! +#endif + +inline TAny* CFreeTypeFont::operator new(TUint aSize, TAny* aBase) __NO_THROW + { return CBase::operator new(aSize, aBase); } +inline TAny* CFreeTypeFont::operator new(TUint aSize) __NO_THROW + { return CBase::operator new(aSize); } + +inline void CFreeTypeFont::operator delete(void*, TAny*) __NO_THROW {} +inline void CFreeTypeFont::operator delete(void* a) __NO_THROW + { + // We happen to know that the constructor of CFreeTypeFont + // will always set iHeap correctly before any leave. + // We would be less certain of this in standard C++ where + // constructors (and even initializer lists) may throw exceptions. + CFreeTypeFont* f = reinterpret_cast(a); + f->iHeap->Free(a); + } + +TInt CFaceList::MemoryUsed() + { + TInt total; + User::AllocSize(total); + return total; + } + +CFreeTypeContext* CFreeTypeContext::NewL() + { + CFreeTypeContext* context = new(ELeave) CFreeTypeContext; + CleanupStack::PushL(context); + context->ConstructL(); + CleanupStack::Pop(); + return context; + } + +void CFreeTypeContext::ConstructL() + { + if (FT_Init_FreeType(&iLibrary)) + { + iLibrary = NULL; + User::Leave(KErrNoMemory); + } + } + +CFreeTypeContext::~CFreeTypeContext() + { + FT_Done_FreeType(iLibrary); + } + +FT_Library CFreeTypeContext::Library() const + { + return iLibrary; + } + +CFaceListItem* CFreeTypeContext::LoadFaceL(const TText8* aFileName, TInt aFaceIndex) + { + return iFaceList.LoadFaceL(this, aFileName, aFaceIndex); + } + +void CFreeTypeContext::DeleteFace(const TText8* aFileName, TInt aFaceIndex) + { + iFaceList.DeleteFace(aFileName, aFaceIndex); + } + +COpenFontRasterizer* CFreeTypeRasterizer::NewL() + { + CFreeTypeRasterizer* r = new(ELeave) CFreeTypeRasterizer; + CleanupStack::PushL(r); + r->iContext = CFreeTypeContext::NewL(); + CleanupStack::Pop(); + return r; + } + +CFreeTypeRasterizer::CFreeTypeRasterizer() + { + } + +CFreeTypeRasterizer::~CFreeTypeRasterizer() + { + delete iContext; + CloseSTDLIB(); + } + +COpenFontFile* CFreeTypeRasterizer::NewFontFileL(TInt aUid, const TDesC& aFileName, RFs& aFileSession) + { + if (4 < aFileName.Length()) + { + // Allow TrueType fonts + _LIT(KFntStoreTrueTypeExtension,".ttf"); + // Allow OpenType fonts + _LIT(KFntStoreOpenTypeExtension,".otf"); + TPtrC16 extension = aFileName.Right(4); + if (0 == extension.CompareF(KFntStoreTrueTypeExtension) + || 0 == extension.CompareF(KFntStoreOpenTypeExtension)) + return CFreeTypeFontFile::NewL(iContext, aUid, aFileName, aFileSession); + } + return NULL; + } + +// Exported proxy for instantiation method resolution +// Define the interface UIDs +const TImplementationProxy ImplementationTable[] = + { + IMPLEMENTATION_PROXY_ENTRY(0x101f7f5e, CFreeTypeRasterizer::NewL) + }; + +EXPORT_C const TImplementationProxy* ImplementationGroupProxy(TInt& aTableCount) + { + aTableCount = sizeof(ImplementationTable) / sizeof(TImplementationProxy); + return ImplementationTable; + } + +CFaceListItem* CFreeTypeFontFile::LoadFaceL(TInt aFaceIndex) + { + return iContext->LoadFaceL(iFileName, aFaceIndex); + } + +CFaceListItem* CFreeTypeFontFile::LoadFaceAndSetTransformL(TInt aFaceIndex, + TInt aSizeInPixels, TInt32 aWidthFactor, TInt32 aSlantFactor) + { + CFaceListItem* face = iContext->LoadFaceL(iFileName, aFaceIndex); + if (FT_Set_Pixel_Sizes(face->Face(), aSizeInPixels, aSizeInPixels)) + { + User::Leave(KErrGeneral); + } + + if (aWidthFactor != 0 && + (aWidthFactor != KOneIn16Dot16FixedPointFormat || aSlantFactor != 0)) + { + FT_Matrix matrix; + matrix.xx = aWidthFactor; + matrix.xy = aSlantFactor; + matrix.yx = 0; + matrix.yy = KOneIn16Dot16FixedPointFormat; + FT_Set_Transform(face->Face(),&matrix,NULL); + } + else + FT_Set_Transform(face->Face(),NULL,NULL); + return face; + } + + +CFreeTypeFontFile* CFreeTypeFontFile::NewL(CFreeTypeContext* aContext, TInt aUid, const TDesC& aFileName, RFs& aFileSession) + { + CFreeTypeFontFile* f = new(ELeave) CFreeTypeFontFile(aUid, aFileName); + CleanupStack::PushL(f); + f->ConstructL(aContext, aFileName, aFileSession); + CleanupStack::Pop(); + return f; + } + +/** Forms a hash value from a TrueType-style tag. +@param aTag + Each byte will contain a lower-case ASCII value (or possibly a number). +@return + Hash value. Because we want to avoid collisions, we will move the bottom + four bits of all four bytes into non-overlapping positions. */ +TUint32 CFreeTypeFontFile::TagHash(const TUint32& aTag) + { + TUint32 t = aTag; + // The most useful bits are in the bottom nybbles of each byte + t ^= t >> 12 | t << 20; + return t ^ (t << 15 | t >> 17); + } + +TBool CFreeTypeFontFile::TagId(const TUint32& aTag1, const TUint32& aTag2) + { + return aTag1 == aTag2; + } + +CFreeTypeFontFile::CFreeTypeFontFile(TInt aUid, const TDesC& aFileName): + COpenFontFile(aUid, aFileName), + iTableStore(THashFunction32(CFreeTypeFontFile::TagHash), + TIdentityRelation(CFreeTypeFontFile::TagId)) + { + } + +CFreeTypeFontFile::~CFreeTypeFontFile() + { + const TInt faces = FaceCount(); + for (TInt i = 0; i < faces; i++) + iContext->DeleteFace(iFileName, i); + delete[] iFileName; + THashMapIter it(iTableStore); + it.NextValue(); + while (it.CurrentValue()) + { + delete it.CurrentValue()->Ptr(); + it.NextValue(); + } + iTableStore.Close(); + } + +void CFreeTypeFontFile::ConstructL(CFreeTypeContext* aContext, const TDesC& aFileName, RFs& aFileSession) + { + iContext = aContext; + + CreateFilenameInUTF8L(aFileName, aFileSession); + + // Get the filename as a fallback for the typeface name. + TParse p; + p.Set(aFileName, NULL, NULL); + const TPtrC name = p.Name(); + + // Load the properties of all the faces in the file. + // + // A file may contain one face (e.g. TTF - TrueType Font) or many (e.g. TTC - TrueType Collection). + // The number is determined from a value stored in the properties of the first face. + // + for (TInt face_index = 0, faces = 1; face_index < faces; face_index++) + { + // Load the face. + const CFaceListItem* face = iContext->LoadFaceL(iFileName, face_index); + + // If this is the first face, get the total number of faces. + if (face_index == 0) + { + faces = face->Face()->num_faces; + if (faces < 1) + User::Leave(KErrGeneral); + } + + // Set the face names to the filename as a fallback in case SetFaceNameL doesn't find appropriate names. + TOpenFontFaceAttrib attrib; + attrib.SetFullName(name); + attrib.SetFamilyName(name); + attrib.SetLocalFullName(name); + attrib.SetLocalFamilyName(name); + + // Set the face names to those stored in the file if possible. + face->SetFaceNamesL(attrib); + + // Set the face attributes exposed via FT_Face. + attrib.SetBold(FT_STYLE_FLAG_BOLD & face->Face()->style_flags); + attrib.SetItalic(FT_STYLE_FLAG_ITALIC & face->Face()->style_flags); + attrib.SetMonoWidth(FT_FACE_FLAG_FIXED_WIDTH & face->Face()->face_flags); + + // Set attributes present in TrueType information but not exposed via FT_Face. + if (FT_FACE_FLAG_SFNT & face->Face()->face_flags) + { + const TT_Face tt_face = (TT_Face) face->Face(); + attrib.SetMinSizeInPixels(tt_face->header.Lowest_Rec_PPEM); + attrib.SetCoverage(tt_face->os2.ulUnicodeRange1,tt_face->os2.ulUnicodeRange2, + tt_face->os2.ulUnicodeRange3,tt_face->os2.ulUnicodeRange4); + const TInt serif_style = tt_face->os2.panose[1]; + attrib.SetSerif((serif_style >= 2 && serif_style <= 10) || serif_style >= 14); + } + + // Add the face to the array of faces owned by the file. + AddFaceL(attrib); + + // Unload the face from the cache; loading a font file should not result in the face remaining in the face cache. + iContext->DeleteFace(iFileName, face_index); + } + } + +void CFreeTypeFontFile::CreateFilenameInUTF8L(const TDesC& aFileName, RFs& aFileSession) + { + CCnvCharacterSetConverter* converter = CCnvCharacterSetConverter::NewLC(); + if (CCnvCharacterSetConverter::EAvailable != + converter->PrepareToConvertToOrFromL(KCharacterSetIdentifierUtf8,aFileSession)) + { + User::Leave(KErrGeneral); + } + const TInt filename_length = aFileName.Length(); + TInt utf8_length = 0; + for (TInt i = 0; i < filename_length; i++) + { + const TText c = aFileName[i]; + if (c < 0x80) + utf8_length++; + else if (c < 0x800) + utf8_length += 2; + else + utf8_length += 3; // allow 3 bytes, even for parts of surrogate pairs, in case CHARCONV doesn't handle them + } + iFileName = new(ELeave) TText8[utf8_length + 1]; + TPtr8 q(iFileName, filename_length * 4); + converter->ConvertFromUnicode(q, aFileName); + iFileName[q.Length()] = 0; + CleanupStack::PopAndDestroy(converter); + } + +#ifndef WORDS_BIGENDIAN +static void SwapWords(TUint16* aStart, TInt aCount) + { + TUint8* p = (TUint8*)aStart; + while (aCount-- > 0) + { + const TUint8 temp = *p; + *p = p[1]; + p[1] = temp; + p += 2; + } + } +#endif + +/* +Set the typeface names (family name and full name, in English and the local language). +If this is a TrueType or OpenType font get the names from the TrueType name table, +which are loaded by FreeType and stored in the TT_Face structure. +*/ +void CFaceListItem::SetFaceNamesL(TOpenFontFaceAttrib& aAttrib) const + { + TInt familyNameLen = 0; + TUint16 name[TOpenFontFaceAttrib::ENameLength]; + const TInt totalLen = GetFamilyAndStyleNames(TOpenFontFaceAttrib::ENameLength, name, familyNameLen); + const TPtrC family_name(name, familyNameLen); + const TPtrC full_name(name, totalLen); + + // Set the local and non-local family and full names to these default versions. + aAttrib.SetFamilyName(family_name); + aAttrib.SetLocalFamilyName(family_name); + aAttrib.SetFullName(full_name); + aAttrib.SetLocalFullName(full_name); + + // If this is a TrueType font, replace the default names with those in the name table if any. + if (FT_FACE_FLAG_SFNT & iFace->face_flags) + { + SetTTFFaceNamesL(aAttrib); + } + } + +TInt CFaceListItem::GetFamilyAndStyleNames(TInt aDestMaxLen, TUint16* aDest, TInt& aFamilyNameLen) const + { + aFamilyNameLen = 0; + // Put the family name into . Ignore non-ASCII characters + const TUint8* p = (const TUint8*) iFace->family_name; + if (p) + aFamilyNameLen = CopyName(aDestMaxLen, aDest, p); + + TInt totalLen = aFamilyNameLen; + // Append the style name if any, + p = (const TUint8*) iFace->style_name; + if (p && aFamilyNameLen < aDestMaxLen) + { + aDest[totalLen++] = ' '; + totalLen += CopyName(aDestMaxLen - totalLen, aDest + totalLen, p); + } + return totalLen; + } + +//@pre: aSrc != NULL +TInt CFaceListItem::CopyName(TInt aDestMaxLen, TUint16* aDest, const TUint8* aSrc) + { + TInt len = 0; + while (len < aDestMaxLen && *aSrc) + { + if (*aSrc <= 127) + aDest[len++] = *aSrc; + aSrc++; + } + return len; + } + +void CFaceListItem::GetDesiredLocalLanguageAndMask(TUint16& aDesiredLocalLanguage, TUint16& aLocalLanguageMask) + { + switch (User::Language()) + { + case ELangEnglish: aDesiredLocalLanguage = 0x0809; aLocalLanguageMask = 0xFFFF; break; + case ELangSwissFrench: + case ELangBelgianFrench: + case ELangInternationalFrench: + case ELangFrench: aDesiredLocalLanguage = 0x0C; break; + case ELangSwissGerman: + case ELangAustrian: + case ELangGerman: aDesiredLocalLanguage = 0x07; break; + case ELangSpanish: aDesiredLocalLanguage = 0x0A; break; + case ELangItalian: aDesiredLocalLanguage = 0x10; break; + case ELangSwedish: aDesiredLocalLanguage = 0x1D; break; + case ELangDanish: aDesiredLocalLanguage = 0x06; break; + case ELangNorwegian: aDesiredLocalLanguage = 0x14; break; + case ELangFinnish: aDesiredLocalLanguage = 0x0B; break; + case ELangPortuguese: aDesiredLocalLanguage = 0x16; break; + case ELangTurkish: aDesiredLocalLanguage = 0x1F; break; + case ELangIcelandic: aDesiredLocalLanguage = 0x0F; break; + case ELangRussian: aDesiredLocalLanguage = 0x19; break; + case ELangHungarian: aDesiredLocalLanguage = 0x0E; break; + case ELangBelgianFlemish: + case ELangDutch: aDesiredLocalLanguage = 0x13; break; + case ELangAustralian: aDesiredLocalLanguage = 0x0C09; aLocalLanguageMask = 0xFFFF; break; + case ELangNewZealand: aDesiredLocalLanguage = 0x1409; aLocalLanguageMask = 0xFFFF; break; + case ELangCzech: aDesiredLocalLanguage = 0x05; break; + case ELangSlovak: aDesiredLocalLanguage = 0x1B; break; + case ELangPolish: aDesiredLocalLanguage = 0x15; break; + case ELangSlovenian: aDesiredLocalLanguage = 0x24; break; + case ELangTaiwanChinese: aDesiredLocalLanguage = 0x0104; aLocalLanguageMask = 0xFFFF; break; + case ELangHongKongChinese: aDesiredLocalLanguage = 0x0304; aLocalLanguageMask = 0xFFFF; break; + case ELangPrcChinese: aDesiredLocalLanguage = 0x0204; aLocalLanguageMask = 0xFFFF; break; + case ELangJapanese: aDesiredLocalLanguage = 0x11; break; + case ELangKorean: aDesiredLocalLanguage = 0x12; break; + case ELangThai: aDesiredLocalLanguage = 0x1e; break; + default: break; + } + } + +void CFaceListItem::SetTTFFaceNamesL(TOpenFontFaceAttrib& aAttrib) const + { + // Search for a suitable name record. Always use Windows Unicode encoding. + const TUint16 desired_platform = 3; // Windows + const TUint16 desired_encoding = 1; // Unicode + const TUint16 desired_language = 0x0409; + const TUint16 family_name_id = 1; + const TUint16 full_name_id = 4; + + TUint16 local_language_mask = 0x00FF; // low byte only - high byte is country or other variant + TUint16 desired_local_language = 0x09; + GetDesiredLocalLanguageAndMask(desired_local_language, local_language_mask); + + const TUint name_records = FT_Get_Sfnt_Name_Count(iFace); + for (TInt i = 0; i < name_records; i++) + { + FT_SfntName sfntName; + if (FT_Get_Sfnt_Name(iFace, i, &sfntName)) + { + User::Leave(KErrGeneral); + } + if (sfntName.platform_id == desired_platform && + sfntName.encoding_id == desired_encoding && + (sfntName.language_id == desired_language || (sfntName.language_id & local_language_mask) == desired_local_language) && + (sfntName.name_id == family_name_id || sfntName.name_id == full_name_id)) + { + const TUint16* name_start = (TUint16*)sfntName.string; + const TInt name_length = sfntName.string_len / sizeof(TUint16); + // Copy the name into a properly aligned buffer. + TBuf name; + const TInt truncated_length = Min(name_length,TOpenFontFaceAttrib::ENameLength); + name.SetLength(truncated_length); + Mem::Copy((void*)name.Ptr(), name_start, truncated_length * sizeof(TText)); + +#ifndef WORDS_BIGENDIAN + // Byte-swap the Unicode text. + SwapWords((TUint16*)name.Ptr(),truncated_length); +#endif + + if (sfntName.language_id == desired_language) + { + if (sfntName.name_id == family_name_id) + { + aAttrib.SetFamilyName(name); + } + else + { + aAttrib.SetFullName(name); + } + } + if ((sfntName.language_id & local_language_mask) == desired_local_language) + { + if (sfntName.name_id == family_name_id) + { + aAttrib.SetLocalFamilyName(name); + } + else + { + aAttrib.SetLocalFullName(name); + } + } + } + } + } + +void CFreeTypeFontFile::GetNearestFontInPixelsL( + RHeap* aHeap, + COpenFontSessionCacheList* aSessionCacheList, + const TOpenFontSpec& aDesiredFontSpec, + TInt aPixelWidth, + TInt aPixelHeight, + COpenFont*& aFont, + TOpenFontSpec& aActualFontSpec) + { + GetNearestFontToDesignHeightInPixelsL( + aHeap, aSessionCacheList, aDesiredFontSpec, aPixelWidth, aPixelHeight, aFont, aActualFontSpec); + } + +void CFreeTypeFontFile::GetNearestFontToDesignHeightInPixelsL( + RHeap* aHeap, + COpenFontSessionCacheList* aSessionCacheList, + const TOpenFontSpec& aDesiredFontSpec, + TInt aPixelWidth, + TInt aPixelHeight, + COpenFont*& aFont, + TOpenFontSpec& aActualFontSpec) + { + GetNearestFontInPixelsL( + aHeap, aSessionCacheList, aDesiredFontSpec, aPixelWidth, aPixelHeight, aFont, aActualFontSpec, 0); + } + +void CFreeTypeFontFile::GetNearestFontToMaxHeightInPixelsL( + RHeap* aHeap, + COpenFontSessionCacheList* aSessionCacheList, + const TOpenFontSpec& aDesiredFontSpec, + TInt aPixelWidth, + TInt aPixelHeight, + COpenFont*& aFont, + TOpenFontSpec& aActualFontSpec, + TInt aMaxHeight) + { + GetNearestFontInPixelsL( + aHeap, aSessionCacheList, aDesiredFontSpec, aPixelWidth, aPixelHeight, aFont, aActualFontSpec, aMaxHeight); + } + +void CFreeTypeFontFile::GetNearestFontInPixelsL( + RHeap* aHeap, + COpenFontSessionCacheList* aSessionCacheList, + const TOpenFontSpec& aDesiredFontSpec, + TInt aPixelWidth, + TInt aPixelHeight, + COpenFont*& aFont, + TOpenFontSpec& aActualFontSpec, + TInt aMaxHeight) +/** +Use the standard matcher. +*/ + { + TInt face_index = 0; + if (GetNearestFontHelper(aDesiredFontSpec, aPixelWidth, aPixelHeight, face_index, aActualFontSpec)) + { + // Allow only monochrome and standard anti-aliasing. + TGlyphBitmapType glyphBitmapType = aDesiredFontSpec.BitmapType(); + if (EAntiAliasedGlyphBitmap != glyphBitmapType) + { + glyphBitmapType = EMonochromeGlyphBitmap; + } + aFont = CFreeTypeFont::NewL( + aHeap, aSessionCacheList, this, face_index, aActualFontSpec.Height(), + aActualFontSpec.WidthFactor(), aActualFontSpec.SlantFactor(), + glyphBitmapType, aMaxHeight, aActualFontSpec.ScriptTypeForMetrics()); + aActualFontSpec.SetBitmapType(glyphBitmapType); + if (aMaxHeight) + { + aActualFontSpec.SetHeight(aFont->Metrics().Size()); + } + } + } + +CFreeTypeFont* CFreeTypeFont::NewL( + RHeap* aHeap, + COpenFontSessionCacheList* aSessionCacheList, + CFreeTypeFontFile* aFontFile, + TInt aFaceIndex, + TInt aSizeInPixels, + TInt32 aWidthFactor, + TInt32 aSlantFactor, + TGlyphBitmapType aBitmapType, + TInt aMaxHeight, + TInt aScript) + { + CFreeTypeFont* f = (CFreeTypeFont*)aHeap->AllocL(sizeof(CFreeTypeFont)); + new(f) CFreeTypeFont(aHeap, aSessionCacheList, aFontFile, aFaceIndex, aWidthFactor, aSlantFactor, aBitmapType); + CleanupStack::PushL(f); + f->ConstructL(aSizeInPixels, aMaxHeight, aScript); + CleanupStack::Pop(); + return f; + } + +CFreeTypeFont::CFreeTypeFont(RHeap* aHeap, COpenFontSessionCacheList* aSessionCacheList, + CFreeTypeFontFile* aFontFile, TInt aFaceIndex, + TInt32 aWidthFactor, TInt32 aSlantFactor, TGlyphBitmapType aBitmapType) : + COpenFont(aHeap, aSessionCacheList, aFontFile, aFaceIndex), + iWidthFactor(aWidthFactor), + iSlantFactor(aSlantFactor), + iBitmapType(aBitmapType) + { + } + +static TUint TwentySixDotSix2Pixel(TInt32 aInt) +// +// Font units in FreeType are specified in a fixed point notation, which is a +// trick that makes it possible to store a fractional number in an integer variable. +// 26.6 means that the top 26 bits are the integer part, and the bottom 6 bits are +// the fractional part. +// +// 32 (binary 100000) is added to round the fractional part off. +// + { + if (0 > aInt) + aInt = -aInt; + return (aInt + 32) >> 6; + } + +void CFreeTypeFont::ConstructL(TInt aSizeInPixels, TInt aMaxHeight, TInt aScript) + { + CFreeTypeFontFile* file = (CFreeTypeFontFile*)File(); + User::LeaveIfNull( file ); + const CFaceListItem* face = file->LoadFaceL( FaceIndex() ); + TInt unicodeAscender = 0, unicodeDescender = 0; + face->GetBiggestGlyphsFromSampleL(aScript, unicodeAscender, unicodeDescender); + if (aMaxHeight) + { + aSizeInPixels = + (unicodeAscender && unicodeDescender) ? + face->DeriveDesignHeightFromMaxHeightWithGlyphSampleL( + aMaxHeight, unicodeAscender, unicodeDescender) : + face->DeriveDesignHeightFromMaxHeightWithBBoxL(aMaxHeight); + } + else + { + // Have to set the pixel size because no + // DeriveDesignHeightFromMaxHeightWith...() was called. + if (FT_Set_Pixel_Sizes(face->Face(), aSizeInPixels, aSizeInPixels)) + User::Leave( KErrGeneral ); + if (aSizeInPixels==0) + aSizeInPixels=TwentySixDotSix2Pixel(face->Face()->size->metrics.height); + } + ComputeMetrics(face->Face(), aSizeInPixels, unicodeAscender, unicodeDescender); + ComputeMaxWidth(face->Face()); + } + +TBool CFaceListItem::LoadGlyphAndGetAscenderDescender( + TUint32 aCode, + TInt32& aAscender, + TInt32& aDescender) const +// +//@param aCode Character code +//@param aAscender Vertical distance from the baseline to the topmost point of aCode +//@param aDescender Vertical distance from the baseline to the bottommost point of aCode +//@return ETrue if aCode exists in aFace and can be loaded, otherwise EFalse +//@pre FT_Set_Pixel_Sizes is called +// + { + const TUint glyphIndex = FT_Get_Char_Index(iFace, aCode); + if (!glyphIndex || FT_Load_Glyph(iFace, glyphIndex, FT_LOAD_DEFAULT | FT_LOAD_IGNORE_TRANSFORM)) + return EFalse; + aAscender = iFace->glyph->metrics.horiBearingY; + aDescender = iFace->glyph->metrics.height - iFace->glyph->metrics.horiBearingY; + // if height < horiBearingY -> no descender possible + if (0 > aDescender) + aDescender = 0; + return ETrue; + } + +void CFaceListItem::GetBiggestGlyphsFromSampleL( + TInt aScript, + TInt& aUnicodeAscender, + TInt& aUnicodeDescender) const +// +//@param aScript The script to determine which glyph sample to use +//@param aUnicodeAscender Unicode of the tallest ascender found in sample +//@param aUnicodeDescender Unicode of the 'deepest' descender found in sample +// + { + aUnicodeAscender = aUnicodeDescender = 0; + const TPtrC glyphSample = GlyphSample::TScript2GlyphSample(aScript); + if (!glyphSample.Ptr()) + return; + // Set the face to an arbitrary value assuming the + // biggest glyphs are the same for different sizes + if (FT_Set_Pixel_Sizes(iFace, 0, 24)) + User::Leave( KErrGeneral ); + TInt32 maxAscender = 0, maxDescender = 0; + const TInt totalGlyphsNum = glyphSample.Length(); + for (TInt i = 0; i < totalGlyphsNum; i++) + { + TInt32 ascender = 0, descender = 0; + if (!LoadGlyphAndGetAscenderDescender(*(glyphSample.Ptr() + i), ascender, descender)) + continue; + // Keep the max up to date + if (maxAscender < ascender) + { + maxAscender = ascender; + aUnicodeAscender = *(glyphSample.Ptr() + i); + } + if (maxDescender < descender) + { + maxDescender = descender; + aUnicodeDescender = *(glyphSample.Ptr() + i); + } + } + } + +TInt CFaceListItem::DeriveDesignHeightFromMaxHeightWithGlyphSampleL( + TInt aMaxHeightInPixel, + TInt aUnicodeAscender, + TInt aUnicodeDescender) const +// +//@param aMaxHeightInPixel Maximum height within which the return design height has to fit into +//@param aUnicodeAscender Unicode of ascender with which font height is calculated +//@param aUnicodeDescender Unicode of descender with which font height is calculated +//@return The maximum design height that fits in aMaxHeightInPixel +//@post FT_Set_Pixel_Sizes is called with the return design height +// + { + TInt designHeightInPixel = aMaxHeightInPixel; + do + { + if (FT_Set_Pixel_Sizes(iFace, 0, designHeightInPixel)) + User::Leave( KErrGeneral ); + TInt32 ascender = 0, descender = 0, dummyAscender = 0, dummyDescender = 0; + LoadGlyphAndGetAscenderDescender(aUnicodeAscender, ascender, dummyDescender); + LoadGlyphAndGetAscenderDescender(aUnicodeDescender, dummyAscender, descender); + if (TwentySixDotSix2Pixel(ascender + descender) <= aMaxHeightInPixel) + { + break; + } + } while (--designHeightInPixel); + return designHeightInPixel; + } + +TInt CFaceListItem::DeriveDesignHeightFromMaxHeightWithBBoxL(TInt aMaxHeightInPixel) const +// +//@param aMaxHeightInPixel Maximum height within which the return design height has to fit into +//@return The maximum design height with which the bbox would fit in aMaxHeightInPixel +//@post FT_Set_Pixel_Sizes is called with the returned design height +// + { + // Calculation is as follows: + // boundingBoxHeightInPixel = aMaxHeightInPixel + // boundingBoxHeightInFontUnit = iFace->bbox.yMax - iFace->bbox.yMin + // PixelsPerFontUnit (PPFU) = boundingBoxHeightInPixel / boundingBoxHeightInFontUnit + // And also: + // PixelsPerEm (PPE) = the design height we are looking for + // FontUnitsPerEm (FUPE) = iFace->units_per_EM + // PixelsPerFontUnit (PPFU) = PixelsPerEm / FontUnitsPerEm + // Therefore by combining the equations we get: + // boundingBoxHeightInPixel / boundingBoxHeightInFontUnit = PixelsPerEm / FontUnitsPerEm + // Rearranged: + // PixelsPerEm = boundingBoxHeightInPixel * FontUnitsPerEm / boundingBoxHeightInFontUnit + // Now, if the result of the calculation is integer then that design height will fit + // but if it is not then we should round down to guarantee the bounding box for the resultant + // integer design height will fit inside our given maximum height + // + const TInt boundingBoxHeightInFontUnit = iFace->bbox.yMax - iFace->bbox.yMin; + TInt designHeightInPixels = (aMaxHeightInPixel * iFace->units_per_EM) / boundingBoxHeightInFontUnit; + if (FT_Set_Pixel_Sizes( iFace, designHeightInPixels, designHeightInPixels )) + User::Leave( KErrGeneral ); + + // Unfortunately, this is just the starting point. Actual design heights do not necessarily + // have a mathematical relationship to font metrics so we need to adjust the design height + // until we find the best value where the metrics do fit. + const TInt maxHeightInFontUnit = aMaxHeightInPixel << 6; + TInt currentMaxHeightInFontUnit = FT_MulFix( boundingBoxHeightInFontUnit, iFace->size->metrics.y_scale ); + while ( currentMaxHeightInFontUnit < maxHeightInFontUnit ) + { + designHeightInPixels++; + if (FT_Set_Pixel_Sizes( iFace, designHeightInPixels, designHeightInPixels )) + User::Leave( KErrGeneral ); + currentMaxHeightInFontUnit = FT_MulFix( boundingBoxHeightInFontUnit, iFace->size->metrics.y_scale ); + } + while ( currentMaxHeightInFontUnit > maxHeightInFontUnit ) + { + designHeightInPixels--; + if (FT_Set_Pixel_Sizes( iFace, designHeightInPixels, designHeightInPixels )) + User::Leave( KErrGeneral ); + currentMaxHeightInFontUnit = FT_MulFix( boundingBoxHeightInFontUnit, iFace->size->metrics.y_scale ); + } + return designHeightInPixels; + } + +void CFreeTypeFont::ComputeMetrics( + const FT_Face& aFace, + TInt aSizeInPixels, + TInt aUnicodeAscender, + TInt aUnicodeDescender) +// +//@param aFace FreeType face object +//@param aSizeInPixels Design height +//@param aUnicodeAscender Unicode of ascender with which maximum ascent is calculated +//@param aUnicodeDescender Unicode of descender with which maximum descent is calculated +// +// sTypoAscender/sTypoDescender are the only metrics in TrueType/OpenType +// that define in the Basic Latin character set. +// OpenType suggests the corresponding metrics in AFM file should be used. +// Adobe Font Metrics (AFM) File Format Specification in turn suggests that the y-value of the +// top of the lowercase 'd' and the bottom of the lowercase 'p' should be used for sTypoAscender +// and sTypoDescender respectively. +// +// Because there is no guarantee that these table values are accurate, we use them only when +// the characters 'd' and 'p' are absent in the font file. +// +// If aUnicodeAscender or aUnicodeDescender is not given, the 'max' metrics are computed with +// the coordinates of the bbox, which is an imaginary box that encloses _all_ glyphs from the font. +// + { + TUint glyphIndex = FT_Get_Char_Index(aFace, 0x0064); + const TInt32 ascentBasicLatin = + (glyphIndex && !FT_Load_Glyph(aFace, glyphIndex, FT_LOAD_DEFAULT | FT_LOAD_IGNORE_TRANSFORM)) ? + aFace->glyph->metrics.horiBearingY : + FT_MulFix(((TT_Face)aFace)->os2.sTypoAscender, aFace->size->metrics.y_scale); + + glyphIndex = FT_Get_Char_Index(aFace, 0x0070); + const TInt32 descentBasicLatin = + (glyphIndex && !FT_Load_Glyph(aFace, glyphIndex, FT_LOAD_DEFAULT | FT_LOAD_IGNORE_TRANSFORM)) ? + aFace->glyph->metrics.height - aFace->glyph->metrics.horiBearingY : + FT_MulFix(((TT_Face)aFace)->os2.sTypoDescender, aFace->size->metrics.y_scale); + + const TInt yMaxBBox = FT_MulFix(aFace->bbox.yMax, aFace->size->metrics.y_scale); + const TInt yMinBBox = FT_MulFix(aFace->bbox.yMin, aFace->size->metrics.y_scale); + + glyphIndex = aUnicodeAscender ? FT_Get_Char_Index(aFace, aUnicodeAscender) : 0; + const TInt32 ascenderMax = + (glyphIndex && !FT_Load_Glyph(aFace, glyphIndex, FT_LOAD_DEFAULT | FT_LOAD_IGNORE_TRANSFORM)) ? + aFace->glyph->metrics.horiBearingY : + yMaxBBox; + + glyphIndex = aUnicodeDescender ? FT_Get_Char_Index(aFace, aUnicodeDescender) : 0; + const TInt32 descenderMax = + (glyphIndex && !FT_Load_Glyph(aFace, glyphIndex, FT_LOAD_DEFAULT | FT_LOAD_IGNORE_TRANSFORM)) ? + aFace->glyph->metrics.height - aFace->glyph->metrics.horiBearingY : + yMinBBox; + + iFontCapitalAscent = TwentySixDotSix2Pixel(ascentBasicLatin); + iFontStandardDescent = TwentySixDotSix2Pixel(descentBasicLatin); + iFontMaxAscent = TwentySixDotSix2Pixel(ascenderMax); + iFontMaxDescent = TwentySixDotSix2Pixel(descenderMax); + iFontLineGap = + (FT_FACE_FLAG_SFNT & aFace->face_flags) ? + iFontMaxAscent + iFontMaxDescent + TwentySixDotSix2Pixel(((TT_Face)aFace)->os2.sTypoLineGap) : + ( ( (iFontMaxAscent + iFontMaxDescent) * 12 ) + 5 ) / 10; + // old version of metrics left for backward compatibility + iMetrics.SetSize (aSizeInPixels); + iMetrics.SetAscent (TwentySixDotSix2Pixel(aFace->size->metrics.ascender)); + iMetrics.SetDescent (TwentySixDotSix2Pixel(aFace->size->metrics.descender)); + iMetrics.SetMaxHeight (TwentySixDotSix2Pixel(yMaxBBox)); + iMetrics.SetMaxDepth (TwentySixDotSix2Pixel(yMinBBox)); + } + +void CFreeTypeFont::ComputeMaxWidth(const FT_Face& aFace) +// +// Scale the maximum advance by the width factor if it is not unity. +// + { + iMetrics.SetMaxWidth(TwentySixDotSix2Pixel(aFace->size->metrics.max_advance)); + if (iWidthFactor && KOneIn16Dot16FixedPointFormat != iWidthFactor) + { + TInt32 advance = iMetrics.MaxWidth() << 16; + advance = FT_MulFix(advance, iWidthFactor); + iMetrics.SetMaxWidth( advance >> 16 ); + } + } + +// Access to extended API though BC interface +void CFreeTypeFont::ExtendedInterface(TUid aUid, TAny*& aParam) + { + if (aUid == KUidOpenFontShapingExtension) + aParam = static_cast(this); + else if (aUid == KUidOpenFontTrueTypeExtension) + aParam = static_cast(this); + else if (aUid == KUidOpenFontGlyphOutlineExtension) + aParam = static_cast(this); + else + COpenFont::ExtendedInterface(aUid, aParam); + } + +// Rasterize a glyph from unicode value of aCode. +void CFreeTypeFont::RasterizeL(TInt aCode, TOpenFontGlyphData* aGlyphData) + { + CFreeTypeFontFile* file = (CFreeTypeFontFile*)File(); + if (!file) + User::Leave(KErrGeneral); + file->RasterizeL( + aCode, FaceIndex(), iMetrics.Size(), iWidthFactor, iSlantFactor, iBitmapType, aGlyphData); + } + +// Rasterize a glyph from glyph code value of aCode. +void CFreeTypeFont::RasterizeGlyphL(TInt aCode,TOpenFontGlyphData* aGlyphData) + { + CFreeTypeFontFile* file = (CFreeTypeFontFile*)File(); + if (!file) + User::Leave(KErrGeneral); + file->RasterizeGlyphL( + aCode, FaceIndex(), iMetrics.Size(), iWidthFactor, iSlantFactor, iBitmapType, aGlyphData); + } + +TInt CFreeTypeFont::GlyphIndex(TInt aUnicode) const + { + CFreeTypeFontFile* file = static_cast(File()); + if (!file) + return 0; + return FT_Get_Char_Index(file->LoadFaceL( FaceIndex() )->Face(), aUnicode); + } + +TBool CFreeTypeFont::GlyphPointInHintedPixels(TInt aGlyphIndex, TInt aPointNumber, + TReal& aX, TReal& aY) const + { + CFreeTypeFontFile* file = static_cast(File()); + if (!file) + return EFalse; + FT_Face face = file->LoadFaceAndSetTransformL( FaceIndex(), + iMetrics.Size(), iWidthFactor, iSlantFactor )->Face(); + // Load glyph outline, scale and hint but don't rasterize + if (0 == FT_Load_Glyph(face, aGlyphIndex, FT_LOAD_NO_BITMAP)) + { + if (face->glyph->format != FT_GLYPH_FORMAT_OUTLINE + || face->glyph->outline.n_points <= aPointNumber) + return EFalse; + FT_Vector& vec = face->glyph->outline.points[aPointNumber]; + // Convert from 1.25.6 fixed point to floating point + aX = vec.x / 64.0; + aY = vec.y / 64.0; + return ETrue; + } + return EFalse; + } + +TBool CFreeTypeFont::GlyphPointInFontUnits(TInt aGlyphIndex, TInt aPointNumber, + TInt& aX, TInt& aY) const + { + CFreeTypeFontFile* file = static_cast(File()); + if (!file) + return EFalse; + FT_Face face = file->LoadFaceL( FaceIndex() )->Face(); + // Load glyph outline but don't scale, hint or rasterize + if (0 == FT_Load_Glyph(face, aGlyphIndex, FT_LOAD_IGNORE_TRANSFORM + | FT_LOAD_NO_BITMAP | FT_LOAD_NO_SCALE)) + { + if (face->glyph->format != FT_GLYPH_FORMAT_OUTLINE + || face->glyph->outline.n_points <= aPointNumber) + return EFalse; + FT_Vector& vec = face->glyph->outline.points[aPointNumber]; + aX = vec.x; + aY = vec.y; + return ETrue; + } + return EFalse; + } + +void CFreeTypeFont::GetExtensionFontMetrics( + MOpenFontShapingExtension::TExtensionFontMetrics& aOut) + { + CFreeTypeFontFile* file = static_cast(File()); + if (file) + { + FT_Face face = file->LoadFaceAndSetTransformL( FaceIndex(), + iMetrics.Size(), iWidthFactor, iSlantFactor )->Face(); + aOut.iUnitsPerEm = face->units_per_EM; + aOut.iXPixelsPerEm = face->size->metrics.x_ppem; + aOut.iYPixelsPerEm = face->size->metrics.y_ppem; + aOut.iXScaleFactor = face->size->metrics.x_scale / 65536.0; + aOut.iYScaleFactor = face->size->metrics.y_scale / 65536.0; + } + else + { + // This shouldn't happen, but we can't panic here. + aOut.iUnitsPerEm = 1; + aOut.iXPixelsPerEm = 1.0; + aOut.iYPixelsPerEm = 1.0; + aOut.iXScaleFactor = 1.0; + aOut.iYScaleFactor = 1.0; + } + } + + +TInt CFreeTypeFont::GetGlyphOutline(TUint aCode, TBool aIsGlyphId, + TBool aHinted, TAny*& aOutline, TInt& aLength) + { + CFreeTypeFontFile* file = (CFreeTypeFontFile*)File(); + if (!file) + { + return KErrNotFound; + } + aHinted = EFalse; // to avoid 'unused' warning. + + return file->GetGlyphOutline(FaceIndex(), aCode, aIsGlyphId, aHinted, aOutline, aLength); + } + +TAny* CFreeTypeFont::GetTrueTypeTable(TInt& aError, TUint32 aTag, TInt* aLength) + { + CFreeTypeFontFile* file = (CFreeTypeFontFile*)File(); + if (!file) + { + aError = KErrNotFound; + return 0; + } + + return file->GetTrueTypeTable(aError, FaceIndex(), aTag, aLength); + } + +TInt CFreeTypeFont::ReleaseTrueTypeTable(TAny*) + { + // CFreeTypeFontFile keeps hold of tables until destruction + return KErrNone; + } + +TBool CFreeTypeFont::HasTrueTypeTable(TUint32 aTag) + { + FT_ULong table_len = 0; + + CFreeTypeFontFile* file = (CFreeTypeFontFile*)File(); + if ( file == NULL ) + { + return EFalse; + } + + // locate the table & find the length + CFaceListItem* faceList = file->LoadFaceL( FaceIndex() ); + FT_Face face = faceList->Face(); + TT_Face ttface = (TT_Face)faceList->Face(); + TInt aError = ttface->goto_table( ttface, aTag, face->stream, &table_len ); + + return aError == KErrNone; + } + +void CFreeTypeFontFile::SetGlyphMetrics(FT_GlyphSlot aGlyphSlot,TOpenFontGlyphData* aGlyphData) + { + TOpenFontCharMetrics metrics; + metrics.SetWidth(aGlyphSlot->bitmap.width); + metrics.SetHeight(aGlyphSlot->bitmap.rows); + metrics.SetHorizBearingX(aGlyphSlot->bitmap_left); + metrics.SetHorizBearingY(aGlyphSlot->bitmap_top); + metrics.SetHorizAdvance(TwentySixDotSix2Pixel(aGlyphSlot->advance.x)); + metrics.SetVertBearingX(0); + metrics.SetVertBearingY(0); + metrics.SetVertAdvance(aGlyphSlot->bitmap.rows); + aGlyphData->SetMetrics(metrics); + } + +void CFreeTypeFontFile::RasterizeL( + TInt aCode, + TInt aFaceIndex, + TInt aSizeInPixels, + TInt32 aWidthFactor, + TInt32 aSlantFactor, + TGlyphBitmapType aBitmapType, + TOpenFontGlyphData* aGlyphData) + { + const CFaceListItem* face = LoadFaceAndSetTransformL(aFaceIndex, + aSizeInPixels, aWidthFactor, aSlantFactor); + TInt glyphCode = FT_Get_Char_Index(face->Face(), aCode); + DoRasterizeGlyphL(glyphCode, face, aBitmapType, aGlyphData); + } + +void CFreeTypeFontFile::RasterizeGlyphL( + TInt aCode, + TInt aFaceIndex, + TInt aSizeInPixels, + TInt32 aWidthFactor, + TInt32 aSlantFactor, + TGlyphBitmapType aBitmapType, + TOpenFontGlyphData* aGlyphData) + { + const CFaceListItem* face = LoadFaceAndSetTransformL(aFaceIndex, + aSizeInPixels, aWidthFactor, aSlantFactor); + DoRasterizeGlyphL(aCode, face, aBitmapType, aGlyphData); + } + +void CFreeTypeFontFile::DoRasterizeGlyphL( + TInt aCode, const CFaceListItem* aFace, + TGlyphBitmapType aBitmapType, TOpenFontGlyphData* aGlyphData) + { + // Substitute it with Symbian's private use area replacement character + // if this character is not in the font. KReplacementCharacter might + // also be absent in the font - this is valid and FT_Load_Glyph will + // handle the case when glyph_index is zero. + // + const TUint glyph_index = + (aCode != 0) ? aCode : FT_Get_Char_Index(aFace->Face(), KReplacementCharacter); + + // Set the flag that causes the isFixedPitch flag in the PostScript table to be ignored. + // If we honour isFoxedPitch, CJK fonts like MSSONG space their Latin characters too widely because all + // characters are given the width of the widest character in the font. Ignoring the flag does no + // harm to genuine fixed-width fonts, which will return the same width for all characters. + // + const TInt32 load_flags = + (aBitmapType == EMonochromeGlyphBitmap) ? + FT_LOAD_RENDER | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH | FT_LOAD_MONOCHROME : + FT_LOAD_RENDER | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH; + + if (FT_Load_Glyph(aFace->Face(), glyph_index, load_flags) != 0) + User::Leave(KErrGeneral); + + // Translate the glyph data from FreeType to EPOC format. + SetGlyphMetrics(aFace->Face()->glyph,aGlyphData); + if (aBitmapType == EMonochromeGlyphBitmap) + iContext->TranslateMonochromeGlyphBitmap(aFace->Face()->glyph,aGlyphData); + else + iContext->TranslateAntiAliasedGlyphBitmap(aFace->Face()->glyph,aGlyphData); + } + +enum vg_commands { + VG_CMD_NONE = 0, + VG_CMD_MOVETO, + VG_CMD_LINETO, + VG_CMD_CONICTO, + VG_CMD_CUBICTO, + VG_CMD_CLOSE +}; + + +enum contour_states + { + CONTOUR_STATE_NOT_STARTED = 0, + CONTOUR_STATE_START, + CONTOUR_STATE_CONIC, + CONTOUR_STATE_CUBIC1, + CONTOUR_STATE_CUBIC2, + CONTOUR_STATE_MAX + }; + +static const TInt StateTransitions[CONTOUR_STATE_MAX][3] = + { + {CONTOUR_STATE_START, -1, -1}, + {CONTOUR_STATE_START, CONTOUR_STATE_CONIC, CONTOUR_STATE_CUBIC1}, + {CONTOUR_STATE_START, -1, -1}, + {-1, -1, CONTOUR_STATE_CUBIC2}, + {CONTOUR_STATE_START, -1, -1}, + }; + +static const TInt OutputCommands[CONTOUR_STATE_MAX][3] = + { + {VG_CMD_MOVETO, -1, -1}, + {VG_CMD_LINETO, VG_CMD_NONE, VG_CMD_NONE}, + {VG_CMD_CONICTO, -1, -1}, + {-1, -1, VG_CMD_NONE}, + {VG_CMD_CUBICTO, -1, -1}, + }; + + +class MVGCommandProcessor + { +public: + virtual TInt ProcessCommand(TInt8 cmd, FT_Vector &start, FT_Vector &end) = 0; + }; + +class COutlineStringBuilder: public MVGCommandProcessor, public CBase + { +private: + TUint8 *iBuffer; + TUint8 *iCur; + TInt iLen; + + +public: + COutlineStringBuilder():iBuffer(0), iLen(2000) + { + iBuffer = (TUint8 *)User::Alloc(iLen); + iCur = iBuffer; + } + + ~COutlineStringBuilder() + { + // ownership of the buffer is transferred to the caller. + } + + + TUint8 *GetBuffer(TInt &aLen) + { + aLen = iCur - iBuffer; + *iCur = '\0'; + return iBuffer; + } + + TInt AppendChar(char ch) + { + *(iCur++) = ch; + return 0; + } + + TInt AppendFTPos(FT_Pos n) + { + int divisor = 1; + FT_Pos tmp = (n > 0) ? n : (-n); + while (tmp/divisor >= 10) { + divisor *= 10; + } + + if (n < 0) + { + AppendChar('-'); + } + + for ( ; divisor >= 1; divisor /= 10) + { + AppendChar('0' + tmp/divisor); + tmp = tmp % divisor; + } + + return 0; + } + + TInt AppendCoord(FT_Pos x, FT_Pos y) + { + AppendFTPos(x); + AppendChar(','); + AppendFTPos(y); + AppendChar(' '); + return 0; + } + + TInt + ProcessCommand(TInt8 cmd, FT_Vector &start, FT_Vector &end) + { + FT_Vector *st = &start; + + if (iCur + 64 > iBuffer + iLen) + { + TUint distance = iCur - iBuffer; + iLen += 1000; + TUint8 *newBuffer = (TUint8 *)User::ReAlloc(iBuffer, iLen); + iBuffer = newBuffer; + iCur = iBuffer + distance; + } + + if (VG_CMD_MOVETO == cmd) + { + AppendChar('M'); + AppendCoord(start.x, start.y); + } + else if (VG_CMD_LINETO == cmd) + { + AppendChar('L'); + AppendCoord(end.x, end.y); + } + else if (VG_CMD_CONICTO == cmd) + { + AppendChar('Q'); + AppendCoord((st+1)->x, (st+1)->y); + AppendCoord(end.x, end.y); + } + else if (VG_CMD_CUBICTO == cmd) + { + AppendChar('Q'); + AppendCoord((st+1)->x, (st+1)->y); + AppendCoord((st+2)->x, (st+2)->y); + AppendCoord(end.x, end.y); + } + else if (VG_CMD_CLOSE == cmd) + { + AppendChar('Z'); + AppendChar(' '); + } + + return KErrNone; + } + }; + + +class COutlineConvDirector: public CBase { +private: + MVGCommandProcessor *iProcessor; + const FT_Outline *iOutline; + FT_Outline iNewOutline; + + +private: + char + GetNextPointType(char aTag) + { + char ret = FT_CURVE_TAG(aTag); + if (FT_CURVE_TAG_ON == ret) + { + ret = 0; + } + else if (FT_CURVE_TAG_CONIC == ret) + { + ret = 1; + } + else if (FT_CURVE_TAG_CUBIC == ret) + { + ret = 2; + } + else + { + __ASSERT_DEBUG(0, User::Panic(_L("IncorrectState"), -1)); + } + return ret; + } + + TInt SwapPoints(const TInt i1, const TInt i2) + { + FT_Vector tmpVector = iOutline->points[i1]; + char tmpTags = iOutline->tags[i1]; + iOutline->points[i1] = iOutline->points[i2]; + iOutline->tags[i1] = iOutline->tags[i2]; + iOutline->points[i2] = tmpVector; + iOutline->tags[i2] = tmpTags; + return 0; + } + + TInt MoveFirstOnPointToBeginning(const TInt aStartIndex, const TInt aEndIndex) + { + /* Contours of three or more points are valid, and single points + * (reference points, technically not contours) are also valid as + * special cases in TrueType. + */ + char curTag = FT_CURVE_TAG(iOutline->tags[aStartIndex]); + + // so a contour having only one point which is 'off' is invalid! + __ASSERT_DEBUG(!(aEndIndex - aStartIndex == 0 && FT_CURVE_TAG_ON != curTag), + User::Panic(_L("Contour consisting of 1 'off' point."), -1)); + + /* Contours consisting of two points are not a valid configuration. */ + __ASSERT_DEBUG(aEndIndex - aStartIndex != 1, + User::Panic(_L("Contour consisting of two points."), -1)); + + if (FT_CURVE_TAG_ON == curTag) + { + return KErrNone; + } + TInt firstOnIndex = -1; + TInt i = 0; + for (i = 1+aStartIndex; i < aEndIndex; ++i) + { + if (FT_CURVE_TAG_ON == FT_CURVE_TAG(iOutline->tags[i])) + { + firstOnIndex = i; + break; + } + } + __ASSERT_DEBUG(-1 != firstOnIndex, + User::Panic(_L("Contour containing no 'on' point."), -1)); + + for (i = firstOnIndex-1; i >= aStartIndex; --i) + { + for (TInt j = i; j < aEndIndex; ++j) + { + SwapPoints(j, j+1); + } + } + + return KErrNone; + } + + TInt + ConvertContour(const TInt aStartIndex, const TInt aEndIndex) + { + /* Contours consisting of two + * points are not a valid configuration. + */ + __ASSERT_DEBUG(aEndIndex - aStartIndex != 1, + User::Panic(_L("Contour consisting of two points."), -1)); + + TInt state = CONTOUR_STATE_NOT_STARTED, newState = -1; + TInt cmdStart = aStartIndex, cmdCur = 0, command = -1; + + char ptype = GetNextPointType(iNewOutline.tags[cmdStart]); + __ASSERT_DEBUG(0 == ptype, User::Panic(_L("IncorrectState"), -1)); + state = CONTOUR_STATE_START; + iProcessor->ProcessCommand(VG_CMD_MOVETO, + iNewOutline.points[aStartIndex], iNewOutline.points[aStartIndex]); + + + for (cmdCur = cmdStart + 1; cmdCur <= aEndIndex; ++cmdCur) + { + ptype = GetNextPointType(iNewOutline.tags[cmdCur]); + newState = StateTransitions[state][ptype]; + __ASSERT_DEBUG(-1 != newState, User::Panic(_L("IncorrectState"), -1)); + command = OutputCommands[state][ptype]; + __ASSERT_DEBUG(-1 != command, User::Panic(_L("IncorrectState"), -1)); + + if (VG_CMD_NONE != command) + { + iProcessor->ProcessCommand(command, + iNewOutline.points[cmdStart], iNewOutline.points[cmdCur]); + cmdStart = cmdCur; + } + state = newState; + } + + if (CONTOUR_STATE_CONIC == state) + { + iProcessor->ProcessCommand(VG_CMD_CONICTO, iNewOutline.points[cmdStart], + iNewOutline.points[aStartIndex]); + } + else if (CONTOUR_STATE_CUBIC2 == state) + { + iProcessor->ProcessCommand(VG_CMD_CUBICTO, iNewOutline.points[cmdStart], + iNewOutline.points[aStartIndex]); + } + iProcessor->ProcessCommand(VG_CMD_CLOSE, + iNewOutline.points[aStartIndex], iNewOutline.points[aStartIndex]); + + return KErrNone; + } + + + TInt Preprocess() + { + /* two successive conic "off" points forces the rasterizer to + * create (during the scan-line conversion process exclusively) a + * virtual "on" point amidst them, at their exact middle. + */ + char prevTag = FT_CURVE_TAG(iOutline->tags[0]), currentTag = 0; + TInt numNewPoints = 0; + TInt contourIndex = 0; + + iNewOutline.contours = 0; + iNewOutline.n_contours = iOutline->n_contours; + iNewOutline.contours = (short *) + User::Alloc(iNewOutline.n_contours * sizeof(short)); + + if (0 == iOutline->contours[0]) + { + iNewOutline.contours[0] = iOutline->contours[0]; // == 0 + ++contourIndex; + } + for (TInt i = 1; i < iOutline->n_points; ++i) + { + currentTag = FT_CURVE_TAG(iOutline->tags[i]); + if (FT_CURVE_TAG_CONIC == prevTag && prevTag == currentTag) + { + numNewPoints++; + } + prevTag = currentTag; + if (i == iOutline->contours[contourIndex]) + { + iNewOutline.contours[contourIndex] = + iOutline->contours[contourIndex] + numNewPoints; + ++contourIndex; + } + } + + + iNewOutline.n_points = iOutline->n_points + numNewPoints; + iNewOutline.flags = iOutline->flags; + + iNewOutline.points = 0; + iNewOutline.tags = 0; + + iNewOutline.points = (FT_Vector *) + User::Alloc(iNewOutline.n_points * sizeof(FT_Vector)); + + if (iNewOutline.contours) + { + iNewOutline.tags = (char *) + User::Alloc(iNewOutline.n_points * sizeof(char)); + } + + // copy the 'points' and 'tags' array, inserting new points + // when necessary. + TInt oldIndex = 0, newIndex = 0; + for ( ; oldIndex < iOutline->n_points; ++oldIndex) + { + char oldTag = FT_CURVE_TAG(iOutline->tags[oldIndex]); + iNewOutline.points[newIndex] = iOutline->points[oldIndex]; + iNewOutline.tags[newIndex] = iOutline->tags[oldIndex]; + + if (FT_CURVE_TAG_CONIC == oldTag && + oldIndex + 1 < iOutline->n_points) + { + char nextTag = FT_CURVE_TAG(iOutline->tags[oldIndex+1]); + // insert a new 'on' point when there are two consecutive + // 'conic off' points. + if (oldTag == nextTag) + { + newIndex++; + FT_Vector *cur = &(iOutline->points[oldIndex]); + FT_Vector *next = &(iOutline->points[oldIndex + 1]); + iNewOutline.points[newIndex].x = (cur->x + next->x)/2; + iNewOutline.points[newIndex].y = (cur->y + next->y)/2; + iNewOutline.tags[newIndex] = FT_CURVE_TAG_ON; + } + } + newIndex++; + } + + return 0; + } + +public: + COutlineConvDirector():iProcessor(0), iOutline(0) + { + // a null constructor + iNewOutline.contours = 0; + iNewOutline.tags = 0; + iNewOutline.points = 0; + } + + ~COutlineConvDirector() + { + User::Free(iNewOutline.contours); + User::Free(iNewOutline.points); + User::Free(iNewOutline.tags); + } + + TInt + ConvertOutline(const FT_Outline &aOutline, MVGCommandProcessor *aProcessor) + { + if (0 != aOutline.n_contours) + { + iProcessor = aProcessor; + iOutline = &aOutline; + + MoveFirstOnPointToBeginning(0, iOutline->contours[0]); + TInt i = 0; + for (i = 1; i < iOutline->n_contours; ++i) + { + MoveFirstOnPointToBeginning(iOutline->contours[i-1]+1, iOutline->contours[i]); + } + + Preprocess(); + + ConvertContour(0, iNewOutline.contours[0]); + for (i = 1; i < iNewOutline.n_contours; ++i) + { + ConvertContour(iNewOutline.contours[i-1]+1, iNewOutline.contours[i]); + } + } + else + { + RDebug::Printf("Zero contour in outline: missing glyph."); + FT_Vector dummyVector; + aProcessor->ProcessCommand(VG_CMD_CLOSE, dummyVector, dummyVector); + } + return KErrNone; + } +}; + + +TInt CFreeTypeFontFile::GetGlyphOutline(TInt aFaceIndex, TUint aCode, + TBool aIsGlyphId, TBool, TAny*& aOutline, TInt &aLength) + { + // The 4th param 'aHinted' is ignored in this reference implementation. + // Need to add it back and implement accordingly if freetype is used in + // production code. + CFaceListItem *faceList = LoadFaceL(aFaceIndex); + FT_Face face = faceList->Face(); + TUint code = aCode; + if (!aIsGlyphId) + { + code = FT_Get_Char_Index(face, aCode); + if (0 == code) + { + return KErrNotFound; + } + } + + TInt ret = FT_Load_Glyph(face, code, + FT_LOAD_NO_SCALE | FT_LOAD_IGNORE_TRANSFORM); + + if (0 != ret) + { + return KErrUnknown; + } + + COutlineStringBuilder strBuilder; + if (0 != strBuilder.GetBuffer(aLength)) + { + FT_Outline &outline = face->glyph->outline; + + COutlineConvDirector d; + d.ConvertOutline(outline, &strBuilder); + } + else + { + return KErrNoMemory; + } + + TUint8 *buf = strBuilder.GetBuffer(aLength); + RDebug::Printf("length of buffer is %d\n", aLength); + RDebug::Printf("Outline for glyph %d: \n", aCode); + RDebug::Printf("%s", buf); + aOutline = (TAny*)buf; + + return KErrNone; + } +TAny* CFreeTypeFontFile::GetTrueTypeTable(TInt& aError, TInt aFaceIndex, + TUint32 aTag, TInt* aLength) + { + aError = KErrNone; + + // already loaded? + TPtrC8* table = iTableStore.Find(aTag); + if (table) + { + if (aLength) + *aLength = table->Length(); + return (TAny*) table->Ptr(); + } + + CFaceListItem* faceList = LoadFaceL( aFaceIndex ); + FT_Face face = faceList->Face(); + TT_Face ttface = (TT_Face)face; + + // locate the table & find the length + TUint32 length = 0; + FT_Error fterror = ttface->goto_table(ttface, aTag, face->stream, &length); + if (fterror != 0) + { + aError = KErrNotFound; + return 0; + } + + // allocate memory for the table + unsigned char * buffer = reinterpret_cast( + User::Alloc(length)); + if ( !buffer ) + { + aError = KErrNoMemory; + return 0; + } + + // read the table + fterror = FT_Load_Sfnt_Table( face, aTag, 0, buffer, &length ); + + if (fterror != 0) + { + User::Free(buffer); + aError = KErrNotFound; + return 0; + } + + // store the table + aError = iTableStore.Insert(aTag, TPtrC8(buffer, length)); + if (aError != KErrNone) + { + User::Free(buffer); + return 0; + } + + if(aLength) + *aLength = length; + + return buffer; + } + +void CFreeTypeContext::TranslateMonochromeGlyphBitmap(FT_GlyphSlot aGlyphSlot,TOpenFontGlyphData* aGlyphData) + { + const TInt width = aGlyphSlot->bitmap.width; + const TInt height = aGlyphSlot->bitmap.rows; + const TInt width_bytes = aGlyphSlot->bitmap.pitch; + + StartGlyph(aGlyphData); + const TUint8* p = (const TUint8*)aGlyphSlot->bitmap.buffer; + TInt row = 0; + while (row < height) + { + // Find the number of repeating or non-repeating rows (up to 15) + TInt count = 1; + TBool repeating = FALSE; + const TUint8* cur = p; + while (count < 15 && row + count < height) + { + const TUint8* prev = cur; + cur += width_bytes; + const TBool same = Mem::Compare(prev,width_bytes,cur,width_bytes) == 0; + if (count == 1) + repeating = same; + else if (repeating != same) + break; + count++; + } + + // Write a 0 for repeating or a 1 for non-repeating. + WriteGlyphBit(repeating ? 0 : 1); + + // Write the count. + WriteGlyphBit(count & 1 ? 1 : 0); + WriteGlyphBit(count & 2 ? 1 : 0); + WriteGlyphBit(count & 4 ? 1 : 0); + WriteGlyphBit(count & 8 ? 1 : 0); + + // Write a single repeating row or all the non-repeating rows. + const TInt rows_written = repeating ? 1 : count; + cur = p; + for (TInt row_written = 0; row_written < rows_written; row_written++, cur += width_bytes) + { + TInt col = 0; + for (TInt byte = 0; byte < width_bytes; byte++) + { + unsigned char x = cur[byte]; + for (TInt bit = 0; bit < 8 && col < width; bit++, col++, x <<= 1) + WriteGlyphBit(x & 128 ? 1 : 0); + } + } + + row += count; + p += width_bytes * count; + } + + EndGlyph(); + } + +void CFreeTypeContext::TranslateAntiAliasedGlyphBitmap(FT_GlyphSlot aGlyphSlot, TOpenFontGlyphData* aGlyphData) + { + const TInt width = aGlyphSlot->bitmap.width; + const TInt height = aGlyphSlot->bitmap.rows; + const TInt width_bytes = aGlyphSlot->bitmap.pitch; + + StartGlyph(aGlyphData); + const TUint8* p = (const TUint8*)aGlyphSlot->bitmap.buffer; + /* + Some fonts include embedded bitmaps for certain characters and certain sizes. + These embedded bitmaps can be 1 bit per pixel. + */ + if (aGlyphSlot->bitmap.pixel_mode == ft_pixel_mode_mono) + { + // how many whole bytes per line + const TInt byteCount = width / 8; + // how many bits used in the last byte - if any. + const TInt bitCount = width % 8; + for (TInt row = 0; row < height; row++) + { + for (TInt col = 0; col < byteCount; col++) + { + WriteMonoData(*p++,8); + } + if (bitCount > 0) + { + WriteMonoData(*p++,bitCount); + } + } + } + else // assume 8 bits per pixel + { + TInt row = 0; + while (row < height) + { + const TUint8* q = p; + for (TInt i = 0; i < width; i++, q++) + WriteGlyphByte(*q); + row++; + p += width_bytes; + } + } + EndGlyph(); + } + +void CFreeTypeContext::WriteMonoData(TUint8 aData, TInt aBitCount) + { + for (TInt j = 7; j >= 8 - aBitCount; j--) + { + WriteGlyphByte((1<LoadFaceL(iFileName, aFaceIndex); + return 0 != FT_Get_Char_Index(face->Face(), aCode); + } + +CFaceListItem::CFaceListItem() + { + } + +CFaceListItem* CFaceListItem::NewL(CFreeTypeContext* aContext, const TText8* aFileName, TInt aFaceIndex) + { + CFaceListItem* item = new(ELeave) CFaceListItem; + CleanupStack::PushL(item); + item->ConstructL(aContext, aFileName, aFaceIndex); + CleanupStack::Pop(); + return item; + } + +void CFaceListItem::ConstructL(CFreeTypeContext* aContext, const TText8* aFileName, TInt aFaceIndex) + { + // Open the face and select the Unicode character map. + iFileName = aFileName; + iFaceIndex = aFaceIndex; + TInt error = FT_New_Face( + aContext->Library(), (const char *) aFileName, aFaceIndex, &iFace); + if (!error) + error = FT_Select_Charmap(iFace, ft_encoding_unicode); + if (error) + { + iFace = NULL; + User::Leave(KErrGeneral); + } + } + +CFaceListItem::~CFaceListItem() + { + FT_Done_Face(iFace); + } + +FT_Face CFaceListItem::Face() const + { + return iFace; + } + +CFaceList::CFaceList() + { + } + +CFaceList::~CFaceList() + { + CFaceListItem* next = NULL; + for (CFaceListItem* p = iFirstItem; p; p = next) + { + next = p->iNext; + delete p; + } + } + +CFaceListItem* CFaceList::LoadFaceL( + CFreeTypeContext* aContext, + const TText8* aFileName, + TInt aFaceIndex) + { + CFaceListItem* prev = NULL; + for (CFaceListItem* p = iFirstItem; p; prev = p, p = p->iNext) + { + if (p->iFileName == aFileName && p->iFaceIndex == aFaceIndex) // found; move to head of list + { + if (prev) + { + prev->iNext = p->iNext; + p->iNext = iFirstItem; + iFirstItem = p; + } + return p; + } + } + + // Load and add a new item to the head of the list + CFaceListItem* new_item = CFaceListItem::NewL(aContext, aFileName, aFaceIndex); + new_item->iNext = iFirstItem; + iFirstItem = new_item; + + + /* + If too much memory has been used delete item(s) from the end of the list, but don't + delete the last item, which is the one requested. + */ +#ifdef _DEBUG + TInt DeleteCount = 0; +#endif + + while (iFirstItem->iNext && MemoryUsed() > EMemoryThreshold) + { +#ifdef _DEBUG + DeleteCount++; +#endif + DeleteLastFace(); + } + +#ifdef _DEBUG + if(DeleteCount) + { + RDebug::Print(_L("Delete %d in Face Cache: MemoryUsed() > EMemoryThreshold"),DeleteCount); + } +#endif + + return iFirstItem; + } + +void CFaceList::DeleteFace(const TText8* aFileName, TInt aFaceIndex) + { + CFaceListItem* prev = NULL; + for (CFaceListItem* p = iFirstItem; p; prev = p, p = p->iNext) + { + if (p->iFileName == aFileName && p->iFaceIndex == aFaceIndex) // found; delete it + { + if (prev) + prev->iNext = p->iNext; + else + iFirstItem = p->iNext; + delete p; + return; + } + } + } + +void CFaceList::DeleteLastFace() + { + CFaceListItem* p = iFirstItem; + if (p) + { + CFaceListItem* prev = NULL; + while (p && p->iNext) + { + prev = p; + p = p->iNext; + } + if (prev) + prev->iNext = NULL; + else + iFirstItem = NULL; + delete p; + } + }