Using CartoType on Symbian OS

CartoType for Symbian OS is delivered as a single DLL called cartotype.dll, with an import library, cartotype.lib. Linking to this library, and including the appropriate CartoType header files in your C++ source code, gives your application access to all the standard CartoType features. The library contains the FreeType-based typeface accessor, with drivers for TrueType and Adobe Type 1 fonts. It contains a data accessor for the CTM1 (CartoType Map Data format 1) database format. Other typeface accessors and map data accessors can be provided by the user and loaded as necessary.

Initialization

Here is the code from a sample Symbian OS application that initializes CartoType. The main stages are:

A brief note about the CartoType API style. The CartoType API is designed to be safe and simple. When you create an object that needs complex initialization that might fail, for example from lack of memory, you call a static constructor function call New that will only return an object if it was possible to construct the object with no errors. The actual constructor of this class is private. (This idiom is also used in Symbian OS.)

Thus to create the CartoType engine we use this code:

	CartoType::TResult error = 0;
	iEngine = CartoType::CEngine::New(error);

If the call fails, iEngine is null, and error contains a non-zero error code. If iEngine is non-null it is guaranteed to be perfectly constructed and usable.

The other code in this function is the standard 'boilerplate' code required by simple Symbian OS applications.

void CCartoTypeDemoAppUi::ConstructL()
    	{
	// Complete construction of base class.
    	BaseConstructL();

	// Create the CartoType engine
	CartoType::TResult error = 0;
	iEngine = CartoType::CEngine::New(error);
	if (error)
		User::Leave(KErrGeneral);

	// Create GC.
	iGc = CartoType::C24BitColorBitmapGraphicsContext::New(error,*iEngine,ClientRect().Width(),ClientRect().Height());
	if (error)
		User::Leave(KErrGeneral);

	// Load FreeType.
	CartoType::CTypefaceAccessor* a = CartoType::CFreeTypeAccessor::New(error);
	if (error)
		User::Leave(KErrGeneral);
	error = iEngine->LoadTypefaceAccessor(a);
	if (error)
		{
		delete a;
		User::Leave(KErrGeneral);
		}

	// Load test fonts.
	CartoType::CString filename;
	filename.Set("d:\\cartotype\\font\\cmss12.pfb");
	error = iEngine->LoadTypefaceResource(filename);
	if (error)
		User::Leave(KErrGeneral);

	// Load the default style sheet.
	RFs file_session;
	User::LeaveIfError(file_session.Connect());
	CleanupClosePushL(file_session);
	RFile file;
	User::LeaveIfError(file.Open(file_session,_L("d:\\cartotype\\data\\style_sheet\\default_style.xml"),EFileRead));
	CleanupClosePushL(file);
	User::LeaveIfError(file.Size(iDefaultStyleSheetLength));
	iDefaultStyleSheet = new(ELeave) TUint8[iDefaultStyleSheetLength];
	TPtr8 ptr(iDefaultStyleSheet,iDefaultStyleSheetLength);
	User::LeaveIfError(file.Read(ptr));
	CleanupStack::PopAndDestroy(&file);
	CleanupStack::PopAndDestroy(&file_session);

	// Create the off-screen Symbian OS bitmap.
	TDisplayMode display_mode = EColor64K;
	iBitmap = new(ELeave) CFbsBitmap;
	if (iBitmap)
		User::LeaveIfError(iBitmap->Create(TSize(ClientRect().Width(),ClientRect().Height()),display_mode));

	// Create the view.
	iView = CCartoTypeDemoView::NewL(ClientRect(),*this,((CCartoTypeDemoDocument*)iDocument)->DataBase());
	
	// Add the view to the control stack.
	AddToStackL(iView);
	
	// Prevent the control environment from panicking the app if file handles are left open.
	iCoeEnv->DisableExitChecks(true);
	}

Loading the data

The Symbian OS application architecture uses the document-view-controller model. The data is managed by a document object.

The filename is supplied by the document construction function, and in this demonstration application we hard-code it:

CCartoTypeDemoDocument* CCartoTypeDemoDocument::NewL(CEikApplication& aApp)
	{
	CCartoTypeDemoDocument* doc = new(ELeave) CCartoTypeDemoDocument(aApp);
	CleanupStack::PushL(doc);
	doc->ConstructL(_L("d:\\cartotype\\data\\london\\london.ctm1"));
	CleanupStack::Pop(doc);
	return doc;
	}

We create a database accessor object that can read CTM1 files, and ask it to create a database object:

void CCartoTypeDemoDocument::ConstructL(const TDesC& aMapFile)
	{
	CartoType::CMapDataAccessor* accessor =  CartoType::CType1MapDataAccessor::New();
	CartoType::TText filename(aMapFile.Ptr(),aMapFile.Length());
	CartoType::TResult error = 0;
	iDataBase = accessor->NewDataBase(error,filename);
	if (error)
		User::Leave(KErrGeneral);
	delete accessor;
	}

