filehandling/richtexttohtmlconverter/Src/RT2HTMCV.CPP
changeset 0 2e3d3ce01487
equal deleted inserted replaced
-1:000000000000 0:2e3d3ce01487
       
     1 // Copyright (c) 1997-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     2 // All rights reserved.
       
     3 // This component and the accompanying materials are made available
       
     4 // under the terms of "Eclipse Public License v1.0"
       
     5 // which accompanies this distribution, and is available
       
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     7 //
       
     8 // Initial Contributors:
       
     9 // Nokia Corporation - initial contribution.
       
    10 //
       
    11 // Contributors:
       
    12 //
       
    13 // Description:
       
    14 // ConArc plug-in converter.
       
    15 // Converts EPOC Rich Text files (i.e. externalised CRichText objects) to HTML files.
       
    16 // 1. Does not support object->object conversions
       
    17 // 2. Therefore does not support embedded objects
       
    18 // 3. No output encoding for UNICODE has beed decided upon
       
    19 // 
       
    20 //
       
    21 
       
    22 #include <e32base.h>
       
    23 #include <e32def.h>
       
    24 #include <e32std.h>
       
    25 #include <s32file.h>
       
    26 #include <txtuids.h>
       
    27 #include <txtetext.h>
       
    28 #include <txtrich.h>
       
    29 #include <conarc.h>
       
    30 #include <bautils.h>
       
    31 
       
    32 #include "RT2HTMCV.H"
       
    33 #include "HTMLDEFS.H"
       
    34 #include <ecom/ecom.h>
       
    35 #include <ecom/implementationproxy.h>
       
    36 
       
    37 
       
    38 //-------------------------------------------------------------------------------------------------
       
    39 const TInt  KMaxTagLength		= 256;
       
    40 const TText KCharacterSpace		= 0x20;
       
    41 //-------------------------------------------------------------------------------------------------
       
    42 #ifdef _UNICODE
       
    43 	HBufC8* ConvertUnicodeToUTF8LC(const TDesC16& uniText);
       
    44 #endif // _UNICODE
       
    45 
       
    46 
       
    47 _LIT(KTempFileExt, ".dat");
       
    48 
       
    49 typedef TPckgBuf<CRichText*> TRTPtrBuffer;
       
    50 
       
    51 /**
       
    52 -------------------------------------------------------------------------------------------------
       
    53  The required ConArc classes: CCtrToHTMLConverter and CCrtToHTMLLibrary
       
    54 -------------------------------------------------------------------------------------------------
       
    55  Class CCrtToHTMLConverter.
       
    56  The CConverterBase derived class which is responsable for performing the conversion operation.
       
    57  1. Supports File->File conversions and output to a RWriteStream
       
    58  2. For the stream interface a temporary file store is created for reading the rich text object.
       
    59     this is deleted once the conversion is complete
       
    60  3. The conversion is performed on a paragraph by paragraph basis
       
    61  4. iHTMLOut is used to output the resulting HTML. For File->File conversions a new file stream store
       
    62     is opened based on the user provided file name. For the streaming interface iHTMLOut is assigned 
       
    63     to a RWriteStream& provided by the user
       
    64 */
       
    65 CCrtToHTMLConverter::CCrtToHTMLConverter() : iHTMLOut(iFileStream)
       
    66 	{ 
       
    67 	}
       
    68 
       
    69 void CCrtToHTMLConverter::ConvertL(const TFileName& aSourceFile, const TFileName& aTargetFile, MConverterUiObserver* aObserver /*= NULL*/)
       
    70 //
       
    71 // Configures and performs a file -> file conversion synchronously
       
    72 //
       
    73 	{
       
    74 	iObserver = aObserver;
       
    75 	PrepForConversionL(aSourceFile, aTargetFile);
       
    76 
       
    77 	while(DoConvertL())
       
    78 		{
       
    79 		}
       
    80 	ASSERT(iParaRemain == 0);
       
    81 	}
       
    82 
       
    83 void CCrtToHTMLConverter::ConvertAL(const TFileName& aSourceFile, const TFileName& aTargetFile, MConverterUiObserver* aObserver /*= NULL*/)
       
    84 //
       
    85 // Configures the object to perform a file -> file conversion asynchronously
       
    86 //
       
    87 	{
       
    88 	// Cast away the const-ness - for now...
       
    89 	iObserver = aObserver;
       
    90 	PrepForConversionL(aSourceFile, aTargetFile);
       
    91 
       
    92 	if(iObserver)
       
    93 		{
       
    94 		iObserver->MaxSteps(iParaRemain, 0);
       
    95 		}
       
    96 	}
       
    97 
       
    98 /**
       
    99  To create the RReadStream you package a pointer to the CRichText object and create a RDesReadStream:
       
   100  TPckgBuf<CRichText*> buffer(text); //text is the CRichText* to be converted
       
   101  RDesReadStream readStream(buffer); //readStream can now be passed to ConvertObjectL/ConvertObjectAL
       
   102 */
       
   103 void CCrtToHTMLConverter::ConvertObjectL(RReadStream& aReadStream, RWriteStream& aWriteStream, MConverterUiObserver* aObserver)
       
   104 	{
       
   105 	iObserver = aObserver;
       
   106 	PrepForConversionL(aReadStream, aWriteStream);
       
   107 
       
   108 	while(DoConvertL());
       
   109 	
       
   110 	ASSERT(iParaRemain == 0);
       
   111 	}
       
   112 
       
   113 /**
       
   114  To create the RReadStream you package a pointer to the CRichText object and create a RDesReadStream:
       
   115  TPckgBuf<CRichText*> buffer(text); //text is the CRichText* to be converted
       
   116  RDesReadStream readStream(buffer); //readStream can now be passed to ConvertObjectL/ConvertObjectAL
       
   117 */
       
   118 void CCrtToHTMLConverter::ConvertObjectAL(RReadStream& aReadStream, RWriteStream& aWriteStream, MConverterUiObserver* aObserver)
       
   119 	{
       
   120 	iObserver = aObserver;
       
   121 	PrepForConversionL(aReadStream, aWriteStream);
       
   122 
       
   123 	if(iObserver)
       
   124 		{
       
   125 		iObserver->MaxSteps(iParaRemain, 0);
       
   126 		}
       
   127 	}
       
   128 
       
   129 
       
   130 TUid CCrtToHTMLConverter::Uid()
       
   131 //
       
   132 // Returns the UID associated with the conversion operation
       
   133 //
       
   134 	{
       
   135 	return KCrt2HTMLConverterUid;
       
   136 	}
       
   137 
       
   138 TInt CCrtToHTMLConverter::Capabilities()
       
   139 //
       
   140 // Returns EConvertsFiles. Only File->File conversion is supported.
       
   141 //
       
   142 	{
       
   143 	return EConvertsFiles;
       
   144 	}
       
   145 
       
   146 void CCrtToHTMLConverter::CancelConvert()
       
   147 //
       
   148 // Clean up. Doesn't remove half finished file.
       
   149 //
       
   150 	{
       
   151 	ResetMembers();
       
   152 	}
       
   153 
       
   154 CCrtToHTMLConverter::~CCrtToHTMLConverter()
       
   155 	{
       
   156 	ResetMembers();
       
   157 	iFs.Close();
       
   158 	}
       
   159 
       
   160 /**
       
   161  Performs a single step of the conversion. Converts a single paragraph from Rich Text -> HTML
       
   162  THe final aparagrahp of a RichText doc contains a single end of document marker. When we 
       
   163  reach this we can delete the output sink object, which will flush and close the output file.
       
   164 */
       
   165 TBool CCrtToHTMLConverter::DoConvertL()
       
   166 	{
       
   167 	TBool closePara = ETrue;
       
   168 
       
   169 	if(iParaNum == -1)				// We're at the start
       
   170 		{
       
   171 		StartDocumentL();			// Write the HTML header
       
   172 
       
   173 	    iParaNum   = 0;
       
   174 	    iParaLen   = 0;
       
   175 		iParaPos   = 0;
       
   176 		closePara  = EFalse;		// Coz we don't want a </P> tag a the start...
       
   177 		}	
       
   178 	
       
   179 	// Do a single paragraph...
       
   180 	if((iParaPos = iInputText->CharPosOfParagraph(iParaLen, iParaNum++)) >= 0)
       
   181 		{
       
   182 		// Construct a formatting object
       
   183 		CParaFormat* format = CParaFormat::NewLC();
       
   184 
       
   185 		TParaFormatMask  mask;
       
   186 
       
   187 		// Cue, Mr Hack...
       
   188 		// Knock 1 off the document length as a panic prevention measure, as the iParaLength of
       
   189 		// the last paragraph is _beyond_ the end of the document. Doh!
       
   190 		if(iParaNum == iParaCount)
       
   191 			iParaLen -= 1;
       
   192 
       
   193 		iInputText->GetParaFormatL(format, mask, iParaPos, iParaLen);
       
   194 
       
   195 		ASSERT(mask.IsNull());
       
   196 
       
   197 		ProcessParagraphFormatL(format, closePara);	// Put paragraph level formatting
       
   198 		ProcessParagraphTextL(iParaPos, iParaLen);	// Process the paragraph itself
       
   199 		--iParaRemain;
       
   200 
       
   201 		CleanupStack::PopAndDestroy(); // CParaFormat format
       
   202 		if(iParaNum == iParaCount)  // We're at the end of the document, so finish up
       
   203 			{
       
   204 			ASSERT(iParaPos + iParaLen == iInputText->DocumentLength());
       
   205 			EndDocumentL();
       
   206 			// Commit the file
       
   207 			iHTMLOut.CommitL();
       
   208 			iHTMLOut.Close();
       
   209 			return EFalse;
       
   210 			}
       
   211 		}
       
   212 	else
       
   213 		{
       
   214 		Panic(EHTMLBeyondLastPara);
       
   215 		}
       
   216 	return ETrue;
       
   217 }
       
   218 
       
   219 /**
       
   220  Both File->File and stream converstions use a file store to read the input CRichText object
       
   221  In File->File conversions the user creates the rich text file store prior to calling ConvertL()/ConvertAL()
       
   222  In stream conversions CCrtToHTMLConverter creates a temporary file store
       
   223 */
       
   224 void CCrtToHTMLConverter::RestoreInputTextL(const TFileName& aSourceFile)
       
   225 	{
       
   226 	delete iInputText;
       
   227 	iInputText = NULL;
       
   228 	
       
   229 	iParaLayer = CParaFormatLayer::NewL();
       
   230 	iCharLayer = CCharFormatLayer::NewL();
       
   231 	iInputText = CRichText::NewL(iParaLayer, iCharLayer, CEditableText::EFlatStorage);
       
   232 
       
   233 	// Get the store and restore from it to the CRichText object
       
   234 	iStore = CDirectFileStore::OpenL(iFs, aSourceFile, EFileRead | EFileShareReadersOnly);
       
   235 
       
   236 	if(iStore->Type()[0] != KDirectFileStoreLayoutUid)
       
   237           User::Leave(KErrUnknown);     
       
   238 	
       
   239    	iInputText->RestoreL(*iStore, iStore->Root());
       
   240 	
       
   241 	iParaRemain = iInputText->ParagraphCount();
       
   242 	iParaCount  = iParaRemain;
       
   243 	}
       
   244 
       
   245 /**
       
   246   Configures the object prior to performing a conversion. 
       
   247   1. Connects to the file server
       
   248   2. Restores the input CRichText object from the given file
       
   249   3. Prepares the output context
       
   250 */
       
   251 void CCrtToHTMLConverter::PrepForConversionL(const TFileName& aSourceFile, const TFileName& aTargetFile)
       
   252 	{
       
   253 	ResetMembers();
       
   254 	// ResetMembers() will close the file server session to ensure it's flushed and any temporary files are deleted 
       
   255 	User::LeaveIfError(iFs.Connect());
       
   256 
       
   257 	iHTMLOut = iFileStream;
       
   258 	RestoreInputTextL(aSourceFile);
       
   259 
       
   260 	// Set up the output context
       
   261 	User::LeaveIfError(((RFileWriteStream&)iHTMLOut).Replace(iFs, aTargetFile, EFileStream|EFileWrite|EFileShareExclusive));
       
   262 	}
       
   263 	
       
   264 /**
       
   265   Configures the object prior to performing a conversion. 
       
   266   1. Connects to the file server
       
   267   2. Assign the output stream
       
   268   3. Copys the read CRichText object to a file store
       
   269   4. Restores our copy of the input CRichText
       
   270 
       
   271  There's no way to copy a CRichText object without first externalizing it in some way. The normal mechanism for 
       
   272  saving and loading CRichText objects is to use Store and Restore. Using Externalize and Internalize is non-trivial
       
   273  with CRichText. The stream interface only gives us a RReadStream& making Restore very difficult. The way around 
       
   274  this is for the user to just stream a pointer to the actual CRichText object. This way the caller dosn't need to 
       
   275  worry about setting up any stores. CCrtToHTMLConverter does the copy.
       
   276  To create the RReadStream you package a pointer to the CRichText object and create a RDesReadStream:
       
   277  TPckgBuf<CRichText*> buffer(text); //text is the CRichText* to be converted
       
   278  RDesReadStream readStream(buffer); //readStream can now be passed to ConvertObjectL/ConvertObjectAL
       
   279 */ 
       
   280 void CCrtToHTMLConverter::PrepForConversionL(RReadStream& aReadStream, RWriteStream& aWriteStream)
       
   281 	{
       
   282 	ResetMembers();
       
   283 	// ResetMembers() will close the file server session to ensure it's flushed and any temporary files are deleted 
       
   284 	User::LeaveIfError(iFs.Connect());
       
   285 	
       
   286 	iHTMLOut = aWriteStream;
       
   287 	
       
   288 	// Unpackage the source rich text pointer
       
   289 	TRTPtrBuffer readBuffer;
       
   290 	aReadStream.ReadL(readBuffer);
       
   291 	CRichText* sourceText = readBuffer();
       
   292 
       
   293 	// The temporary file needs to be unique to allow for concurrent conversions
       
   294 	// This file is deleted on a call to ResetMembers()
       
   295 	TFileName newTempFile;
       
   296 	User::LeaveIfError(iFs.PrivatePath(newTempFile));
       
   297 
       
   298 	newTempFile.AppendNum((TInt)sourceText);
       
   299 	newTempFile.Append(KTempFileExt);
       
   300 	BaflUtils::EnsurePathExistsL(iFs, newTempFile);
       
   301 	iInternalFile = newTempFile.AllocL();
       
   302 		
       
   303 	CDirectFileStore* fileStore = CDirectFileStore::ReplaceL(iFs, *iInternalFile, EFileWrite|EFileShareAny);
       
   304 	CleanupStack::PushL( fileStore );
       
   305 	fileStore->SetTypeL( KDirectFileStoreLayoutUid );
       
   306 	TStreamId streamID = sourceText->StoreL(*fileStore);
       
   307 	fileStore->SetRootL(streamID);
       
   308 	fileStore->CommitL();
       
   309 	CleanupStack::PopAndDestroy( fileStore );
       
   310 	
       
   311 	// Restore our copy of the input rich text object from the newly created file store
       
   312 	RestoreInputTextL(*iInternalFile);
       
   313 	}
       
   314 
       
   315 /**
       
   316  Ensures that any contained objects are desstroyed and sets all member variable to zero(NULL)
       
   317 */
       
   318 void CCrtToHTMLConverter::ResetMembers()
       
   319 	{
       
   320 	iStyleIndex = -1;
       
   321 	iParaNum    = -1;
       
   322 	iParaLen    = 0;
       
   323 	iParaPos    = 0;
       
   324 	iParaCount  = 0;
       
   325 	iParaRemain = 0;
       
   326 	iIndent     = 0;
       
   327 	iOldFmtCount = 0;
       
   328 
       
   329 	iHTMLOut.Close();
       
   330 	delete iInputText;
       
   331 	iInputText = NULL;
       
   332 	delete iParaLayer;
       
   333 	iParaLayer = NULL;
       
   334 	delete iCharLayer;
       
   335 	iCharLayer = NULL;
       
   336 	delete iBullet;
       
   337 	iBullet = NULL;
       
   338 	delete iStore;
       
   339 	iStore = NULL;
       
   340 	if (iFs.Handle()) 
       
   341 		{
       
   342 		if (iInternalFile && BaflUtils::FileExists(iFs,*iInternalFile))
       
   343 			{
       
   344 			iFs.Delete(*iInternalFile);
       
   345 			delete iInternalFile;
       
   346 			iInternalFile = NULL;			
       
   347 			}
       
   348 		// I'm closing the file server to ensure it flushes.
       
   349 		// Otherwise internal files are not always deleted
       
   350 		iFs.Close();
       
   351 		}
       
   352 	}
       
   353 
       
   354 /**
       
   355  Scans the passed buffer, replacing special characters in the source with relevant HTML tags
       
   356  before sending them to be output
       
   357 */
       
   358 void CCrtToHTMLConverter::TranslateL(const TDesC& aBuf)
       
   359 	{
       
   360 	int i = 0;
       
   361 	while(i < aBuf.Length())
       
   362 		{
       
   363 		TText ch = aBuf[i++];
       
   364 		switch(ch)
       
   365 			{
       
   366 			// !! am I picking up all possibles here?
       
   367 			case CEditableText::EPageBreak:
       
   368 			case CEditableText::EPotentialHyphen:
       
   369 			case CEditableText::ENonBreakingHyphen:
       
   370 				break;  // These characters are not emitted.
       
   371 			case CEditableText::EPictureCharacter:
       
   372 				break;
       
   373 			case CEditableText::ELineBreak:
       
   374 				WriteTagL(KHtmlLineBreak);
       
   375 				break;
       
   376 			case CEditableText::ENonBreakingSpace:
       
   377 			case CEditableText::ETabCharacter:
       
   378 				WriteContentL(TPtrC(&KCharacterSpace, 1));
       
   379 				break;
       
   380 			case CEditableText::EParagraphDelimiter:
       
   381 				break;
       
   382 			case KLessThan:
       
   383 				WriteTagL(KHtmlLessThan);
       
   384 				break;
       
   385 			case KGreaterThan:
       
   386 				WriteTagL(KHtmlGreaterThan);
       
   387 				break;
       
   388 			case KAmpersand:
       
   389 				WriteTagL(KHtmlAmpersand);
       
   390 				break;
       
   391 			default:
       
   392 				WriteContentL(TPtrC(&ch, 1));
       
   393 				break;
       
   394 			}
       
   395 		}
       
   396 	}
       
   397 
       
   398 void CCrtToHTMLConverter::WriteTagL(const TDesC8& aTagText)
       
   399 	{
       
   400 	iHTMLOut.WriteL(aTagText);
       
   401 	}
       
   402 
       
   403 void CCrtToHTMLConverter::WriteContentL(const TDesC& aText)
       
   404 	{
       
   405 #ifdef _UNICODE
       
   406 	HBufC8* pBuf = ConvertUnicodeToUTF8LC(aText);
       
   407 	User::LeaveIfNull(pBuf);
       
   408 	iHTMLOut.WriteL(*pBuf);
       
   409 	CleanupStack::PopAndDestroy();
       
   410 #else
       
   411 	iHTMLOut.WriteL(aText);
       
   412 #endif
       
   413 	}
       
   414 
       
   415 void CCrtToHTMLConverter::ProcessParagraphFormatL(const CParaFormat* aFormat, TBool aClosePara)
       
   416 //
       
   417 // Processes any paragraph level formatting (paragraph alignment, list bullets, indentation)
       
   418 //
       
   419 	{
       
   420 	// Close indents
       
   421 	for( ; iIndent ; iIndent--)
       
   422 		WriteTagL(KHtmlBlockquoteEnd);
       
   423 
       
   424 	if(!iBullet && aClosePara)
       
   425 		{
       
   426 		if ( iInsertBlankDivClose )
       
   427 			{
       
   428 			WriteTagL(KHtmlDivBlankEnd);
       
   429 			}
       
   430 		else
       
   431 			{
       
   432 			WriteTagL(KHtmlDivEnd);
       
   433 			}
       
   434 		}
       
   435 
       
   436 	// Process unordered (bulleted) lists
       
   437 	if(iBullet)
       
   438 		{
       
   439 		// Previous paragraph was bulleted
       
   440 		if(!aFormat->iBullet)
       
   441 			{
       
   442 			// End of list
       
   443 			delete iBullet;
       
   444 			iBullet = NULL;
       
   445 			WriteTagL(KHtmlBulletListPointEnd);
       
   446 			WriteTagL(KHtmlBulletListEnd);
       
   447 			}
       
   448 		else
       
   449 			{
       
   450 			if(*iBullet == *(aFormat->iBullet))
       
   451 				{
       
   452 				WriteTagL(KHtmlBulletListPointEnd);
       
   453 				WriteTagL(KHtmlBulletListPointStart);
       
   454 				}
       
   455 			else
       
   456 				{
       
   457 				// A _new_ list
       
   458 				WriteTagL(KHtmlBulletListPointEnd);
       
   459 				WriteTagL(KHtmlBulletListEnd);
       
   460 				WriteTagL(KHtmlBulletListStart);
       
   461 				WriteTagL(KHtmlBulletListPointStart);
       
   462 				*iBullet = *(aFormat->iBullet);
       
   463 				}
       
   464 			}
       
   465 		}
       
   466 	else
       
   467 		{
       
   468 		// Previous paragraph was _not_ bulleted
       
   469 		if(aFormat->iBullet)
       
   470 			{
       
   471 			// But this one is: start a new list
       
   472 			iBullet = new (ELeave) TBullet();
       
   473 			*iBullet = *(aFormat->iBullet);
       
   474 			WriteTagL(KHtmlBulletListStart);
       
   475 			WriteTagL(KHtmlBulletListPointStart);
       
   476 			}
       
   477 		}
       
   478 
       
   479 	// Process paragraph alignment
       
   480 	switch(aFormat->iHorizontalAlignment)
       
   481 		{
       
   482 	case CParaFormat::ELeftAlign:   // Paragraph aligned flush with left margin
       
   483 		WriteTagL(KHtmlDivAlignLeftStart);
       
   484 		break;
       
   485 	case CParaFormat::ECenterAlign: // Paragraph center aligned
       
   486 		WriteTagL(KHtmlDivAlignCentreStart);
       
   487 		break;
       
   488 	case CParaFormat::ERightAlign:  // Paragraph aligned flush with right margin
       
   489 		WriteTagL(KHtmlDivAlignRightStart);
       
   490 		break;
       
   491 	case CParaFormat::EJustifiedAlign: // Justified text
       
   492 		WriteTagL(KHtmlDivAlignJustifyStart);
       
   493 		break;
       
   494 	default:
       
   495 		WriteTagL(KHtmlDivAlignNoneStart);
       
   496 		break;
       
   497 		}
       
   498 
       
   499 	// Open indents
       
   500     iIndent = (aFormat->iLeftMarginInTwips) / KTwipsToBlockQuote;
       
   501 	for(TInt i = 0; i < iIndent; i++)
       
   502   		WriteTagL(KHtmlBlockquoteStart);
       
   503 	}
       
   504 
       
   505 void CCrtToHTMLConverter::ProcessParagraphTextL(TInt aPos, TInt aLength)
       
   506 //
       
   507 // Processes a paragraph of text
       
   508 //
       
   509 	{
       
   510 	TInt			pos = aPos;
       
   511 	TCharFormat		oldFormat,
       
   512 		            newFormat;
       
   513 
       
   514 	TCharFormatMask maskChar;
       
   515 	// Set up initial character formatting
       
   516 	iInputText->GetSpecificCharFormat(newFormat, maskChar, pos);
       
   517 	DiffCharFormats(newFormat, oldFormat, maskChar);
       
   518 	OpenCharFormatL (maskChar, newFormat);
       
   519 	oldFormat = newFormat;
       
   520 
       
   521 	// reset blank paragraph flag
       
   522 	iInsertBlankDivClose = EFalse;
       
   523 	// Scan the paragraph 1 char at a time...
       
   524 	while((pos - aPos) < aLength)
       
   525 		{
       
   526 		ASSERT(pos < iInputText->DocumentLength());
       
   527 		TPtrC str = iInputText->Read(pos++, 1);
       
   528 
       
   529 		if(str[0] == CEditableText::EParagraphDelimiter)
       
   530 			{
       
   531 			// only insert a blank div if we have a blank paragraph
       
   532 			// ie aLength == 1 && it's only contents are CEditableText::EParagraphDelimiter
       
   533 			iInsertBlankDivClose = ( aLength == 1 );
       
   534 			continue;
       
   535 			}
       
   536 
       
   537 		TCharFormatMask testMask;
       
   538 		TCharFormat		tstFormat;
       
   539 
       
   540 		iInputText->GetCharFormat(tstFormat, testMask, pos-1, 2);
       
   541 
       
   542 		if(!tstFormat.IsEqual(oldFormat))
       
   543 			{
       
   544 			// Something has changed...
       
   545 			DiffCharFormats(tstFormat, oldFormat, maskChar);
       
   546 			CloseCharFormatL(maskChar, oldFormat);
       
   547 			OpenCharFormatL (maskChar, tstFormat);
       
   548 			}
       
   549 		oldFormat = tstFormat;
       
   550 		TranslateL(str);
       
   551 		}
       
   552 	// End of paragraph, reset formatting to base...
       
   553 	TCharFormat	closeFormat;
       
   554 	DiffCharFormats(oldFormat, closeFormat, maskChar);
       
   555 	CloseCharFormatL(maskChar, oldFormat);
       
   556 	}
       
   557 
       
   558 void CCrtToHTMLConverter::DiffCharFormats(const TCharFormat& aFormatA, const TCharFormat& aFormatB, TCharFormatMask& aMask)
       
   559 //
       
   560 // Compare two TCharFormat and set flags in the mask which descrbe the differences
       
   561 // (Would be quite nice if TCharFormat knew how to do this itself...)
       
   562 //
       
   563 	{
       
   564 	aMask.ClearAll();
       
   565 
       
   566 	if(aFormatA.iLanguage!=aFormatB.iLanguage)
       
   567 		aMask.SetAttrib(EAttCharLanguage);
       
   568 
       
   569 	if(aFormatA.iFontSpec.iHeight != aFormatB.iFontSpec.iHeight)
       
   570 		aMask.SetAttrib(EAttFontHeight);
       
   571 
       
   572 	if(!(aFormatA.iFontSpec.iTypeface == aFormatB.iFontSpec.iTypeface))
       
   573 		aMask.SetAttrib(EAttFontTypeface);
       
   574 
       
   575 	if(aFormatA.iFontSpec.iFontStyle.Posture() != aFormatB.iFontSpec.iFontStyle.Posture())
       
   576 		aMask.SetAttrib(EAttFontPosture);
       
   577 
       
   578 	if(aFormatA.iFontSpec.iFontStyle.StrokeWeight() != aFormatB.iFontSpec.iFontStyle.StrokeWeight())
       
   579 		aMask.SetAttrib(EAttFontStrokeWeight);
       
   580 
       
   581 	if(aFormatA.iFontSpec.iFontStyle.PrintPosition() != aFormatB.iFontSpec.iFontStyle.PrintPosition())
       
   582 		aMask.SetAttrib(EAttFontPrintPos);
       
   583 
       
   584 	if(aFormatA.iFontPresentation.iUnderline != aFormatB.iFontPresentation.iUnderline)
       
   585 		aMask.SetAttrib(EAttFontUnderline);
       
   586 
       
   587 	if (aFormatA.iFontPresentation.iStrikethrough != aFormatB.iFontPresentation.iStrikethrough)
       
   588 		aMask.SetAttrib(EAttFontStrikethrough);
       
   589 
       
   590 	if(aFormatA.iFontPresentation.iTextColor != aFormatB.iFontPresentation.iTextColor)
       
   591 		aMask.SetAttrib(EAttColor);
       
   592 
       
   593 	if(!(aFormatA.iFontSpec.iTypeface == aFormatB.iFontSpec.iTypeface))
       
   594 		aMask.SetAttrib(EAttFontTypeface);
       
   595 	}
       
   596 
       
   597 void CCrtToHTMLConverter::OpenCharFormatL(const TCharFormatMask& aMask, const TCharFormat& aFormat)
       
   598 //
       
   599 //	Open the formating tags as set in aMask using parameters gleened from aFormat
       
   600 //
       
   601 	{
       
   602 	if(aMask.IsNull())
       
   603 		return;
       
   604 	// Bold text ?
       
   605 	if(aMask.AttribIsSet(EAttFontStrokeWeight) && (aFormat.iFontSpec.iFontStyle.StrokeWeight() == EStrokeWeightBold))
       
   606 		WriteTagL(KHtmlBoldStart);
       
   607 	// Underlined ?
       
   608 	if(aMask.AttribIsSet(EAttFontUnderline)  && (aFormat.iFontPresentation.iUnderline == EUnderlineOn))
       
   609 		WriteTagL(KHtmlUnderlineStart);
       
   610 	// Italic ?
       
   611 	if(aMask.AttribIsSet(EAttFontPosture) && (aFormat.iFontSpec.iFontStyle.Posture() == EPostureItalic))
       
   612 		WriteTagL(KHtmlItalicStart);
       
   613 	// Strike through ?
       
   614 	if(aMask.AttribIsSet(EAttFontStrikethrough) && aFormat.iFontPresentation.iStrikethrough)
       
   615 		WriteTagL(KHtmlStrikeoutStart);
       
   616 	// Sub/Super-script ?
       
   617 	if(aMask.AttribIsSet(EAttFontPrintPos))
       
   618 		{
       
   619 		switch(aFormat.iFontSpec.iFontStyle.PrintPosition())
       
   620 			{
       
   621 		case EPrintPosSuperscript:	WriteTagL(KHtmlSuperscriptStart);
       
   622 			break;
       
   623 		case EPrintPosSubscript  :	WriteTagL(KHtmlSubscriptStart);
       
   624 			break;
       
   625 		case EPrintPosNormal     :	break;
       
   626 			}
       
   627 		}
       
   628 	// Font typeface
       
   629 	if(aMask.AttribIsSet(EAttFontTypeface) && !aFormat.iFontSpec.iTypeface.IsProportional())
       
   630 		WriteTagL(KHtmlTeletypeStart);
       
   631 
       
   632 
       
   633 	//	if both font height and colour are set.
       
   634 		if(aMask.AttribIsSet(EAttFontHeight) && aMask.AttribIsSet(EAttColor))
       
   635 		{
       
   636 			TInt htmlHeight = ((aFormat.iFontSpec.iHeight - KHtmlTwipsToHeightBaseAdjust) / KHtmlTwipsToHeight) + 1;
       
   637 			TBuf8<KMaxTagLength > tag;
       
   638 			if(htmlHeight > KHtmlMaxFontSize)
       
   639 				htmlHeight = KHtmlMaxFontSize;
       
   640 			tag.Format(KHtmlFontStartClrNSize, htmlHeight, aFormat.iFontPresentation.iTextColor.Red(),
       
   641 												 aFormat.iFontPresentation.iTextColor.Green(),
       
   642 												 aFormat.iFontPresentation.iTextColor.Blue());
       
   643 			WriteTagL(tag);
       
   644 			//if(!iOldFmtCount)
       
   645 			iOldFmtCount = 1;
       
   646 		}
       
   647 		else
       
   648 		{
       
   649 			// Font height
       
   650 			if(aMask.AttribIsSet(EAttFontHeight))
       
   651 			{
       
   652 				TInt htmlHeight = ((aFormat.iFontSpec.iHeight - KHtmlTwipsToHeightBaseAdjust) / KHtmlTwipsToHeight) + 1;
       
   653 				TBuf8<KMaxTagLength > tag;
       
   654 				if(htmlHeight > KHtmlMaxFontSize)
       
   655 					htmlHeight = KHtmlMaxFontSize;
       
   656 				tag.Format(KHtmlFontSizeStart, htmlHeight);
       
   657 				WriteTagL(tag);
       
   658 				//if(!iOldFmtCount)
       
   659 				iOldFmtCount = 1;
       
   660 
       
   661 			}
       
   662 			// Font colour
       
   663 			if(aMask.AttribIsSet(EAttColor))
       
   664 			{
       
   665 				TBuf8<KMaxTagLength > tag;
       
   666 				tag.Format(KHtmlFontColourStart, aFormat.iFontPresentation.iTextColor.Red(),
       
   667 												 aFormat.iFontPresentation.iTextColor.Green(),
       
   668 												 aFormat.iFontPresentation.iTextColor.Blue());
       
   669 				WriteTagL(tag);
       
   670 				//if(!iOldFmtCount)
       
   671 				iOldFmtCount = 1;
       
   672 			}
       
   673 		}
       
   674 
       
   675 
       
   676 
       
   677 	}
       
   678 
       
   679 void CCrtToHTMLConverter::CloseCharFormatL(const TCharFormatMask& aMask, const TCharFormat& aFormat)
       
   680 //
       
   681 // Open the formating tags as set in aMask using parameters gleened from aFormat
       
   682 // These tags should be checked in the exact _reverse_ order as when they were opened
       
   683 // to ensure that they nest properly.
       
   684 //
       
   685 	{
       
   686 	if(aMask.IsNull())
       
   687 		return;
       
   688 
       
   689 	//Font color or height
       
   690 	if(iOldFmtCount && (aMask.AttribIsSet(EAttColor)	|| aMask.AttribIsSet(EAttFontHeight)))
       
   691 	{
       
   692 		WriteTagL(KHtmlFontEnd);
       
   693 		iOldFmtCount = 0;
       
   694 	}
       
   695 
       
   696 	// Font typeface
       
   697 if(aMask.AttribIsSet(EAttFontTypeface) && !aFormat.iFontSpec.iTypeface.IsProportional())
       
   698 		WriteTagL(KHtmlTeletypeEnd);
       
   699 	// Sub/Super-script ?
       
   700 	if(aMask.AttribIsSet(EAttFontPrintPos))
       
   701 		{
       
   702 		switch(aFormat.iFontSpec.iFontStyle.PrintPosition())
       
   703 			{
       
   704 		case EPrintPosSuperscript:	WriteTagL(KHtmlSuperscriptEnd);
       
   705 			break;
       
   706 		case EPrintPosSubscript  :	WriteTagL(KHtmlSubscriptEnd);
       
   707 			break;
       
   708 		case EPrintPosNormal     :	break;
       
   709 			}
       
   710 		}
       
   711 	// Strike through ?
       
   712 	if(aMask.AttribIsSet(EAttFontStrikethrough) && aFormat.iFontPresentation.iStrikethrough)
       
   713 		WriteTagL(KHtmlStrikeoutEnd);
       
   714 	// Italic ?
       
   715 	if(aMask.AttribIsSet(EAttFontPosture) && (aFormat.iFontSpec.iFontStyle.Posture() == EPostureItalic))
       
   716 		WriteTagL(KHtmlItalicEnd);
       
   717 	// Underlined ?
       
   718 	if(aMask.AttribIsSet(EAttFontUnderline)  && (aFormat.iFontPresentation.iUnderline == EUnderlineOn))
       
   719 		WriteTagL(KHtmlUnderlineEnd);
       
   720 	// Bold text ?
       
   721 	if(aMask.AttribIsSet(EAttFontStrokeWeight) && (aFormat.iFontSpec.iFontStyle.StrokeWeight() == EStrokeWeightBold))
       
   722 		WriteTagL(KHtmlBoldEnd);
       
   723 	}
       
   724 
       
   725 //-------------------------------------------------------------------------------------------------
       
   726 // Output functions
       
   727 //
       
   728 void CCrtToHTMLConverter::StartDocumentL()
       
   729 //
       
   730 // Put the <HTML><HEAD>...</HEAD> tags and open the >BODY> tag.
       
   731 //
       
   732 	{
       
   733 	// Do some validity checks ?				// TTD:	Change this to 4.0 xxxx - or skip it!
       
   734 	WriteTagL(KHTMLDocType32);				// <!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">
       
   735 	WriteTagL(KHtmlStartTag);				// <HTML>
       
   736 	WriteTagL(KHtmlHeadStartTag); 			// <HEAD>
       
   737 
       
   738 	WriteTagL(KHTMLHeadTitleStartTag);		// <TITLE>
       
   739 	TranslateL(_L("Converted from Rich Text"));	//   ...
       
   740 	WriteTagL(KHTMLHeadTitleEndTag);		// </TITLE>
       
   741 #ifdef _UNICODE
       
   742 	WriteTagL(KHTMLMetaCharSetUTF8);		// <META HTTP-EQUIV ="Content-Type" CONTENT = "text/html; charset=UTF-8">
       
   743 #endif //_UNICODE
       
   744 	WriteTagL(KHTMLHeadMetaGenTag);  		// <META NAME = "generator" CONTENT = "rt2html converter">
       
   745 	WriteTagL(KHtmlHeadEndTag);	  		// </HEAD>
       
   746 
       
   747 	// Get default background colour...
       
   748 	CParaFormat* formatPara = CParaFormat::NewLC();
       
   749 	iInputText->GlobalParaFormatLayer()->SenseEffectiveL(formatPara);
       
   750 	TRgb backColour = formatPara->iFillColor;
       
   751 	CleanupStack::PopAndDestroy(); // CParaFormat formatPara
       
   752 
       
   753 	// Get default foreground colour...
       
   754 	TCharFormat formatChar;
       
   755 	iInputText->GlobalCharFormatLayer()->SenseEffective(formatChar);
       
   756 	TRgb foreColour = formatChar.iFontPresentation.iTextColor;
       
   757 
       
   758 	TBuf8<KMaxTagLength > tag;
       
   759 	tag.Format(KHtmlBodyStartTag, backColour.Red(),
       
   760 								  backColour.Green(),
       
   761 								  backColour.Blue(),
       
   762 								  foreColour.Red(),
       
   763 								  foreColour.Green(),
       
   764 								  foreColour.Blue());
       
   765 
       
   766 	WriteTagL(tag);
       
   767 	}
       
   768 
       
   769 
       
   770 void CCrtToHTMLConverter::EndDocumentL()
       
   771 //
       
   772 // Close the document (write the </BODY> and </HTML> tags
       
   773 //
       
   774 	{
       
   775 	if(iOldFmtCount)
       
   776 		{
       
   777 		WriteTagL(KHtmlFontEnd);
       
   778 		}
       
   779 
       
   780 	if(iBullet)
       
   781 		{
       
   782 		// End of list
       
   783 		delete iBullet;
       
   784 		iBullet = NULL;
       
   785 		WriteTagL(KHtmlBulletListPointEnd);
       
   786 		WriteTagL(KHtmlBulletListEnd);
       
   787 		}
       
   788 	else if (iInsertBlankDivClose)
       
   789 		{
       
   790 		WriteTagL(KHtmlDivBlankEnd);//&nbsp</DIV>
       
   791 		}
       
   792 	else
       
   793 		{
       
   794 		WriteTagL(KHtmlDivEnd);//</DIV>
       
   795 		}
       
   796 	
       
   797 	WriteTagL(KHtmlBodyEndTag);	// </BODY>
       
   798 	WriteTagL(KHtmlEndTag);		// </HTML>
       
   799 	}
       
   800 
       
   801 CConverterBase2* CCrtToHTMLConverter::NewL()
       
   802 	{
       
   803 	CConverterBase2* crtToHtmlConverter=new (ELeave) CCrtToHTMLConverter();
       
   804 	return crtToHtmlConverter;
       
   805 	}
       
   806 
       
   807 const TImplementationProxy ImplementationTable[] =
       
   808     {
       
   809 	IMPLEMENTATION_PROXY_ENTRY(0x1000071c,CCrtToHTMLConverter::NewL)
       
   810     };
       
   811 
       
   812 EXPORT_C const TImplementationProxy* ImplementationGroupProxy(TInt& aTableCount)
       
   813     {
       
   814     aTableCount = sizeof(ImplementationTable) / sizeof(TImplementationProxy);
       
   815     return ImplementationTable;
       
   816     }
       
   817 //-------------------------------------------------------------------------------------------------
       
   818 // Conversion helper
       
   819 //-------------------------------------------------------------------------------------------------
       
   820 #ifdef _UNICODE
       
   821 #include <utf.h>
       
   822 // Stole most of this from Phil :-)
       
   823 HBufC8* ConvertUnicodeToUTF8LC(const TDesC16& uniText)
       
   824 	{
       
   825     // Final UTF8 destination buffer.
       
   826 	TInt    len     = uniText.Length() * sizeof(TText);
       
   827 	HBufC8* utfText = HBufC8::NewL(len); // Probably be enough...
       
   828     CleanupStack::PushL(utfText);
       
   829 
       
   830     // Keep going until there are no unconverted characters left.
       
   831     FOREVER
       
   832 		{
       
   833         TPtr8 destination = utfText->Des();
       
   834 		      destination.FillZ();
       
   835         TInt  charsLeft   = CnvUtfConverter::ConvertFromUnicodeToUtf8(destination, uniText);
       
   836 
       
   837         if(charsLeft < 0)
       
   838 			User::Leave(KErrCorrupt);       // Conversion error due to input stream.
       
   839         else if(0==charsLeft)
       
   840 			{
       
   841 			return utfText;
       
   842 			}
       
   843             else
       
   844             {
       
   845             // There are characters left to convert due to running out of destination buffer space.
       
   846 			len += charsLeft * sizeof(TText);
       
   847 			utfText = utfText->ReAlloc(len);
       
   848             }
       
   849 		}
       
   850 //    return NULL;  PFD - removed unreachable code as no warnings are acceptable.
       
   851 	}
       
   852 #endif // _UNICODE
       
   853 
       
   854 //-------------------------------------------------------------------------------------------------
       
   855 // Globals
       
   856 //-------------------------------------------------------------------------------------------------
       
   857 
       
   858 GLDEF_C void Panic(TTextToHTMLPanic aPanic)
       
   859 // Panic the process with HTML converter as the category.
       
   860 //
       
   861 	{
       
   862 	User::Panic(_L("CRT2HTML"),aPanic);
       
   863 	}
       
   864 
       
   865