textrendering/texthandling/stext/TXTRICH.CPP
changeset 0 1fb32624e06b
child 24 71313a964664
child 40 91ef7621b7fc
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/textrendering/texthandling/stext/TXTRICH.CPP	Tue Feb 02 02:02:46 2010 +0200
@@ -0,0 +1,1838 @@
+/*
+* Copyright (c) 1997-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: 
+*
+*/
+
+
+#include <e32std.h>
+#include <e32base.h>
+
+#include "TXTRICH.H"
+#include "TXTINDEX.H"
+#include "TXTSTD.H"
+#include "TXTRTPFL.H"
+#include "ParseLst.h"
+#include "TXTCLIPBOARD.H"
+
+#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS
+#include "TXTETEXT_INTERNAL.H"
+#include "TXTRICH_INTERNAL.H"
+#endif
+
+EXPORT_C void CRichText::__DbgTestInvariant()const
+// Provides class invariants.  Explanations below:
+//
+	{
+#ifdef _DEBUG
+// ASSERT: The global format layers are never null.
+	__ASSERT_DEBUG(iGlobalParaFormatLayer != NULL, User::Invariant());
+	__ASSERT_DEBUG(iGlobalCharFormatLayer != NULL, User::Invariant());
+	if (IndexPresent())
+		{
+// ASSERT: The sum of para lengths == the length as described by the document storage.
+		TInt cumulativeParaLength = 0;
+		TInt maxPara = iIndex->iParaIx->Count();
+		for (TInt offset = 0; offset < maxPara; offset++)
+			{
+			TParaAttribsEntry entry = (*iIndex->iParaIx)[offset];
+			cumulativeParaLength += entry.iLength;
+			}
+		__ASSERT_DEBUG(cumulativeParaLength == (DocumentLength() + 1), User::Invariant());
+		}
+		// Change here for defect INC005336.
+		// This defect is present when the assertion below fails.
+		__ASSERT_DEBUG( (iParserData == NULL) || \
+			(!iParserData->HaveRange()) || \
+			(iParserData->EndParse() <= DocumentLength()), User::Invariant());
+#endif
+	}
+
+EXPORT_C CRichText* CRichText::NewL(const CParaFormatLayer* aGlobalParaLayer,const CCharFormatLayer* aGlobalCharLayer,
+									TDocumentStorage aStorage,TInt aDefaultTextGranularity,TParaType aParaType)
+/** Allocates and constructs an empty rich text object, with a global character
+and paragraph format layer. A single end-of-document delimiter is inserted.
+No style list is allocated.
+
+@param aGlobalParaLayer Pointer to the paragraph format layer referenced by
+the rich text object. Must not be NULL, or a panic occurs.
+@param aGlobalCharLayer Pointer to the character format layer referenced by
+the rich text object. Must not be NULL, or a panic occurs.
+@param aStorage The type of in-memory buffer to use. Defaults to ESegmentedStorage
+which should rarely need to be changed.
+@param aDefaultTextGranularity Specifies the granularity of the in-memory buffer.
+Default is EDefaultTextGranularity bytes (=256), and this should rarely need
+to be changed.
+@param aParaType This argument indicates whether you are using a single paragraph
+or multiple paragraphs, and thus affects the granularity of aggregate objects
+used internally for storing paragraph attributes. Default = EMultiPara.
+@return The rich text object. */
+	{
+	// Create new rich text containing just a single end-of-document character.
+	__ASSERT_ALWAYS(aGlobalParaLayer != NULL, Panic(ENullFormatLayerHandle));
+	__ASSERT_ALWAYS(aGlobalCharLayer != NULL, Panic(ENullFormatLayerHandle));
+
+	CRichText* self = new(ELeave) CRichText(aGlobalParaLayer, aGlobalCharLayer);
+	CleanupStack::PushL(self);
+	self->ConstructL(aStorage, aDefaultTextGranularity, aParaType);
+	CleanupStack::Pop();
+	return self;
+	}
+
+
+// Create new rich text that supports Paragraph Styles, containing just a single end-of-document character.
+EXPORT_C CRichText* CRichText::NewL(const CParaFormatLayer* aGlobalParaLayer,const CCharFormatLayer* aGlobalCharLayer,
+									const CStyleList& aStyleList,
+									TDocumentStorage aStorage,TInt aDefaultTextGranularity,TParaType aParaType)
+/** Allocates and constructs an empty rich text object which supports styles. It
+is constructed with a global character and paragraph format layer and a style
+list. A single end-of-document delimiter is inserted. The rich text object
+takes ownership of the style list.
+
+Note:
+
+A rich text object not constructed with a style list may still use styles,
+by calling SetStyleListExternallyOwned() at any time after construction. In
+this case, the rich text object does not own the style list.
+
+@param aGlobalParaLayer Pointer to the paragraph format layer referenced by
+the rich text object. Must not be NULL, or a panic occurs.
+@param aGlobalCharLayer Pointer to the character format layer referenced by
+the rich text object. Must not be NULL, or a panic occurs.
+@param aStyleList Style list. Holds the set of paragraph styles which can be
+used in the rich text object.
+@param aStorage The type of in-memory buffer to use. Defaults to ESegmentedStorage
+which should rarely need to be changed.
+@param aDefaultTextGranularity Specifies the granularity of the in-memory buffer.
+Default is EDefaultTextGranularity bytes (=256), and this should rarely need
+to be changed.
+@param aParaType This argument indicates whether you are using a single paragraph
+or multiple paragraphs, and thus affects the granularity of aggregate objects
+used internally for storing paragraph attributes. Default = EMultiPara.
+@return The new rich text object. */
+	{
+	__ASSERT_ALWAYS(aGlobalParaLayer != NULL, Panic(ENullFormatLayerHandle));
+	__ASSERT_ALWAYS(aGlobalCharLayer != NULL, Panic(ENullFormatLayerHandle));
+
+	CRichText* self = new(ELeave) CRichText(aGlobalParaLayer, aGlobalCharLayer, CONST_CAST(CStyleList*, &aStyleList));
+	CleanupStack::PushL(self);
+	self->ConstructL(aStorage, aDefaultTextGranularity, aParaType);
+	CleanupStack::Pop();
+	return self;
+	}
+
+
+// Restore into a new rich text object, using the specified global layers.
+EXPORT_C CRichText* CRichText::NewL(const CStreamStore& aStore,TStreamId aStreamId,
+									const CParaFormatLayer* aGlobalParaLayer,const CCharFormatLayer* aGlobalCharLayer,
+									MTextFieldFactory* aFieldFactory,
+									TDocumentStorage aStorage)
+/** Allocates and constructs a rich text object with a field factory. Its text
+content is internalized from a stream store.
+
+Note:
+
+A rich text object not constructed with a field factory may still support
+the addition of fields, by calling SetFieldFactory(), defined in the base
+class CPlainText.
+
+@param aStore Stream store from which the object is restored.
+@param aStreamId ID of the stream store.
+@param aGlobalParaLayer Pointer to the paragraph format layer referenced by
+the rich text object. Must not be NULL, or a panic occurs.
+@param aGlobalCharLayer Pointer to the character format layer referenced by
+the rich text object. Must not be NULL, or a panic occurs.
+@param aFieldFactory Pointer to a field factory. A field factory must be provided
+if the text object supports fields.
+@param aStorage The type of in-memory buffer to use. Defaults to ESegmentedStorage
+which should rarely need to be changed.
+@return The new rich text object. */
+	{
+	__ASSERT_ALWAYS(aGlobalParaLayer != NULL, Panic(ENullFormatLayerHandle));
+	__ASSERT_ALWAYS(aGlobalCharLayer != NULL, Panic(ENullFormatLayerHandle));
+
+	CRichText* self = new(ELeave) CRichText(aGlobalParaLayer, aGlobalCharLayer);
+	CleanupStack::PushL(self);
+	self->ConstructL(aStore, aStreamId, NULL, NULL, aFieldFactory, aStorage);
+	CleanupStack::Pop();
+	return self;
+	}
+
+
+EXPORT_C CRichText* CRichText::NewL(const CStreamStore& aStore, TStreamId aStreamId,
+									const CParaFormatLayer* aGlobalParaLayer, const CCharFormatLayer* aGlobalCharLayer,
+									MPictureFactory* aPictureFactory, MRichTextStoreResolver* aStoreResolver,
+									MTextFieldFactory* aFieldFactory,
+									TDocumentStorage aStorage)
+/** Allocates and constructs a rich text object with a field factory and a picture
+factory. Its text content is internalized from a stream store.
+
+Note:
+
+A rich text object not constructed with a field factory may still support
+the addition of fields, by calling SetFieldFactory(), defined in the base
+class CPlainText.
+
+@param aStore Stream store from which the object is restored.
+@param aStreamId ID of the stream store.
+@param aGlobalParaLayer Pointer to the paragraph format layer referenced by
+the rich text object. Must not be NULL, or a panic occurs.
+@param aGlobalCharLayer Pointer to the character format layer referenced by
+the rich text object. Must not be NULL, or a panic occurs.
+@param aPictureFactory The picture factory. This is needed to load pictures
+into memory, (see PictureHandleL()). If a store resolver is specified (not
+NULL), then a factory must also be specified, or a panic occurs.
+@param aStoreResolver A store resolver. This determines which file store the
+picture is stored in. It is used to get from a reference to an embedded picture
+within a CRichText object to the actual picture itself. Picture loading is
+done by the picture factory.
+@param aFieldFactory Pointer to a field factory. A field factory must be provided
+if the text object supports fields.
+@param aStorage The type of in-memory buffer to use. Defaults to ESegmentedStorage
+which should rarely need to be changed.
+@return The new rich text object. */
+	{
+	// Restore a new rich text from the specified stream, that uses the specified global layers, and the
+	// specified picture header factory and store, if this rich text supports pictures.
+	__ASSERT_ALWAYS(!(!aPictureFactory && aStoreResolver), Panic(EInvalidPictureFactorySettings));
+	__ASSERT_ALWAYS(aGlobalParaLayer != NULL, Panic(ENullFormatLayerHandle));
+	__ASSERT_ALWAYS(aGlobalCharLayer != NULL, Panic(ENullFormatLayerHandle));
+
+	CRichText* self = new(ELeave) CRichText(aGlobalParaLayer, aGlobalCharLayer);
+	CleanupStack::PushL(self);
+	self->ConstructL(aStore, aStreamId, aPictureFactory, aStoreResolver, aFieldFactory, aStorage);
+	CleanupStack::Pop();
+	return self;
+	}
+
+
+EXPORT_C CRichText::CRichText(const CParaFormatLayer* aGlobalParaLayer, const CCharFormatLayer* aGlobalCharLayer,
+							  CStyleList* aStyleList):
+	CGlobalText(aGlobalParaLayer, aGlobalCharLayer),
+	iStyleList(aStyleList)
+	{
+	}
+
+
+EXPORT_C void CRichText::ConstructL(TDocumentStorage aStorage, TInt aDefaultTextGranularity, TParaType aParaType)
+// Initialises and updates the index following the CPlainText::ConstructL
+// insertion of the end-of-document character.
+//
+	{
+	CPlainText::ConstructL(aStorage, aDefaultTextGranularity);
+	SetParaTypeIsSingle(aParaType == ESinglePara);
+	iParserData = new(ELeave) CParserData(DocumentLength());
+	TInt a;
+	TInt b;
+	ParseText(a, b, ETrue);
+
+	__TEST_INVARIANT;
+	}
+
+
+EXPORT_C void CRichText::ConstructL(const CStreamStore& aStore,TStreamId aStreamId,
+									MPictureFactory* aPictureFactory, MRichTextStoreResolver* aStoreResolver,
+									MTextFieldFactory* aFieldFactory,
+									TDocumentStorage aStorage)
+// Initialises and updates the index following the CPlainText::ConstructL
+// insertion of the end-of-document character.
+// Sets the picture header factory if provided.
+//
+	{
+	CPlainText::ConstructL(aStore, aStreamId, aFieldFactory, aStorage);
+	SetPictureFactory(aPictureFactory, aStoreResolver);
+	if (iParserData == NULL)
+		iParserData = new(ELeave) CParserData(DocumentLength());
+	TInt a;
+	TInt b;
+	ParseText(a, b, ETrue);
+
+	__TEST_INVARIANT;
+	}
+
+
+EXPORT_C CRichText::~CRichText()
+/** The destructor frees all resources owned by the rich text object, prior to
+its destruction. */
+	{
+	// We cannot call DestroyParserSystem() here because it applies to all instances in the thread
+	delete iParserData;
+	KillStyleList();
+	KillIndex();
+	}
+
+
+void CRichText::KillStyleList()
+// Free up the style table
+//
+	{
+	if (StyleListPresent() && !StyleListExternallyOwned())
+		{
+		delete iStyleList.AsPtr();
+		iStyleList = NULL;
+		}
+	}
+
+
+void CRichText::KillIndex()
+// Delete the rich text index if it's resident in memory.
+//
+	{
+	if (IndexPresent())
+		delete iIndex.AsPtr();
+	iIndex=NULL;
+	}
+
+
+TBool CRichText::CreateEmptyMarkupComponentL()
+// If necessary, creates an empty markup component.
+// Returns ETrue if the markup component was created as a result of this function,
+// otherwise returns EFalse.
+//
+	{
+	if (IndexPresent())
+		return EFalse;
+	TInt paraGran = (ParaTypeIsSingle()) ? KSingleParaGranularity : KMultiParaGranularity;
+	TInt phrGran = (ParaTypeIsSingle()) ? KSmallPhraseGranularity : KLargePhraseGranularity;
+	iIndex = CRichTextIndex::NewL(iGlobalParaFormatLayer, iGlobalCharFormatLayer, *this,paraGran,phrGran);
+	return ETrue;
+	}
+
+
+void CRichText::CreateAndGenerateMarkupComponentL()
+// Checks if the rich text index needs to be created and does so if necessary.
+// Called by all public rich text functions that manipulate specific formatting.
+//
+	{
+	if (CreateEmptyMarkupComponentL())
+		{
+		TRAPD(ret, GenerateMarkupL());
+		if (ret != KErrNone)
+			{// destroy this partially set markup component
+			delete iIndex.AsPtr();
+			iIndex = NULL;
+			User::Leave(ret);
+			}
+		}
+	}
+
+
+void CRichText::GenerateMarkupL()
+// Generate markup data corresponding to the current text content.
+//
+	{
+	TInt remainingLength = DocumentLength();
+	TInt startPos = 0;
+	while (remainingLength)
+		{
+		TPtrC buf = Read(startPos);
+		TInt consumed = buf.Length();
+		if (consumed > remainingLength)
+			{
+			consumed = remainingLength;
+			buf.Set(buf.Ptr(), consumed);
+			}
+		iIndex->InsertL(startPos, buf, *iGlobalParaFormatLayer);
+		remainingLength -= consumed;
+		startPos += consumed;
+		}
+	}
+
+EXPORT_C void CRichText::CopyToStoreL(CStreamStore& aStore,CStreamDictionary& aDictionary,TInt aPos,TInt aLength) const
+/** Copies a portion of the rich text object, with components to the clipboard.
+
+A panic occurs in the following circumstances:
+
+aPos is an invalid document position
+
+aLength is invalid (zero or less)
+
+the sum of aPos and aLength is greater than or equal to the number of characters
+in the document
+
+@param aStore Stream store to which the rich text is written.
+@param aDictionary The stream dictionary.
+@param aPos The document position from which to begin copying.
+@param aLength The number of characters to copy. */
+	{
+	if (aLength > 0)
+		{
+		TStreamId plainTextId = CPlainText::DoCopyToStoreL(aStore,aDictionary,aPos,aLength);
+		TStreamId id = DoCopyToStoreL(aStore,aPos,aLength,plainTextId,FALSE);
+		aDictionary.AssignL(KClipboardUidTypeRichText,id);
+		TStreamId idStyles = DoCopyToStoreL(aStore,aPos,aLength,plainTextId,TRUE);
+		aDictionary.AssignL(KClipboardUidTypeRichTextWithStyles,idStyles);
+		}
+	}
+
+
+// Copy the selected region of rich text, with components, to the specified store.
+TStreamId CRichText::DoCopyToStoreL(CStreamStore& aStore,TInt aPos,TInt aLength,TStreamId aPlainTextId,TBool aCopyStyles) const
+	{
+	__TEST_INVARIANT;
+
+	TInt documentLength = DocumentLength();
+	__ASSERT_ALWAYS(aPos >= 0 && aPos <= documentLength,Panic(ECharPosBeyondDocument));
+	__ASSERT_ALWAYS(aLength >= 0,Panic(ECopyToClipboardNegativeLength));
+	__ASSERT_ALWAYS(aPos + aLength <= documentLength,Panic(ECharPosBeyondDocument));
+
+	if (aLength == 0)
+		return KNullStreamId;
+
+	CStoreMap* map = CStoreMap::NewLC(aStore);
+	CopyComponentsL(aStore,*map,aPos,aLength,aPlainTextId);
+	RStoreWriteStream stream(*map);
+	TStreamId id = stream.CreateLC(aStore);
+	CopyToStreamL(stream,aPos,aLength,aPlainTextId,aCopyStyles);
+	stream.CommitL();
+	map->Reset();
+	CleanupStack::PopAndDestroy(2);
+
+	__TEST_INVARIANT;
+	return id;
+	}
+
+
+EXPORT_C void CRichText::CopyComponentsL(CStreamStore& aStore,CStoreMap& aMap,TInt aPos,TInt aLength,TStreamId aPlainTextId) const
+	{
+	if (aPlainTextId == KNullStreamId)
+		CPlainText::CopyComponentsL(aStore,aMap,aPos,aLength);
+	if (IndexPresent())
+		iIndex->StorePicturesL(aStore,aMap,aPos,aLength);
+	}
+
+
+// Copy the selected region of rich text and components to the specified stream.
+EXPORT_C void CRichText::CopyToStreamL(RWriteStream& aStream,TInt aPos,TInt aLength) const
+	{
+	__TEST_INVARIANT;
+	CopyToStreamL(aStream, aPos, aLength, KNullStreamId);
+	}
+
+
+/*
+Copy the selected region of rich text and components to the specified stream.
+If aPlainTextId is NULL the plain text component is stored in-line,
+otherwise the reference to the plain text stream component is stored.
+*/
+EXPORT_C void CRichText::CopyToStreamL(RWriteStream& aStream,TInt aPos,TInt aLength,TStreamId aPlainTextId) const
+	{
+	CopyToStreamL(aStream,aPos,aLength,aPlainTextId,TRUE);
+	}
+
+
+void CRichText::CopyToStreamL(RWriteStream& aStream,TInt aPos,TInt aLength,TStreamId aPlainTextId,TBool aCopyStyles) const
+	{
+	if (aPlainTextId == KNullStreamId)
+		{
+		aStream.WriteUint8L(TRUE);
+		CPlainText::CopyToStreamL(aStream,aPos,aLength);
+		}
+	else
+		{
+		aStream.WriteUint8L(FALSE);
+		aStream << aPlainTextId;
+		}
+
+	if (!IndexPresent())
+		CONST_CAST(CRichText*,this)->CreateAndGenerateMarkupComponentL();
+	aStream.WriteUint8L(TRUE);
+	iIndex->CopyToStreamL(aStream,aPos,aLength,aCopyStyles);
+	}
+
+EXPORT_C TInt CRichText::PasteFromStoreL(const CStreamStore& aStore, const CStreamDictionary& aDictionary, TInt aPos, CParagraphStyle::TStylePasteMode aStylePasteMode)
+/** Pastes the contents of the clipboard into the rich text object at the
+specified document position. Returns the number of characters pasted.
+
+If the text in the clipboard has been formatted using styles, the
+aStylePasteMode argument indicates whether the styles should be preserved
+or discarded. If the argument is not specified, the pasted rich text
+retains all formatting specified in the styles, and any new style
+definitions are added to the style list of the rich text object into which
+it is pasted.
+
+@param aStore The stream store from which to paste the rich text.
+@param aDictionary The stream dictionary.
+@param aPos The document position at which to paste the rich text. Must be
+valid, or a panic occurs.
+@param aStylePasteMode Indicates whether styles in the pasted text should be
+preserved or discarded.
+@return The number of characters pasted. */
+	{
+	return DoPasteRtFromStoreL(aStore, aDictionary, aPos, aStylePasteMode);
+	}
+
+
+EXPORT_C TInt CRichText::PasteFromStoreL(const CStreamStore& aStore, const CStreamDictionary& aDictionary, TInt aPos)
+// Paste the lesser of i) aMaxPasteLength ii) the entire clipboard contents.
+// Return a count of the number of characters pasted.
+//
+	{
+	return DoPasteRtFromStoreL(aStore, aDictionary, aPos, CParagraphStyle::EAddNewStyles);
+	}
+
+
+TInt CRichText::DoPasteRtFromStoreL(const CStreamStore& aStore, const CStreamDictionary& aDictionary, TInt aPos, CParagraphStyle::TStylePasteMode aStylePasteMode)
+// Paste the rich text data from the specified clipboard, inserting it into this
+// instance at character position aPos.  Returns the number of characters pasted.
+// May be 0.
+//
+	{
+	__TEST_INVARIANT;
+	__ASSERT_ALWAYS(aPos <= DocumentLength(), Panic(ECharPosBeyondDocument));
+
+	TUid type = KClipboardUidTypeRichTextWithStyles;
+	TStreamId id = aDictionary.At(type);
+	if (id == KNullStreamId)
+		{
+		type = KClipboardUidTypeRichText;
+		id = aDictionary.At(type);
+		}
+	if (id == KNullStreamId)
+		{
+		type = KClipboardUidTypePlainText;
+		id = aDictionary.At(type);
+		}
+	TInt consumed = 0;
+	if (id == KNullStreamId)
+		return consumed;
+	if (type == KClipboardUidTypeRichText || type == KClipboardUidTypeRichTextWithStyles)
+		consumed = PasteRichTextFromStoreL(aStore, aDictionary, id, aPos, aStylePasteMode);
+	else if (type == KClipboardUidTypePlainText)
+		consumed = PastePlainTextFromStoreL(aStore, id, aPos);
+
+	if (consumed)
+		{
+		iParserData->MergeRange(aPos,0,consumed);
+		CallEditObserver(aPos, consumed);
+		}
+
+	SetHasChanged(ETrue);
+	__TEST_INVARIANT;
+	return consumed;
+	}
+
+
+TInt CRichText::PasteRichTextFromStoreL(const CStreamStore& aStore, const CStreamDictionary& aDictionary, TStreamId& aRichTextStreamId, TInt aPos,CParagraphStyle::TStylePasteMode aStylePasteMode)
+// Paste the plain text stream, then open and paste the rich text markup from the specified stream.
+//
+	{
+	if (!IndexPresent())
+		CreateAndGenerateMarkupComponentL();  // create the index if it does not already exist
+	TStreamId id = aDictionary.At(KClipboardUidTypePlainText);
+// ASSERT: We have rich text, so the plain text stream must exist.
+	__ASSERT_ALWAYS(id != KNullStreamId,Panic(EClipboardIntegrity));
+	TInt consumed = CPlainText::DoPasteFromStoreL(aStore, id, aPos);
+	TRAPD(ret, CompletePasteRichTextFromStoreL(aStore, aRichTextStreamId, aPos, aStylePasteMode));
+	if (ret != KErrNone)
+		{
+		CPlainText::Delete(aPos,consumed);
+		User::Leave(ret);
+		}
+	return consumed;
+	}
+
+
+void CRichText::CompletePasteRichTextFromStoreL(const CStreamStore& aStore, TStreamId& aRichTextStreamId, TInt aPos, CParagraphStyle::TStylePasteMode aStylePasteMode)
+//
+	{
+	RStoreReadStream stream;
+	stream.OpenLC(aStore, aRichTextStreamId);
+	TBool plainTextInline = (TBool)stream.ReadUint8L();
+	if (!plainTextInline)
+		{
+		TStreamId dummy;
+		stream >> dummy;
+		}
+	TBool markupPresentInClipboard = (TBool)stream.ReadUint8L();
+	if (markupPresentInClipboard)
+		iIndex->PasteFromStreamL(aStore, stream, aPos,aStylePasteMode, iGlobalParaFormatLayer, iGlobalCharFormatLayer);
+	CleanupStack::PopAndDestroy();  // stream
+	}
+
+
+TInt CRichText::PastePlainTextFromStoreL(const CStreamStore& aStore, TStreamId& anId, TInt aPos)
+// Paste the plain text stream, then update the rich text index to show any paragraph delimiters pasted.
+//
+	{
+	TInt consumed = CPlainText::DoPasteFromStoreL(aStore, anId, aPos);
+	//
+	if (IndexPresent())
+		{
+		TRAPD(ret, CompletePastePlainTextL(aPos, consumed));
+		if (ret != KErrNone)
+			{
+			CPlainText::Delete(aPos,consumed);
+			User::Leave(ret);
+			}
+		}
+	return consumed;
+	}
+
+void CRichText::CompletePastePlainTextL(TInt aPos, TInt aCharacterCount)
+// Updates the rich text index following the pasting of global text.
+//
+	{
+	HBufC* buf = HBufC::NewLC(aCharacterCount);
+	TPtr bf = buf->Des();
+	Extract(bf, aPos, aCharacterCount);
+	iIndex->InsertL(aPos, bf, *iGlobalParaFormatLayer);
+	CleanupStack::PopAndDestroy();  // buf
+	}
+
+EXPORT_C void CRichText::Reset()
+/** Resets the document's contents to a single end-of-document delimiter. Also
+deletes the style list if owned by the object. */
+	{
+	// Resets document contents to single end-of-document charcter,
+	// and resets the index to match document content.
+	TInt length = DocumentLength();
+	CPlainText::Reset();  // remove all content bar eod character
+	KillStyleList();  // does not get destroyed if externally owned
+	KillIndex();
+	SetHasChanged(ETrue);
+	iParserData->KillRange();
+	iParserData->iLastKnownCursor = -1;
+	CallEditObserver(0, -length);
+
+	__TEST_INVARIANT;
+	}
+
+EXPORT_C void CRichText::InsertL(TInt aPos, const TChar& aChar)
+/** Inserts either a single character or a descriptor into the text object
+at a specified document position.
+
+The insertion position must be valid or a panic occurs.Note:A panic
+occurs if the text object is in an "insert pending" state (i.e.
+SetInsertCharFormatL() has been called and has not been
+cancelled using CancelInsertCharFormat()) and aPos is not
+the same as the insertion point.
+
+@param aPos The document position at which to insert the character/descriptor.
+@param aChar The character to insert. Must not be a picture character or a
+panic occurs.
+@param aBuf The descriptor to insert. */
+	{
+	__TEST_INVARIANT;
+	__ASSERT_ALWAYS(aPos >= 0 && aPos <= DocumentLength(), Panic(ECharPosBeyondDocument));
+	__ASSERT_ALWAYS(aChar!= EPictureCharacter, Panic(ENonOverloadedInsertCalledWithPictureCharacter));
+
+	TBuf<1> content;
+	content.Append(aChar);
+	RtInsertL(aPos, content);
+
+	__TEST_INVARIANT;
+	}
+
+
+EXPORT_C void CRichText::InsertL(TInt aPos, const TDesC& aBuf)
+// Inserts the contents of aBuf into the text component at character position aPos.
+//
+	{
+	__ETEXT_WATCH(INSERT_DESC);
+
+	RtInsertL(aPos, aBuf);
+
+	__ETEXT_WATCH_END(INSERT_DESC);
+	}
+
+
+EXPORT_C void CRichText::RtInsertL(TInt aPos, const TDesC& aBuf)
+// Inserts the contents a aBuf into the text component at position aPos.
+// Updates the index accordingly, and accounts for all embedded paragraph delimiters
+// in the passed buffer aBuf.
+//
+	{
+	__TEST_INVARIANT;
+	__ASSERT_ALWAYS(aPos >= 0 && aPos <= DocumentLength(), Panic(ECharPosBeyondDocument));
+
+	int length = aBuf.Length();
+	CPlainText::InsertL(aPos, aBuf);
+	if (IndexPresent())
+		{
+		TRAPD(ret, iIndex->InsertL(aPos, aBuf, *iGlobalParaFormatLayer));
+		if (ret != KErrNone)
+			{
+			CPlainText::Delete(aPos,length);
+			User::Leave(ret);
+			}
+		SetHasChanged(ETrue);
+		}
+
+	iParserData->MergeRange(aPos,0,length);
+	CallEditObserver(aPos,length);
+
+	__TEST_INVARIANT;
+	}
+
+
+EXPORT_C void CRichText::InsertL(TInt aPos,const TPictureHeader& aHeader)
+/**Inserts a picture header into the text object at a specified document
+position. The picture header specified must reference a valid picture, or
+a panic occurs.
+
+A panic also occurs if the text object is in an "insert pending" state
+(SetInsertCharFormatL() has been called and has not been cancelled using
+CancelInsertCharFormat()) and aPos is not the same as the insertion point.
+
+This method takes ownership of a picture referenced in aHeader.
+
+@param aPos The document position at which to insert the picture header. Must
+be valid, or a panic occurs.
+@param aHeader A picture header. This holds a pointer to the picture to insert,
+and information about the picture. */
+	{
+	__TEST_INVARIANT;
+	__ASSERT_ALWAYS(aPos >= 0 && aPos <= DocumentLength(), Panic(ECharPosBeyondDocument));
+
+	CleanupStack::PushL(aHeader.iPicture);
+
+	if (!IndexPresent())
+		CreateAndGenerateMarkupComponentL();
+	TBuf<1> picture;
+	picture.Append(EPictureCharacter);
+	CPlainText::InsertL(aPos, picture);
+
+    CleanupStack::Pop(aHeader.iPicture);
+
+	TBool pictureOwnershipTaken(EFalse);
+	TRAPD(ret, iIndex->InsertL(aPos, aHeader,pictureOwnershipTaken));
+	if (ret != KErrNone)
+		{
+		if(!pictureOwnershipTaken)
+		    {
+		    const_cast <TPictureHeader&> (aHeader).DeletePicture();
+		    }
+		CPlainText::Delete(aPos,picture.Length());  // remove the picture place-holder
+    	User::Leave(ret);
+		}
+
+	SetHasChanged(ETrue);
+	iParserData->MergeRange(aPos,0,1);
+	CallEditObserver(aPos,1);
+	__TEST_INVARIANT;
+	}
+
+
+EXPORT_C TBool CRichText::DeleteL(TInt aPos, TInt aLength)
+/** Deletes one or more characters beginning at, and including, the character at
+a specified document position. Can leave because paragraphs may be merged
+and reformatted by the function.
+
+@param aPos The start of the range of characters to delete. Must be valid
+or a panic occurs.
+@param aLength The number of characters to delete. Must be positive or a panic
+occurs. The sum of aPos and aLength must be less than the document length,
+or a panic occurs.
+@return Indicates whether two paragraphs have been merged as a result of the
+delete, indicating that the formatting of part of the resulting paragraph
+may have changed. */
+	{
+	// Deletes aRange number of characters from the text component
+	// and the corresponding index data.
+	// Delete commences at, and includes, character position aPos.
+	__TEST_INVARIANT;
+
+	__ASSERT_ALWAYS(aPos >= 0 && aPos <= DocumentLength(), Panic(ECharPosBeyondDocument));
+	__ASSERT_ALWAYS(aLength >= 0, Panic(EDebugDeleteZeroLength));
+	__ASSERT_ALWAYS(aPos + (aLength - 1) <= DocumentLength(), Panic(ECharPosBeyondDocument));
+
+	TBool requireMerge = EFalse;
+	if (!IndexPresent())
+		CPlainText::Delete(aPos,aLength);
+	else
+		{
+		iIndex->CancelInsertCharFormat();
+		TIndexDeleteInfo info;
+		iIndex->SetForDeleteL(info, aPos, aLength);
+		CPlainText::Delete(aPos,aLength);
+		requireMerge = iIndex->DeleteNow(info);
+		}
+	iParserData->MergeRange(aPos,aLength,0);
+	CallEditObserver(aPos,-aLength);
+
+	__TEST_INVARIANT;
+	return requireMerge;
+	}
+
+EXPORT_C void CRichText::DeleteParagraph(TInt aPos, TInt aLength)
+/** Deletes one or more whole paragraphs of text. No paragraphs can be merged together
+by this function, so it cannot leave it must only be used to delete entire
+paragraphs.
+
+@param aPos The document position of the start of the first paragraph to delete.
+Must be a valid position or a panic occurs.
+@param aLength The number of characters to delete. */
+	{
+	// Use to remove entire paragraphs of text.
+	// Leave-safe if called in this context.
+	// MUST NOT CALL if not in this context.
+	__TEST_INVARIANT;
+
+	// Store the length of the text before we commence with deletions.
+	TInt initialDocLen=DocumentLength();
+
+	__ASSERT_ALWAYS(aPos >= 0 && aPos <= initialDocLen, Panic(ECharPosBeyondDocument));
+	__ASSERT_ALWAYS(aPos + aLength <= initialDocLen + 1, Panic(ECharPosBeyondDocument));
+
+	if (aLength <= 0)
+		return;
+
+	if (IndexPresent())
+		iIndex->DeleteParagraph(aPos, aLength);
+	CPlainText::Delete(aPos,aLength);
+
+	// Change here for defect INC005336.
+	// Previously the MergeRange call (see else line) was incorrectly handling
+	// the scenario where all but the first paragraph was deleted e.g. pasting
+	// multi line buffer into a single line text control in a new email on Techview
+	// emulator.
+	// This was because the deletion in the two lines above would delete the
+	// 'end-of-text' maker causing the iEndParse member of CParserData to
+	// index the char right after the end of test marker. This would panic
+	// plugin parsers thatsubsquently executed.
+
+	if (aPos+aLength > initialDocLen)
+		// When deletion includes the end-of-text marker, adjust start
+		// supplied so that it appears to MergeRange that we are
+		// deleting the paragraph and CR just before it which belongs
+		// to the previous paragraph and not the end-of-text marker.
+		// This is actually the end result of the deletion anyway!
+		iParserData->MergeRange(initialDocLen-aLength,aLength,0);
+	else
+		// All other deletions which don't.
+		iParserData->MergeRange(aPos,aLength,0);
+
+	CallEditObserver(aPos,-aLength);
+
+	__TEST_INVARIANT;
+	}
+
+EXPORT_C void CRichText::DeleteFromParagraph(TInt aPos, TInt aLength)
+/** Removes a range of characters from within a single paragraph only. Should not
+be used for deleting an entire paragraph or paragraphs because this may cause
+it to leave. Otherwise, it is guaranteed not to leave.
+
+@param aPos The document position of the start of the range of characters
+to delete. Must be a valid document position, or a panic occurs.
+@param aLength The number of characters to delete. The sum of aPos and aLength
+must be less than the document length, or a panic occurs. */
+	{
+	// Removes aLength characters from *within* a single paragraph only.
+	// Guaranteed not to leave if this pre-condition holds true.
+	__TEST_INVARIANT;
+
+	__ASSERT_ALWAYS(aPos >= 0 && aPos <= DocumentLength(), Panic(ECharPosBeyondDocument));
+	__ASSERT_ALWAYS(aPos + (aLength - 1) <= DocumentLength(), Panic(ECharPosBeyondDocument));
+
+	if (aLength <= 0)
+		return;
+
+	if (IndexPresent())
+		iIndex->DeleteFromParagraph(aPos, aLength);
+	CPlainText::Delete(aPos,aLength);
+	iParserData->MergeRange(aPos,aLength,0);
+	CallEditObserver(aPos,-aLength);
+
+	__TEST_INVARIANT;
+	}
+
+EXPORT_C TInt CRichText::ParagraphCount()const
+/** Gets a count of the number of paragraphs in the text object.
+
+Note:
+
+The paragraph delimiter which terminates every text object means this function
+always returns a count of at least one.
+
+@return The number of paragraphs in the document. */
+	{
+	// Returns a count of the number of paragraphs
+	// in the document.
+	//
+	__TEST_INVARIANT;
+
+	if (IndexPresent())
+		return iIndex->ParagraphCount();
+	else
+		return CPlainText::ParagraphCount();
+	}
+
+
+EXPORT_C TEtextComponentInfo CRichText::ComponentInfo() const
+/** Gets information about the number of components contained in the text object
+(the field count, the picture count and the style count).
+
+@return Contains the component information. */
+	{
+	return TEtextComponentInfo(FieldCount(), PictureCount(), StyleCount());
+	}
+
+EXPORT_C TInt CRichText::CharPosOfParagraph(TInt& aLength, TInt aParaOffset) const
+/** Finds the length and the start position of a paragraph identified by its
+paragraph number. The first paragraph is numbered zero.
+
+Notes:
+
+if aParaOffset is invalid, (equal to or greater than the total number of
+paragraphs), the function's return value is EScanEndOfData (= -1)
+
+@param aLength On return contains the length of the specified paragraph.
+@param aParaOffset The paragraph number. The first paragraph is numbered zero.
+@return The document position of the first character in the paragraph. */
+	{
+	// Returns the character position of the first character of paragraph aParaOffset,
+	// where aParaOffset specifies the nth paragraph.
+	// The length of this nth paragraph is written to aLength.
+	//
+	// If aParaOffset specifies a paragraph that does not exist, EScanEndOfData is returned.
+	//
+	__TEST_INVARIANT;
+
+	if (IndexPresent())
+		return iIndex->CharPosOfParagraph(aLength, aParaOffset);
+	else
+		return CPlainText::CharPosOfParagraph(aLength, aParaOffset);
+	}
+
+EXPORT_C TInt CRichText::ParagraphNumberForPos(TInt& aPos) const
+/** Gets the number of the paragraph which contains a document position.
+Paragraph numbering begins at zero.
+
+@param aPos A document position. Must be valid or a panic occurs. On return,
+contains the document position of the first character in the paragraph in
+which it is located.
+@return The number of the paragraph containing the specified document position. */
+	{
+	// Returns the paragraph offset for the specified character position aPos.
+	// aPos is in turn modified to hold the character position of the first character
+	// of this paragraph.  If aPos is already on a paragraph boundary then do nothing.
+	//
+	__TEST_INVARIANT;
+	__ASSERT_ALWAYS(aPos >= 0 && aPos <= DocumentLength(), Panic(ECharPosBeyondDocument));
+
+	if (IndexPresent())
+		return iIndex->ParagraphNumberForPos(aPos);
+	else
+		return CPlainText::ParagraphNumberForPos(aPos);
+	}
+
+/** Applies character formatting to a zero length selection, for example
+turning bold on. This has the effect that the formatting will be applied to
+text subsequently inserted at the position. This "insert pending" state is
+cancelled by calling CancelInsertCharFormat().
+
+Note 1: After calling this function, if text is inserted at a different
+position to aPos, a panic will occur, unless CancelInsertCharFormat() has been
+called before the insertion to cancel the "insert pending" state.
+
+Note 2: If the insert character format is being set for the end of the
+paragraph, the paragraph delimiter is set to that format as well. This helps
+end-of-paragraph behaviour be more similar to other places.
+
+@param aFormat The character format values to apply.
+@param aMask Character format mask specifying the attributes affected.
+@param aPos The document position at which to insert the character format.
+@pre aPos must be a valid position, or a panic will occur. */
+EXPORT_C void CRichText::SetInsertCharFormatL(const TCharFormat& aFormat,
+	const TCharFormatMask& aMask, TInt aPos)
+	{
+	SetExtendedInsertCharFormatL(aFormat, aMask,aPos);
+	}
+
+void CancelInsertCharFormat(TAny* aCRichTextIndex)
+	{
+	reinterpret_cast<CRichTextIndex*>(aCRichTextIndex)->CancelInsertCharFormat();
+	}
+
+void CRichText::SetExtendedInsertCharFormatL(const TCharFormatX& aFormat, const TCharFormatXMask& aMask, TInt aPos)
+	{
+	__ASSERT_ALWAYS(aPos >= 0 && aPos <= DocumentLength(), Panic(ECharPosBeyondDocument));
+	CreateAndGenerateMarkupComponentL();
+	CRichTextIndex* index = iIndex.AsPtr();
+	if (index->InsertCharFormatIsActive())
+		{
+		TCharFormatX format = aFormat;
+        TCharFormatXMask mask = aMask;
+        CCharFormatLayer* currentLayer = index->GetCurrentInsertCharFormat();
+        currentLayer->Sense(format,mask);
+		CCharFormatLayer* newLayer = CCharFormatLayer::NewCopyBaseL(currentLayer);
+		CleanupStack::PushL(newLayer);
+        newLayer->SetL(format,mask);
+		if (Read(aPos, 1)[0] == EParagraphDelimiter)
+			index->ApplyCharFormatL(aFormat, aMask, aPos, 1, EFalse);
+		if (index->InsertCharFormatIsActive())
+			{
+			currentLayer = index->GetCurrentInsertCharFormat();
+			currentLayer->Swap(*newLayer);
+			}
+		CleanupStack::PopAndDestroy(newLayer);
+		}
+	else
+		{
+		TCleanupItem cleanup(::CancelInsertCharFormat, index);
+		CleanupStack::PushL(cleanup);
+		index->NewInsertCharFormatL(aFormat, aMask, aPos);
+		if (Read(aPos, 1)[0] == EParagraphDelimiter)
+			index->ApplyCharFormatL(aFormat, aMask, aPos, 1, EFalse);
+		CleanupStack::Pop();
+		}
+	SetHasChanged(TRUE);
+	}
+	
+EXPORT_C void CRichText::ExtendedInterface(TAny*& aInterface, TUid aInterfaceId)
+/**
+Returns the interface corresponding to the specified UID if it exists, or 0 if not. 
+Overridden versions should base call rather than returning 0.
+For KUidRichText, CRichText will be returned if rich text is supported.
+
+@param aInterfaceId The UID indicating the interface to return
+@param aInterface The interface corresponding to aInterfaceId
+if it is supported, or 0 if it is not
+*/
+	{ 
+	if(KUidRichText == aInterfaceId) 
+		{
+		aInterface = REINTERPRET_CAST(TAny*, this);
+		}
+	else
+		{
+	    CGlobalText::ExtendedInterface(aInterface, aInterfaceId);
+		}
+	}
+
+EXPORT_C TBool CRichText::DelSetInsertCharFormatL(TInt aPos, TInt aLength)
+/** Deletes a range of characters. The range affected is from aPos to
+aPos+(aLength-1) inclusive. It differs from DeleteL() in that this function
+preserves the formatting of the deleted character at position aPos, so that
+any text subsequently inserted at aPos will have that formatting applied to it.
+
+A panic occurs if:
+
+after calling this function, text is inserted at a different position to aPos,
+without calling CancelInsertCharFormat() before the insertion
+
+aPos is invalid
+
+aLength is negative
+
+the range goes beyond the end of the document
+
+@param aPos The document position of the first character to delete.
+@param aLength The number of characters to delete.
+@return ETrue if two paragraphs have been merged as a result of the deletion;
+EFalse if there has been no paragraph merge. */
+	{
+	// Delete aLength characters, commencing at, and including, aPos.
+	// If aPos is on a phrase boundary, and the whole phrase or more is deleted then
+	// remember temporarily the phrase format.  This is applied to any content that is
+	// immediately inserted.
+	//
+	__TEST_INVARIANT;
+	__ASSERT_ALWAYS(aPos >= 0 && aPos <= DocumentLength(), Panic(ECharPosBeyondDocument));
+	__ASSERT_ALWAYS(aLength >= 0, Panic(EDebugDeleteZeroLength));
+	__ASSERT_ALWAYS(aPos + (aLength - 1) <= DocumentLength(), Panic(ECharPosBeyondDocument));
+
+	TBool parasMerged = EFalse;
+	if (!IndexPresent())
+		CPlainText::DeleteL(aPos, aLength);
+	else
+		{
+		parasMerged = iIndex->DelSetInsertCharFormatL(aPos, aLength);
+		CPlainText::Delete(aPos,aLength);
+		SetHasChanged(ETrue);
+		}
+	iParserData->MergeRange(aPos,aLength,0);
+	CallEditObserver(aPos,-aLength);
+
+	__TEST_INVARIANT;
+	return parasMerged;
+	}
+
+EXPORT_C void CRichText::CancelInsertCharFormat()
+/** Cancels the "insert pending" state set by a call to SetInsertCharFormatL()
+or DelSetInsertCharFormatL().
+
+This function removes the restriction on the text insertion position imposed
+by these two functions. It is recommended that it is called before every cursor
+movement, scroll, paste, etc. This call is a small overhead, and has no effect
+if not applicable. */
+	{
+	// Cancels the transitory state where a specified character format
+	// is applied *on top of* any inherited formatting.  eg, when bold is on.
+	// Cancel when: (1) the text position is altered.  (2) the first character
+	// has been inserted following the setting of this state.
+	//
+	if (IndexPresent() && iIndex->InsertCharFormatIsActive())
+		{
+		iIndex->CancelInsertCharFormat();
+		SetHasChanged(ETrue);
+		}
+	}
+
+EXPORT_C void CRichText::ApplyParaFormatL(const CParaFormat* aFormat, const TParaFormatMask& aMask, TInt aPos, TInt aLength)
+/** Applies paragraph formatting to a range of paragraphs. The attributes which
+are set in the mask are taken from aFormat and applied. The attributes which
+are not set in the mask are not changed.
+
+The region affected consists of every paragraph containing one or more
+characters in the range aPos to aPos+(aLength-1).
+
+@param aFormat Contains the paragraph format attribute values to apply.
+@param aMask Specifies which paragraph format attributes should be affected.
+@param aPos The document position of the start of the range.
+@param aLength The number of characters in the range. */
+	{
+	// Applies the specified format attributes to the paragraphs covering
+	// character position aPos to aPos+aLength-1.
+	//
+	__TEST_INVARIANT;
+	__ASSERT_ALWAYS(aPos >= 0 && aPos <= DocumentLength(), Panic(ECharPosBeyondDocument));
+	__ASSERT_ALWAYS(aLength >= 0,Panic(EApplyParaFormatNegativeLength));
+	__ASSERT_ALWAYS(aPos + (aLength - 1) <= DocumentLength(), Panic(ECharPosBeyondDocument));
+
+	CreateAndGenerateMarkupComponentL();
+	iIndex->ApplyParaFormatL(aFormat, aMask, aPos, aLength);
+	SetHasChanged(ETrue);
+
+	__TEST_INVARIANT;
+	}
+
+EXPORT_C void CRichText::ApplyCharFormatL(const TCharFormat& aFormat, const TCharFormatMask& aMask, TInt aPos, TInt aLength)
+/** Applies character formatting to a range of characters. The attributes which
+are set in the mask are read from aFormat and applied. The attributes which
+are not set in the mask are not changed. The range of characters affected
+is from aPos to aPos+(aLength-1) inclusive. The sum of aPos and aLength
+must be less than or equal to the document length, or a panic occurs.
+
+@param aFormat Contains the character format attribute values to apply.
+@param aMask Bitmask specifying which character format attributes should be
+applied.
+@param aPos Document position from which to apply the new character formatting.
+Must be greater than or equal to zero, or a panic occurs.
+@param aLength The number of characters to which the new formatting should
+be applied. Must be greater than or equal to zero, or a panic occurs. If the
+length is zero, the function has the same effect as SetInsertCharFormatL(). */
+	{
+	// Applies the specified character formatting to the characters conatined within the range
+	// aPos to aPos+(aLength-1).
+	// If aLength is zero, the SetInsertCharFormat state is called.
+	//
+	__ETEXT_WATCH(APPLY_CHAR_FORMAT);
+	__TEST_INVARIANT;
+	
+	TInt document_length = DocumentLength();
+	__ASSERT_ALWAYS(aPos >= 0,Panic(ECharPosBeyondDocument));
+	__ASSERT_ALWAYS(aLength >= 0,Panic(EApplyCharFormatNegativeLength));
+	__ASSERT_ALWAYS(aPos + aLength - 1 <= document_length,Panic(ECharPosBeyondDocument));
+
+	//If some characters are highlighted AND current position + highlighted txt = document length	
+	// Fix for INC097216. Compensate for the changes introduced to Form in defect fix INC087637,
+	// which now considers the height of the EOD character; meaning that this character now 
+	// needs to be formatted along with rest of text.
+	if ((aLength > 0) && (aPos + aLength == document_length))
+		{
+		aLength++;
+		}
+	
+	DoApplyExtendedCharFormatL(aFormat, aMask, aPos, aLength);
+	
+	__TEST_INVARIANT;
+	__ETEXT_WATCH_END(APPLY_CHAR_FORMAT);
+	}
+	
+// This method is used internally only. It does not format the EOD character.
+void CRichText::ApplyExtendedCharFormatL(const TCharFormatX& aFormat,const TCharFormatXMask& aMask,TInt aPos,TInt aLength)
+	{
+	TInt document_length = DocumentLength();
+	__ASSERT_ALWAYS(aPos >= 0,Panic(ECharPosBeyondDocument));
+	__ASSERT_ALWAYS(aLength >= 0,Panic(EApplyCharFormatNegativeLength));
+	__ASSERT_ALWAYS(aPos + aLength - 1 <= document_length,Panic(ECharPosBeyondDocument));
+	
+	DoApplyExtendedCharFormatL(aFormat, aMask, aPos, aLength);
+	}	
+	
+// Apply the extended character formatting.	
+void CRichText::DoApplyExtendedCharFormatL(const TCharFormatX& aFormat,const TCharFormatXMask& aMask,TInt aPos,TInt aLength)
+	{
+	if (aLength > 0)
+		{
+		CreateAndGenerateMarkupComponentL();
+		iIndex->ApplyCharFormatL(aFormat,aMask,aPos,aLength);
+		SetHasChanged(ETrue);
+		}
+	else
+		SetExtendedInsertCharFormatL(aFormat,aMask,aPos);
+	}		
+
+EXPORT_C void CRichText::SetStyleListExternallyOwned(const CStyleList& aStyleList)
+/** Assigns an externally owned style list to the rich text object.
+Replaces any previous style list used by the object. Calls
+SetStyleListExternallyOwned(ETrue).
+
+@param aExternallyOwned The style list to assign to this rich text object.
+Not owned by the rich text object. */
+	{
+	CStyleList* styleList = CONST_CAST(CStyleList*, &aStyleList);
+	iStyleList = styleList;
+	SetStyleListExternallyOwned(ETrue);
+	}
+
+EXPORT_C void CRichText::ApplyParagraphStyleL(const CParagraphStyle& aStyle, TInt aPos, TInt aLength, CParagraphStyle::TApplyParaStyleMode aMode)
+/** Applies a specified paragraph style to a range of paragraphs. The region
+affected consists of every paragraph containing one or more characters in the
+range aPos to aPos+(aLength-1).
+
+A panic occurs if:
+
+aPos is invalid, or
+
+aLength is negative, or
+
+the range goes beyond the end of the document, or
+
+the rich text object has no style list
+
+@param aStyle The style to apply.
+@param aPos The document position of the start of the range.
+@param aLength The number of characters in the range.
+@param aMode Controls what specific formatting, if any, should be preserved
+when the style is applied. */
+	{
+	// Applies the specified paragraph style to the paragraphs covering
+	// character positions aPos to aPos+aLength-1.
+	//
+	__TEST_INVARIANT;
+	__ASSERT_ALWAYS(aPos >= 0 && aPos <= DocumentLength(), Panic(ECharPosBeyondDocument));
+	__ASSERT_ALWAYS(aLength >= 0, Panic(EApplyParaStyleNegativeLength));
+	__ASSERT_ALWAYS(aPos + (aLength - 1) <= DocumentLength(), Panic(ECharPosBeyondDocument));
+	__ASSERT_ALWAYS(StyleListPresent(), Panic(ERichTextNotSetForUsingStyles));
+
+	CreateAndGenerateMarkupComponentL();
+	iIndex->ApplyParagraphStyleL(aStyle, aPos, aLength, iGlobalCharFormatLayer, aMode);
+	SetHasChanged(ETrue);
+
+	__TEST_INVARIANT;
+	}
+
+EXPORT_C void CRichText::NotifyStyleChangedL(const CParagraphStyle* aTo, const CParagraphStyle* aFrom)
+/** Removes a style from every paragraph in the document to which it applies,
+and replaces it with another.
+
+If style aTo is NULL, aFrom is replaced by the global character and paragraph
+format layers, so that in effect, style aFrom is removed. Any specific
+formatting which has been applied to the paragraphs is retained.
+
+Notes:
+
+This function should be called on the text content object after changing a
+style in the style list.
+
+A panic occurs if the rich text object does not use a style list (this can
+be tested for using StyleListPresent()).
+
+@param aTo The new paragraph style to apply.
+@param aFrom The paragraph style to remove. */
+	{
+	// Update the rich text index following the change of an applied paragraph style.
+	//
+	__TEST_INVARIANT;
+	__ASSERT_ALWAYS(StyleListPresent(), Panic(ERichTextNotSetForUsingStyles));
+
+	CreateAndGenerateMarkupComponentL();
+	iIndex->NotifyStyleChangedL(aTo, aFrom, *iGlobalParaFormatLayer, *iGlobalCharFormatLayer);
+	SetHasChanged(ETrue);
+
+	__TEST_INVARIANT;
+	}
+
+EXPORT_C const CParaFormatLayer* CRichText::ParagraphStyle(TBool& aStyleChangesOverRange, TInt aPos, TInt aLength) const
+/** Gets a pointer to the first paragraph style encountered in the specified
+range.
+
+@param aStyleChangesOverRange On return, set to ETrue if more than one paragraph
+style is used over the specified range of characters. Otherwise EFalse
+@param aPos The document position of the start of the range. Must be valid.
+@param aLength The number of characters in the range. Must be greater than
+or equal to zero.
+@return Pointer to the paragraph style which applies to the paragraph containing
+document position aPos. Its type (returned by CParaFormatLayer::Type())
+indicates whether this object is a style, or just a paragraph format layer. */
+	{
+	__TEST_INVARIANT;
+	__ASSERT_ALWAYS(aPos >= 0 && aPos <= DocumentLength(), Panic(ECharPosBeyondDocument));
+	__ASSERT_ALWAYS(aLength >= 0,Panic(EParagraphStyleNegativeLength));
+	__ASSERT_ALWAYS(aPos + (aLength - 1) <= DocumentLength(), Panic(ECharPosBeyondDocument));
+
+	if (IndexPresent())
+		return iIndex->ParagraphStyle(aStyleChangesOverRange, aPos, aLength);
+	else
+		{
+		aStyleChangesOverRange = EFalse;
+		return iGlobalParaFormatLayer;
+		}
+	}
+
+EXPORT_C void CRichText::SetHasChanged(TBool aHasChanged)
+/** Sets whether the document's content or formatting has changed. This function
+is called with an value of ETrue by all functions which modify the text content
+or formatting. Use CEditableText::HasChanged() to test whether the document
+has changed.
+
+@param aHasChanged ETrue if the text object has been changed, EFalse if not. */
+	{
+	// Replaces the base class method of the same name.
+	//
+	if (aHasChanged && IndexPresent())
+		iIndex->DocumentChanged();
+	CGlobalText::SetHasChanged(aHasChanged);
+	}
+
+EXPORT_C void CRichText::RemoveSpecificParaFormatL(TInt aPos, TInt aLength)
+/** Removes all specific paragraph formatting from a range of paragraphs. This
+does not remove formatting from the object's global paragraph format layer.
+The region affected consists of every paragraph containing one or more
+characters in the range covered by document position aPos to aPos+(aLength-1)
+inclusive.
+
+A panic occurs in the following situations:
+
+the position is negative,
+
+the length is negative,
+
+the range goes beyond the end of the document
+
+@param aPos The document position of the start of the range.
+@param aLength The number of characters in the range. */
+	{
+	// Removes all specific paragraph formatting from the selected region.
+	//
+	__ETEXT_WATCH(REMOVE_PARA_FORMAT);
+
+	__TEST_INVARIANT;
+
+	__ASSERT_ALWAYS(aPos >= 0 && aPos <= DocumentLength(), Panic(ECharPosBeyondDocument));
+	__ASSERT_ALWAYS(aLength >= 0, Panic(ERemoveSpecificParaFormatNegativeLength));
+	__ASSERT_ALWAYS(aPos + (aLength - 1) <= DocumentLength(), Panic(ECharPosBeyondDocument));
+
+	if (IndexPresent())
+		{
+		iIndex->RemoveSpecificParaFormatL(aPos, aLength);
+		SetHasChanged(ETrue);
+		}
+
+	__TEST_INVARIANT;
+
+	__ETEXT_WATCH_END(REMOVE_PARA_FORMAT);
+	}
+
+EXPORT_C void CRichText::RemoveSpecificCharFormatL(TInt aPos, TInt aLength)
+/** Removes all specific character formatting from a range of characters (does
+not remove the formatting which has been taken from the object's global character
+format layer). A panic occurs in the following situations:
+
+the position is negative,
+
+the length is negative,
+
+the range goes beyond the end of the document
+
+@param aPos The document position of the start of the region affected.
+@param aLength The number of characters in the region affected. If zero, the
+function has no effect. */
+	{
+	__TEST_INVARIANT;
+	
+	TInt document_length = DocumentLength();
+	__ASSERT_ALWAYS(aPos >= 0 && aPos <= DocumentLength(), Panic(ECharPosBeyondDocument));
+	__ASSERT_ALWAYS(aLength >= 0, Panic(ERemoveSpecificParaFormatNegativeLength));
+	__ASSERT_ALWAYS(aPos + (aLength - 1) <= DocumentLength(), Panic(ECharPosBeyondDocument));
+	
+	// in correspondance to INC097216, character format removing considers the height of end of document
+    // character
+    if (aPos + aLength == document_length)
+   		{
+        aLength++;
+        }
+
+	if (aLength > 0 && IndexPresent())
+		{
+		iIndex->RemoveSpecificCharFormatL(aPos, aLength);
+		SetHasChanged(ETrue);
+		}
+
+	__TEST_INVARIANT;
+	}
+
+EXPORT_C void CRichText::GetChars(TPtrC& aText, TCharFormat& aFormat, TInt aPos) const
+/** Gets a constant pointer descriptor to a portion of the text object
+with constant character formatting.
+
+The view starts at the document position specified, and ends at: the
+last character which shares the same character formatting, orthe end
+of the document, orthe end of the segment, if segmented storage is
+being usedwhichever occurs first. Also fills a character format object
+with the character formatting of the range of characters.
+
+@param aView On return, a constant pointer to a portion of the text.
+@param aFormat On return, contains the character formatting of the text.
+@param aPos The start position for the view. Must be a valid document
+position, or a panic occurs. */
+	{
+	// Get a run of text and its format, starting at aPos.
+	__ETEXT_WATCH(GET_CHARS)
+	TCharFormatX format;
+	GetTextAndExtendedFormat(aText, format, aPos);
+	OverrideFormatForParsersIfApplicable(aText, format, aPos);
+	aFormat = format.iCharFormat;
+	OverrideFormatOfInlineTextIfApplicable(aText, aFormat, aPos);
+	__ETEXT_WATCH_END(GET_CHARS)
+	}
+
+
+void CRichText::GetTextAndExtendedFormat(TPtrC& aText,TCharFormatX& aFormat,TInt aPos) const
+	{
+	__TEST_INVARIANT;
+	TInt documentLength = DocumentLength();
+	__ASSERT_ALWAYS(aPos >= 0 && aPos <= documentLength, Panic(ECharPosBeyondDocument));
+	if (!IndexPresent())
+		{
+		aText.Set(Read(aPos));
+		iGlobalCharFormatLayer->SenseEffective(aFormat);
+		}
+	else
+		{
+		int phrase_length = iIndex->GetChars(aFormat,aPos);
+		aText.Set(Read(aPos,phrase_length));
+		}
+	__TEST_INVARIANT;
+	}
+
+EXPORT_C TInt CRichText::GetPictureSizeInTwips(TSize& aSize, TInt aPos) const
+/** Gets the size of a picture located at a specified document position.
+
+@param aSize On return, contains the size of the picture located at aPos.
+@param aPos Document position of the picture. Must be a valid position.
+@return KErrNotFound if there is no picture at the specified document position,
+KErrNone if there is. */
+	{
+	__TEST_INVARIANT;
+	__ASSERT_ALWAYS(aPos >= 0 && aPos <= DocumentLength(), Panic(ECharPosBeyondDocument));
+
+	if (IndexPresent())
+		return iIndex->GetPictureSizeInTwips(aSize, aPos);
+	else
+		return KErrNotFound;
+	}
+
+EXPORT_C CPicture* CRichText::PictureHandleL(TInt aPos, MLayDoc::TForcePictureLoad aForceLoad) const
+/** Gets a pointer to the picture located at a specified document position, if
+one exists. If the picture is not in memory, the function loads it (if the
+second argument has a value of MLayDoc::EForceLoadTrue).
+
+Note:
+
+In order to load the picture, a picture factory and a store resolver must
+have been set.
+
+@param aPos Document position of the picture character. Must be a valid position.
+@param aForceLoad If the picture is not loaded into memory,
+MLayDoc::EForceLoadTrue loads it using the picture factory;
+MLayDoc::EForceLoadFalse does not, and in this case, the function returns NULL.
+@return A pointer to the picture located at aPos. NULL if aPos does not specify
+a picture character, or if there is a picture at aPos which is not in memory,
+and the second argument is MLayDoc::EForceLoadFalse. */
+	{
+	__ETEXT_WATCH(PICTURE_HANDLE);
+
+	__TEST_INVARIANT;
+	__ASSERT_ALWAYS(aPos>=0 && aPos<=DocumentLength(),Panic(ECharPosBeyondDocument));
+
+	if (IndexPresent())
+		return iIndex->PictureHandleL(aPos, aForceLoad);
+	else
+		return NULL;
+
+	__ETEXT_WATCH_END(PICTURE_HANDLE);
+	}
+
+EXPORT_C void CRichText::GetParagraphFormatL(CParaFormat* aFormat, TInt aPos) const
+/** Gets the effective paragraph formatting which applies to the paragraph which
+contains a specified document position. On return, aFormat is filled with
+values for all paragraph format attributes.
+
+@param aFormat On return, filled with the paragraph's effective paragraph
+formatting.
+@param aPos Any document position within the paragraph of interest. */
+	{
+	__ETEXT_WATCH(GET_PARAGRAPH_FORMAT)
+
+	__TEST_INVARIANT;
+	__ASSERT_ALWAYS(aPos >= 0 && aPos <= DocumentLength(), Panic(ECharPosBeyondDocument));
+
+	if (!IndexPresent())
+		CGlobalText::GetParagraphFormatL(aFormat, aPos);
+	else
+		{
+		aFormat->Reset();
+		iIndex->GetParagraphFormatL(aFormat, aPos);
+		}
+
+	__ETEXT_WATCH_END(GET_PARAGRAPH_FORMAT)
+	}
+
+EXPORT_C void CRichText::GetSpecificParagraphFormatL(CParaFormat* aFormat,
+													 TParaFormatMask& aMask,
+													 TInt aPos) const
+// Fills aFormat with the effective Paragraph format attributes for the paragraph
+// in which character position aPos is contained.
+//
+	{
+	__ETEXT_WATCH(GET_PARAGRAPH_FORMAT)
+
+	__TEST_INVARIANT;
+	__ASSERT_ALWAYS(aPos >= 0 && aPos <= DocumentLength(), Panic(ECharPosBeyondDocument));
+
+	aFormat->Reset();
+	aMask.ClearAll();
+	if (IndexPresent())
+		iIndex->GetSpecificParagraphFormatL(aFormat, aMask, aPos);
+
+	__ETEXT_WATCH_END(GET_PARAGRAPH_FORMAT)
+	}
+
+EXPORT_C void CRichText::GetParaFormatL(CParaFormat* aFormat, TParaFormatMask& aVaries, TInt aPos, TInt aLength,
+										CParaFormat::TParaFormatGetMode aMode) const
+/** Gets the effective paragraph formatting which applies to a range of paragraphs.
+The region involved is every paragraph containing one or more characters in
+the range aPos to aPos+(aLength-1) inclusive. On return, aFormat is filled
+with values for all paragraph format attributes and the mask indicates the
+values that change over the region, and whose value is therefore indeterminate.
+
+Note:
+
+If aMode has a value of EFixedAttributes, the function cannot leave.
+
+@param aFormat Must not be NULL or a panic occurs. On return, contains the
+effective paragraph formatting for the range of paragraphs.
+@param aVaries On return, a bitmask indicating which paragraph format attributes
+vary over the range of characters selected.
+@param aPos The document position of the start of the range.
+@param aLength The number of characters in the range.
+@param aMode The default, EAllAttributes means that values for all paragraph
+format attributes are written to aFormat. EFixedAttributes means that tabs,
+bullets and borders are not written to aFormat. */
+	{
+	// Senses the paragraph format of para(s) covered by the region aPos to aPos+aLength-1.
+	// aFormat takes the values of all attributes, and the mask aMask indicates those values that change
+	// over the selected region, and are therefore *indeterminate*.
+	// Application: seeding paragraph formatting dialogs.
+	//
+	__TEST_INVARIANT;
+	__ASSERT_ALWAYS(aPos >= 0 && aPos <= DocumentLength(), Panic(ECharPosBeyondDocument));
+	__ASSERT_ALWAYS(aLength >= 0, Panic(EGetParaFormatNegativeLength));
+	__ASSERT_ALWAYS(aPos + aLength <= DocumentLength(), Panic(ECharPosBeyondDocument));
+
+	if (IndexPresent())
+		iIndex->GetParaFormatL(aFormat, aVaries, aPos, aLength, aMode);
+	else
+		CGlobalText::GetParaFormatL(aFormat, aVaries, aPos, aLength, aMode);
+	}
+
+EXPORT_C void CRichText::GetCharFormat(TCharFormat& aFormat, TCharFormatMask& aVaries, TInt aPos, TInt aLength) const
+/** Gets the effective character formatting which applies to a range of
+characters. The range of characters involved is from aPos to aPos+(aLength-1)
+inclusive. On return, aFormat is filled with values for all character format
+attributes, and on return, the mask indicates the values that change over the
+region, and whose value is therefore indeterminate.
+
+The length value can be zero. In this case, the character formatting sensed
+is that of the character immediately to the left of the position specified.
+
+@param aFormat On return, contains the character format values for the range
+of characters.
+@param aVaries On return, indicates which character format attributes vary
+over the range.
+@param aPos Document position of the start of the range. Must be valid or a
+panic occurs.
+@param aLength Number of characters in the range. Must be greater than or equal
+to zero, or a panic occurs. */
+	{
+	// Senses the character formatting of the phrase(s) covered by the region aPos to aPos+aLength-1.
+	// May be called with zero length.
+	// aFormat takes the values of all character format attributes, and the mask aMask indicates those
+	// values that change over the selected region, and are therefore *indeterminate*.
+	// Application: seeding character formatting dialogs.
+	//
+	TCharFormatX format;
+	TCharFormatXMask varies;
+	GetExtendedCharFormat(format, varies, aPos, aLength);
+	aFormat = format.iCharFormat;
+	varies.ClearExtendedAttribs();
+	aVaries = varies;
+	}
+
+
+void CRichText::GetExtendedCharFormat(TCharFormatX& aFormat, TCharFormatXMask& aVaries, TInt aPos, TInt aLength) const
+	{
+	__TEST_INVARIANT;
+	int document_length = DocumentLength();
+	__ASSERT_ALWAYS(aPos >= 0 && aPos <= document_length, Panic(ECharPosBeyondDocument));
+	__ASSERT_ALWAYS(aLength >= 0, Panic(EGetCharFormatNegativeLength));
+	__ASSERT_ALWAYS(aPos + aLength - 1 <= document_length, Panic(ECharPosBeyondDocument));
+
+	if (IndexPresent())
+		iIndex->GetCharFormat(aFormat, aVaries, aPos, aLength);
+	else
+		{
+		iGlobalCharFormatLayer->SenseEffective(aFormat);
+		aVaries.ClearAll();
+		}
+	}
+
+
+void CRichText::GetSpecificCharFormatLeftRight(TCharFormat& aFormat,
+									TCharFormatMask& aMask,
+									TInt aPos,
+									TBool aLeft) const
+	{
+	__ETEXT_WATCH(GET_SPECIFIC_CHARS);
+
+	__TEST_INVARIANT;
+
+	__ASSERT_ALWAYS(aPos >= 0 && aPos <= DocumentLength(), Panic(ECharPosBeyondDocument));
+
+	if (IndexPresent())
+		{
+		TCharFormatX format;
+		TCharFormatXMask mask;
+		iIndex->GetSpecificCharFormatDirection(format, mask, aPos, aLeft);
+		aFormat = format.iCharFormat;
+		mask.ClearExtendedAttribs();
+		aMask = mask;
+		}
+
+	__ETEXT_WATCH_END(GET_SPECIFIC_CHARS);
+	}
+
+EXPORT_C void CRichText::GetSpecificCharFormat(TCharFormat& aFormat, TCharFormatMask& aMask, TInt aPos) const
+/** Gets the specific character formatting which applies to the character to the
+left of a document position. Specific formatting is just the formatting which
+has been applied over the object's global format layers - it is not the
+same as the effective formatting.
+
+@param aFormat On return contains the specific character formatting which
+applies to the character to the left of the document position.
+@param aMask On return, indicates which character format attributes have been
+applied specifically, (not taken from the global layers).
+@param aPos The document position. Must be valid or a panic occurs. */
+	{
+	// Return the format attributes store in the specific layer only, for the specified document position.
+	// THIS IS NOT THE EFFECTIVE FORMAT, BUT THE SPECIFIC FORMATTING ONLY.
+	//
+	GetSpecificCharFormatLeftRight(aFormat, aMask, aPos, ETrue);
+	}
+
+EXPORT_C void CRichText::GetSpecificCharFormatRight(TCharFormat& aFormat, TCharFormatMask& aMask, TInt aPos) const
+/** Gets the specific character formatting which applies to the character logically
+after a document position. Note that this is not necessarily the character
+to the right of the document position, because right to left text is supported.
+
+Specific formatting is just the formatting which has been applied over the
+object's global format layers it is not the same as the effective formatting.
+
+@param aFormat On return, contains the specific character formatting which
+applies to the character logically after the document position.
+@param aMask On return, indicates which character format attributes have been
+applied specifically, (not taken from the global layers).
+@param aPos The document position. Must be valid or a panic occurs. */
+	{
+	GetSpecificCharFormatLeftRight(aFormat, aMask, aPos, EFalse);
+	}
+
+TBool CRichText::IndexPresent()const
+// Returns ETrue if the rich text index is present, otherwise
+// returns EFalse.
+//
+	{
+	return iIndex.IsPtr() && iIndex.AsPtr();
+	}
+
+
+EXPORT_C TInt CRichText::PictureCount() const
+/** Gets a count of the number of pictures contained in the rich text object.
+
+@return The picture count. */
+	{return (IndexPresent()) ? iIndex->iPictureCount : 0;}
+
+
+void CRichText::SetParaTypeIsSingle(TBool aBool)
+	{
+	if (aBool)
+		iFlags |= KParaTypeIsSingle;
+	else
+		iFlags &= ~KParaTypeIsSingle;
+	}
+
+
+TBool CRichText::ParaTypeIsSingle() const
+	{return iFlags & KParaTypeIsSingle;}
+
+
+EXPORT_C void CRichText::AppendTakingSolePictureOwnershipL(const CRichText& aSource)
+/** Appends a rich text object to this one. The text is appended immediately after
+the end-of-text paragraph delimiter. The incoming text's formatting is set
+to be based on the global formatting of this rich text object.
+
+Notes:
+
+If this rich text object is empty (e.g. because it is newly initialised, or
+has been reset), then the end-of-text delimiter of the incoming rich text
+is not appended. This avoids the possibility of having a trailing paragraph
+delimiter, giving one more empty line than would typically be desired.
+
+If the incoming rich text contains pictures which have been loaded into memory,
+their sole ownership is transferred to the current rich text object. In aSource,
+these picture handles are set to NULL.
+
+@param aSource The rich text object to append. */
+	{
+	// Appends the specified rich text object to this one.
+	// If this rich text is empty or has been newly initialised or reset, then the final paragraph delimiter
+	// of the incoming rich text is NOT appended, thus avoiding the prospect of having a trailing, empty
+	// paragraph delimiter, giving one more empty line than would typically be desired.
+	//
+	__ETEXT_WATCH(APPEND_RICH_TEXT);
+
+	// Append the text
+	const TInt thisDocumentLength = DocumentLength();
+	MPictureFactory* factory = NULL;
+	MRichTextStoreResolver* resolver = NULL;
+	if (thisDocumentLength == 0)  // this is an empty document
+		{
+		factory = iPictureFactory;
+		resolver = iStoreResolver;
+		Reset();  // destroy the markup component if it exists.  Makes the job easier
+		}
+	//
+	PrepareAppendMarkupL(aSource);  // requires no rollback - objects in a (bigger) stable state
+	//
+	TRAPD(ret, DoAppendTakingSolePictureOwnershipL(aSource));
+	if (ret != KErrNone)
+		{
+		CPlainText::Delete(thisDocumentLength,DocumentLength() - thisDocumentLength);
+		User::Leave(ret);
+		}
+	if (thisDocumentLength == 0)
+		{
+		DeleteParagraph(0, 1);   // remove excess first paragraph from empty documents
+		SetPictureFactory(factory, resolver);
+		}
+	int new_length = DocumentLength() - thisDocumentLength;
+	iParserData->MergeRange(thisDocumentLength,0,new_length);
+	CallEditObserver(thisDocumentLength,new_length);
+
+	__TEST_INVARIANT;
+
+	__ETEXT_WATCH_END(APPEND_RICH_TEXT);
+	}
+
+
+void CRichText::DoAppendTakingSolePictureOwnershipL(const CRichText& aSource)
+	{
+	TInt lengthRemaining = aSource.DocumentLength() + 1;  // we want to append the para delimiters also!
+	TInt consumed = 0;
+	FOREVER
+		{
+		TPtrC view = aSource.Read(consumed);
+		if (view.Length() > lengthRemaining)
+			view.Set(view.Ptr(), lengthRemaining);
+		CPlainText::DoPtInsertL(DocumentLength() + 1, view);  // insert AFTER the final paragraph delimiter
+		TInt viewLength = view.Length();
+		lengthRemaining -= viewLength;
+		if (lengthRemaining == 0)
+			break;
+		consumed += viewLength;
+		}
+
+	if (IndexPresent())
+		{
+		__ASSERT_DEBUG(aSource.IndexPresent(), User::Invariant());       // PrepareAppend should have sorted this
+
+		TGlobalLayerInfoAppend info(GlobalParaFormatLayer(), GlobalCharFormatLayer(), aSource.GlobalParaFormatLayer(), aSource.GlobalCharFormatLayer());
+		iIndex->AppendTakingSolePictureOwnershipL(aSource.iIndex, info);
+		}
+
+	__TEST_INVARIANT;
+	}
+
+
+void CRichText::PrepareAppendMarkupL(const CRichText& aSource)
+// Guarantees that both the component and aggregate objects have a valid markup component.
+//
+	{
+	if (aSource.HasMarkupData() && !HasMarkupData())
+		CreateAndGenerateMarkupComponentL();
+	else if (IndexPresent())
+		CONST_CAST(CRichText&, aSource).CreateAndGenerateMarkupComponentL();
+	}
+
+
+EXPORT_C void CRichText::AppendParagraphL(TInt aReplicas)
+/** Appends one or more empty paragraphs to the document. The new paragraphs take
+on the formatting specified in the global format layers.
+
+@param aReplicas The number of empty paragraphs to append to the document.
+By default, a single paragraph. */
+	{
+	// Inserts an empty paragraph at the end of the document.
+	// The new paragraph takes on the format as described
+	// by the global format layers.
+	//
+	__ETEXT_WATCH(APPEND_PARAGRAPH);
+
+	__TEST_INVARIANT;
+
+	TInt documentLength = DocumentLength();
+	if (aReplicas == 1)
+		CPlainText::InsertL(documentLength, CEditableText::EParagraphDelimiter);
+	else
+		{
+		HBufC* bb = HBufC::NewLC(aReplicas);
+		TPtr buf = bb->Des();
+		buf.Fill(CEditableText::EParagraphDelimiter, aReplicas);
+		CPlainText::InsertL(documentLength, buf);
+		CleanupStack::PopAndDestroy();  // bb
+		}
+	//
+	if (IndexPresent())
+		{
+		TRAPD(ret,
+		iIndex->AppendParagraphL(iGlobalParaFormatLayer, iGlobalCharFormatLayer, aReplicas));
+		if (ret != KErrNone)
+			{
+			CPlainText::Delete(DocumentLength() - aReplicas, aReplicas);
+			User::Leave(ret);
+			}
+		SetHasChanged(ETrue);
+		}
+
+	int new_length = DocumentLength() - documentLength;
+	iParserData->MergeRange(documentLength,0,new_length);
+	CallEditObserver(documentLength,new_length);
+	__TEST_INVARIANT;
+
+	__ETEXT_WATCH_END(APPEND_PARAGRAPH);
+	}