Creating the map object

The function call

iView = CCartoTypeDemoView::NewL(ClientRect(),*this,((CCartoTypeDemoDocument*)iDocument)->DataBase());

in CCartoTypeDemoAppUi::ConstructL() creates a view control that is responsible for drawing the map. The map is represented by a CartoType::CMap object. The main stages in constructing the map object are:

void CCartoTypeDemoView::ConstructL(const TRect& aRect)
	{
	// Set the projection parameters
	CartoType::TRect extent(iDataBase.Extent());
	if (iDataBase.Grid().iPointFormat == CartoType::EDegreePointFormat)
		iProjectionParam.iCentralMeridian = ((CartoType::TFixed(extent.Left(),16).Rounded() / 3) - 1) * 3;

	iProjectionParam.iScale = 65536 * aRect.Width() / iDataBase.Extent().Width();
	CartoType::TCoordinateTransform* projection = NewProjectionL();
	CartoType::TRect pixel_rect;
	CartoType::TResult error = projection->InputRectToPixelRect(extent,pixel_rect);
	CartoType::TFixed height = pixel_rect.Height();
	CartoType::TFixed width = pixel_rect.Width();
	CartoType::TFixed y_scale_factor(aRect.Height());
	y_scale_factor /= height;
	CartoType::TFixed x_scale_factor(aRect.Width());
	x_scale_factor /= width;
	CartoType::TFixed scale_factor = x_scale_factor < y_scale_factor ? x_scale_factor : y_scale_factor; 

	iProjectionParam.iScale *= scale_factor; // number of pixels to a degree
	delete projection;
	projection = NewProjectionL();

	error = projection->InputRectToPixelRect(extent,pixel_rect);
	iProjectionParam.iShift = CartoType::TPoint(-pixel_rect.Left(),-pixel_rect.Bottom());
	iProjectionParam.iTransform = CartoType::TTransform(1,0,0,-1,0,0);
	delete projection;
	projection = NewProjectionL();

	// Create the map object.
	iMap = CartoType::CMap::New(error,&iAppUi.Engine(),&iDataBase,projection,aRect.Width(),aRect.Height());
	if (!iMap)
		{
		if (error == CartoType::KErrorNoMemory)
			User::Leave(KErrNoMemory);
		else
			User::Leave(KErrGeneral);
		}

	// The control is a window-owning control.
	CreateWindowL();
	Window().SetShadowDisabled(true);
	Window().SetRequiredDisplayMode(EColor64K);
	SetRect(aRect);

	// Draw the map.
	UpdateL();

	ActivateL();
	}

Drawing the map

The function given in the last section, void CCartoTypeDemoView::ConstructL, contains a call to void CCartoTypeDemoView::UpdateL. This does nothing but call void CCartoTypeDemoAppUi::DrawMap:

void CCartoTypeDemoAppUi::DrawMap(CartoType::CMap& aMap,const TRect& aRect)
	{
	CartoType::TMemoryInputStream style_sheet(iDefaultStyleSheet,iDefaultStyleSheetLength);
	aMap.Draw(*iGc,CartoType::TPoint(0,0),&style_sheet);

	/*
	Copy the image to the Symbian-format bitmap.
	This code supports EColor64K only.
	*/
	ASSERT(iBitmap->DisplayMode() == EColor64K);
	TRect r = aRect;
	int w = r.Width();
	int h = r.Height();
	const TUint8* row_start = iGc->Bitmap().Data();
	int row_bytes = iGc->Bitmap().RowBytes();

	iBitmap->LockHeap();
	TUint32* dest_row_start = iBitmap->DataAddress();
	int dest_row_words = (w + 1) / 2;
	for (int y = 0; y < h; y++, row_start += row_bytes, dest_row_start += dest_row_words)
		{
		const TUint8* p = row_start;
		TUint32* q = dest_row_start;
		for (int x = 0; x < w; x++, p += 6, q++)
			{
			*q = ((p[5] & 0xF8) << 24) |
				 ((p[4] & 0xFC) << 19) |
				 ((p[3] & 0xF8) << 13) |
				 ((p[2] & 0xF8) << 8) |
				 ((p[1] & 0xFC) << 3) |
				 (p[0] >> 3);
			}
		}
	iBitmap->UnlockHeap();
	}

Nearly all this code is concerned with copying the image from the 24bpp CartoType bitmap to the 16bpp Symbian OS bitmap. The first two statements do the actual drawing. All that is required is to create a stream to read the style sheet, then call CartoType::CMap::Draw, passing the destination graphics context, the top left corner of the map, and the style sheet stream.

The style sheet is an XML document that controls the way the map is drawn in great detail. It determines which layers are drawn in which order, and the colors, transparency, sizes, typefaces and other details.