text: /*
   *	@doc TOM
   *
   *	@module	tomfmt.cpp - Implement the CTxtFont and CTxtPara Classes |
   *
   *		This module contains the implementation of the TOM ITextFont and
   *		ITextPara interfaces
   *
   *	History: <nl>
   *		11/8/95 - MurrayS: created
   *		5/96	- MurrayS: added zombie protection
   *
   *	Copyright (c) 1995-1996, Microsoft Corporation. All rights reserved.
   */
  
  #include "stdafx.h"
  #include "_common.h"
  #include "_tomfmt.h"
  
  ASSERTDATA
  
  // Alignment translation vectors
  BYTE g_rgREtoTOMAlign[] =				// RichEdit to TOM
  {
  	tomAlignLeft, tomAlignLeft, tomAlignRight, tomAlignCenter, tomAlignJustify
  };
  
  BYTE g_rgTOMtoREAlign[] =				// TOM to RichEdit
  {
  	PFA_LEFT, PFA_CENTER, PFA_RIGHT, PFA_JUSTIFY
  };
  
  BOOL	SameVtables(IUnknown *punk1, IUnknown *punk2);
  
  /*
   *	QueryInterface(riid, riid1, punk, ppv, fZombie)
   *
   *	@func
   *		QueryInterface punk for the ref IDs riid1, IID_IDispatch, and
   *		IID_IUnknown
   *
   *	@rdesc
   *		HRESULT = (!ppv) ? E_INVALIDARG :
   *				  (interface found) ? NOERROR : E_NOINTERFACE
   */
  HRESULT QueryInterface(REFIID riid, REFIID riid1, IUnknown *punk,
  	void **ppv, BOOL fZombie)
  {
  	if (!ppv)
  	{
  		return E_INVALIDARG;
  	}
  
  	*ppv = NULL;
  
  	if (fZombie)							// Check for range zombie
  	{
  		return CO_E_RELEASED;
  	}
  
  	Assert(punk);
  
  	if (IsEqualIID(riid, IID_IUnknown)   ||
  		IsEqualIID(riid, IID_IDispatch)  ||
  		IsEqualIID(riid, riid1))
  	{
  		*ppv = punk;
  		punk->AddRef();
  		return NOERROR;
  	}
  	return E_NOINTERFACE;
  }
  
  //------------------------------- CTxtFont -------------------------------------
  
  /*
   *	CTxtFont::CTxtFont(prg)
   *
   *	@mfunc
   *		Constructor
   */
  CTxtFont::CTxtFont(CTxtRange *prg) : CTxtFormat(prg)
  {
  	_CF.dwMask = 0;				// Private CCharFormat is initially undefined
  }
  
  
  //------------------------- CTxtFont IUnknown Methods -------------------------------------
  
  /*	CTxtFont::IUnknown methods
   *
   *		See tomDoc.cpp for comments
   */
  STDMETHODIMP CTxtFont::QueryInterface(REFIID riid, void **ppv)
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::QueryInterface");
  
  	return ::QueryInterface(riid, IID_ITextFont, this, ppv, IsZombie());
  }
  
  STDMETHODIMP_(ULONG) CTxtFont::AddRef()
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::AddRef");
  
  	return ++_cRefs;
  }
  
  STDMETHODIMP_(ULONG) CTxtFont::Release()
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::Release");
  
  	_cRefs--;
  
  	if (!_cRefs)
  	{
  		delete this;
  		return 0;
  	}
  	return _cRefs;
  }
  
  
  //------------------------- CTxtFont IDispatch Methods -------------------------------------
  
  /*
   *	CTxtFont::GetTypeInfoCount(pcTypeInfo)
   *
   *	@mfunc
   *		Get the number of TYPEINFO elements (1)
   *
   *	@rdesc
   *		HRESULT = (pcTypeInfo) ? NOERROR : E_INVALIDARG;
   */
  STDMETHODIMP CTxtFont::GetTypeInfoCount(
  	UINT *pcTypeInfo)			//@parm Out parm to receive type-info count
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::GetTypeInfoCount");
  
  	if (!pcTypeInfo)
  	{
  		return E_INVALIDARG;
  	}
  
  	*pcTypeInfo = 1;
  	return NOERROR;
  }
  
  /*
   *	CTxtFont::GetTypeInfo(iTypeInfo, lcid, ppTypeInfo)
   *
   *	@mfunc
   *		Return ptr to type information object for ITextFont interface
   *
   *	@rdesc
   *		HRESULT
   */
  STDMETHODIMP CTxtFont::GetTypeInfo(
  	UINT		iTypeInfo,		//@parm Index of type info to return
  	LCID		lcid,			//@parm Local ID of type info
  	ITypeInfo **ppTypeInfo)		//@parm Out parm to receive type info
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::GetTypeInfo");
  
  	return ::GetTypeInfo(iTypeInfo, g_pTypeInfoFont, ppTypeInfo);
  }
  
  /*
   *	CTxtFont::GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid)
   *
   *	@mfunc
   *		Get DISPIDs for ITextFont methods and properties
   *
   *	@rdesc
   *		HRESULT
   */
  STDMETHODIMP CTxtFont::GetIDsOfNames(
  	REFIID		riid,			//@parm Interface ID to interpret names for
  	OLECHAR 	**rgszNames,		//@parm Array of names to be mapped
  	UINT		cNames,			//@parm Count of names to be mapped
  	LCID		lcid,			//@parm Local ID to use for interpretation
  	DISPID 	*rgdispid)		//@parm Out parm to receive name mappings
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::GetIDsOfNames");
  
  	HRESULT hr = GetTypeInfoPtrs();				// Ensure TypeInfo ptrs are OK
  	if (hr != NOERROR)
  	{
  		return hr;
  	}
  
  	return g_pTypeInfoFont->GetIDsOfNames(rgszNames, cNames, rgdispid);
  }
  
  /*
   *	CTxtFont::Invoke(dispidMember, riid, lcid, wFlags, pdispparams,
   *					  pvarResult, pexcepinfo, puArgError)
   *	@mfunc
   *		Invoke methods for the ITextFont interface
   *
   *	@rdesc
   *		HRESULT
   */
  STDMETHODIMP CTxtFont::Invoke(
  	DISPID		dispidMember,	//@parm Identifies member function
  	REFIID		riid,			//@parm Pointer to interface ID
  	LCID		lcid,			//@parm Locale ID for interpretation
  	USHORT		wFlags,			//@parm Flags describing context of call
  	DISPPARAMS *pdispparams,	//@parm Ptr to method arguments
  	VARIANT 	*pvarResult,		//@parm Out parm for result (if not NULL)
  	EXCEPINFO *pexcepinfo,		//@parm Out parm for exception info
  	UINT 		*puArgError)		//@parm Out parm for error
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::Invoke");
  
  	HRESULT hr = GetTypeInfoPtrs();				// Ensure TypeInfo ptrs are OK
  	if (hr != NOERROR)
  	{
  		return hr;
  	}
  
  	if (IsZombie())
  	{
  		return CO_E_RELEASED;
  	}
  
  	return g_pTypeInfoFont->Invoke(this, dispidMember, wFlags,
  			pdispparams, pvarResult, pexcepinfo, puArgError);
  }
  
  
  //--------------------------- ITextFont Methods -------------------------------------
  
  /*
   *	ITextFont::CanChange(long * pbCanChange)
   *
   *	@mfunc
   *		Method that sets *pbCanChange = tomTrue if and only if the
   *		font can be changed.
   *
   *	@rdesc
   *		HRESULT = (can change char format) ? NOERROR : S_FALSE
   */
  STDMETHODIMP CTxtFont::CanChange(
  	long *pbCanChange)		//@parm Out parm to receive boolean value
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::CanChange");
  
  	return CTxtFormat::CanChange(pbCanChange);
  }
  
  /*
   *	ITextFont::GetAllCaps(long * pValue)
   *
   *	@mfunc
   *		Property get method that gets the AllCaps state.
   *
   *	@rdesc
   *		HRESULT = (!pValue) ? E_INVALIDARG : NOERROR
   */
  STDMETHODIMP CTxtFont::GetAllCaps(
  	long *pValue)		//@parm Out parm to receive tomBool
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::GetAllCaps");
  
  	return EffectGetter(pValue, CFM_ALLCAPS);
  }
  
  /*
   *	ITextFont::GetAnimation(long * pValue)
   *
   *	@mfunc
   *		Property get method that gets the animation type as defined
   *		in the table below.
   *
   *	@rdesc
   *		HRESULT = (!pValue) ? E_INVALIDARG : NOERROR
   */
  STDMETHODIMP CTxtFont::GetAnimation(
  	long *pValue)		//@parm Out parm to receive animation type
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::GetAnimation");
  
  	return GetParameter((long *)&_CF.bAnimation, CFM_ANIMATION, 1, pValue);
  }
  
  /*
   *	ITextFont::GetBackColor(long * pValue)
   *
   *	@mfunc
   *		Property get method that gets the background color.  The
   *		value is a Win32 COLORREF.
   *
   *	@rdesc
   *		HRESULT = (!pValue) ? E_INVALIDARG : NOERROR
   */
  STDMETHODIMP CTxtFont::GetBackColor(
  	long *pValue)		//@parm Out parm to receive COLORREF value
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::GetBackColor");
  
  	HRESULT hr = EffectGetter(pValue, CFE_AUTOBACKCOLOR);
  
  	if (hr != NOERROR || *pValue == tomUndefined)
  	{
  		return hr;
  	}
  
  	*pValue = (*pValue == tomFalse) ? _CF.crBackColor : tomAutoColor;
  	return NOERROR;
  }
  
  /*
   *	ITextFont::GetBold(long * pValue)
   *
   *	@mfunc
   *		Property get method that gets the bold state.
   *
   *	@rdesc
   *		HRESULT = (!pValue) ? E_INVALIDARG : NOERROR
   */
  STDMETHODIMP CTxtFont::GetBold(
  	long *pValue)		//@parm Out parm to receive tomBool
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::GetBold");
  
  	return EffectGetter(pValue, CFM_BOLD);
  }
  
  /*
   *	ITextFont::GetDuplicate(ITextFont **ppFont)
   *
   *	@mfunc
   *		Property get method that gets a clone of this character
   *		format object.
   *
   *	@rdesc
   *		HRESULT = (!ppFont) ? E_INVALIDARG :
   *				  (if success) ? NOERROR : S_FALSE
   */
  STDMETHODIMP CTxtFont::GetDuplicate(
  	ITextFont **ppFont)		//@parm Out parm to receive font clone
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::GetDuplicate");
  
  	if (!ppFont)
  	{
  		return E_INVALIDARG;
  	}
  
  	*ppFont = NULL;
  
  	if (IsZombie())
  	{
  		return CO_E_RELEASED;
  	}
  
  	CTxtFont *pFont = new CTxtFont(NULL);
  	if (!pFont)
  	{
  		return E_OUTOFMEMORY;
  	}
  
  	if (_prg)								// If live font object, load
  	{
  		_prg->GetCharFormat(&pFont->_CF);    //  clone directly from range
  	}
  
  	else
  	{
  		pFont->_CF = _CF;
  	}
  
  	*ppFont = pFont;
  	return NOERROR;
  }
  
  /*
   *	ITextFont::GetEmboss(long * pValue)
   *
   *	@mfunc
   *		Property get method that gets the embossed state.
   *
   *	@rdesc
   *		HRESULT = (!pValue) ? E_INVALIDARG : NOERROR
   */
  STDMETHODIMP CTxtFont::GetEmboss(
  	long *pValue)		//@parm Out parm to receive tomBool
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::GetEmboss");
  
  	return EffectGetter(pValue, CFM_EMBOSS);
  }
  
  /*
   *	ITextFont::GetForeColor(long * pValue)
   *
   *	@mfunc
   *		Property get method that gets the foreground color.
   *
   *	@rdesc
   *		HRESULT = (!pValue) ? E_INVALIDARG : NOERROR
   */
  STDMETHODIMP CTxtFont::GetForeColor(
  	long *pValue)		//@parm Out parm to receive COLORREF value
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::GetForeColor");
  
  	HRESULT hr = EffectGetter(pValue, CFE_AUTOCOLOR);
  
  	if (hr != NOERROR || *pValue == tomUndefined)
  	{
  		return hr;
  	}
  
  	*pValue = (*pValue == tomFalse) ? _CF.crTextColor : tomAutoColor;
  	return NOERROR;
  }
  
  /*
   *	ITextFont::GetHidden(long * pValue)
   *
   *	@mfunc
   *		Property get method that gets the hidden state.
   *
   *	@rdesc
   *		HRESULT = (!pValue) ? E_INVALIDARG : NOERROR
   */
  STDMETHODIMP CTxtFont::GetHidden(
  	long *pValue)		//@parm Out parm to receive tomBool
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::GetHidden");
  
  	return EffectGetter(pValue, CFM_HIDDEN);
  }
  
  /*
   *	ITextFont::GetImprint(long * pValue)
   *
   *	@mfunc
   *		Property get method that gets the imprint state.
   *
   *	@rdesc
   *		HRESULT = (!pValue) ? E_INVALIDARG : NOERROR
   */
  STDMETHODIMP CTxtFont::GetImprint(
  	long *pValue)		//@parm Out parm to receive tomBool
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::GetImprint");
  
  	return EffectGetter(pValue, CFM_IMPRINT);
  }
  
  /*
   *	ITextFont::GetItalic(long * pValue)
   *
   *	@mfunc
   *		Property get method that gets the italic state.
   *
   *	@rdesc
   *		HRESULT = (!pValue) ? E_INVALIDARG : NOERROR
   */
  STDMETHODIMP CTxtFont::GetItalic(
  	long *pValue)		//@parm Out parm to receive tomBool
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::GetItalic");
  
  	return EffectGetter(pValue, CFM_ITALIC);
  }
  
  /*
   *	ITextFont::GetKerning(float * pValue)
   *
   *	@mfunc
   *		Property get method that gets the minimum kerning size,
   *		which is given in floating-point points.
   *
   *	@rdesc
   *		HRESULT = (!pValue) ? E_INVALIDARG : NOERROR
   *
   *	@comm
   *		A kerning size of 0 turns off kerning, but an arbitrarily small
   *		value turns it on, e.g., 1.0, which is too small to see, let alone
   *		kern!
   */
  STDMETHODIMP CTxtFont::GetKerning(
  	float *pValue)		//@parm Out parm to receive minimum kerning size
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::GetKerning");
  
  	return GetParameter((long *)&_CF.wKerning, CFM_KERNING, -2, (long *)pValue);
  }
  
  /*
   *	ITextFont::GetLanguageID(long * pValue)
   *
   *	@mfunc
   *		Property get method that gets the language ID (more
   *		generally LCID).
   *
   *	@rdesc
   *		HRESULT = (!pValue) ? E_INVALIDARG : NOERROR
   */
  STDMETHODIMP CTxtFont::GetLanguageID(
  	long *pValue)		//@parm Out parm to receive LCID value
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::GetLanguageID");
  
  	return GetParameter((long *)&_CF.lcid, CFM_LCID, 4, pValue);
  }
  
  /*
   *	ITextFont::GetName(BSTR *pbstr)
   *
   *	@mfunc
   *		Property get method that gets the font name.
   *
   *	@rdesc
   *		HRESULT = (!pValue) ? E_INVALIDARG :
   *				  (can allocate bstr) ? NOERROR : E_OUTOFMEMORY
   */
  STDMETHODIMP CTxtFont::GetName(
  	BSTR *pbstr)	//@parm Out parm to receive font name bstr
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::GetName");
  
  	if (!pbstr)
  	{
  		return E_INVALIDARG;
  	}
  
  	*pbstr = NULL;
  
  	HRESULT hr = UpdateFormat();			// If live Font object, update
  	//  _CF to current _prg values
  	if (hr != NOERROR)						// Attached to zombied range
  	{
  		return hr;
  	}
  
  #ifdef MACPORT
  	CStrIn str(_CF.szFaceName);
  	*pbstr = SysAllocString(str);
  #else
  	*pbstr = SysAllocString(_CF.szFaceName);
  #endif
  
  	return *pbstr ? NOERROR : E_OUTOFMEMORY;
  }
  
  /*
   *	ITextFont::GetOutline(long * pValue)
   *
   *	@mfunc
   *		Property get method that gets the outline state.
   *
   *	@rdesc
   *		HRESULT = (!pValue) ? E_INVALIDARG : NOERROR
   */
  STDMETHODIMP CTxtFont::GetOutline(
  	long *pValue)		//@parm Out parm to receive tomBool
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::GetOutline");
  
  	return EffectGetter(pValue, CFM_OUTLINE);
  }
  
  /*
   *	ITextFont::GetPosition(float *pValue)
   *
   *	@mfunc
   *		Property get method that gets the character position
   *		relative to the baseline. The value is given in floating-point
   *		points.
   *
   *	@rdesc
   *		HRESULT =  (!pValue) ? E_INVALIDARG : NOERROR
   */
  STDMETHODIMP CTxtFont::GetPosition(
  	float *pValue)		//@parm Out parm to receive relative vertical position
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::GetPosition");
  
  	return GetParameter(&_CF.yOffset, CFM_OFFSET, -4, (long *)pValue);
  }
  
  /*
   *	ITextFont::GetProtected(long * pValue)
   *
   *	@mfunc
   *		Property get method that gets the protected state.
   *
   *	@rdesc
   *		HRESULT = (!pValue) ? E_INVALIDARG : NOERROR
   */
  STDMETHODIMP CTxtFont::GetProtected(
  	long *pValue)		//@parm Out parm to receive tomBool
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::GetProtected");
  
  	return EffectGetter(pValue, CFM_PROTECTED);
  }
  
  /*
   *	ITextFont::GetShadow(long * pValue)
   *
   *	@mfunc
   *		Property get method that gets the shadow state.
   *
   *	@rdesc
   *		HRESULT = (!pValue) ? E_INVALIDARG : NOERROR
   */
  STDMETHODIMP CTxtFont::GetShadow(
  	long *pValue)		//@parm Out parm to receive tomBool
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::GetShadow");
  
  	return EffectGetter(pValue, CFM_SHADOW);
  }
  
  /*
   *	ITextFont::GetSize(float * pValue)
   *
   *	@mfunc
   *		Property get method that gets the font size, which is given
   *		in floating-point points.
   *
   *	@rdesc
   *		HRESULT = (!pValue) ? E_INVALIDARG : NOERROR
   */
  STDMETHODIMP CTxtFont::GetSize(
  	float *pValue)		//@parm Out parm to receive font size
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::GetSize");
  
  	return GetParameter(&_CF.yHeight, CFM_SIZE, -4, (long *)pValue);
  }
  
  /*
   *	ITextFont::GetSmallCaps(long * pValue)
   *
   *	@mfunc
   *		Property get method that gets the SmallCaps state.
   *
   *	@rdesc
   *		HRESULT = (!pValue) ? E_INVALIDARG : NOERROR
   */
  STDMETHODIMP CTxtFont::GetSmallCaps(
  	long *pValue)		//@parm Out parm to receive tomBool
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::GetSmallCaps");
  
  	return EffectGetter(pValue, CFM_SMALLCAPS);
  }
  
  /*
   *	ITextFont::GetSpacing(float * pValue)
   *
   *	@mfunc
   *		Property get method that gets the intercharacter spacing,
   *		which is given in floating-point points.
   *
   *	@rdesc
   *		HRESULT = (!pValue) ? E_INVALIDARG : NOERROR
   */
  STDMETHODIMP CTxtFont::GetSpacing(
  	float *pValue)		//@parm Out parm to receive intercharacter spacing
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::GetSpacing");
  
  	return GetParameter((long *)&_CF.sSpacing, CFM_SPACING, -2, (long *)pValue);
  }
  
  /*
   *	ITextFont::GetStrikeThrough(long * pValue)
   *
   *	@mfunc
   *		Property get method that gets the strikeout state.
   *
   *	@rdesc
   *		HRESULT = (!pValue) ? E_INVALIDARG : NOERROR
   */
  STDMETHODIMP CTxtFont::GetStrikeThrough(
  	long *pValue)		//@parm Out parm to receive tomBool
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::GetStrikeThrough");
  
  	return EffectGetter(pValue, CFM_STRIKEOUT);
  }
  
  /*
   *	ITextFont::GetStyle(long * pValue)
   *
   *	@mfunc
   *		Property get method that gets the character style handle for
   *		the characters in a range.
   *
   *	@rdesc
   *		HRESULT = (!pValue) ? E_INVALIDARG : NOERROR;
   */
  STDMETHODIMP CTxtFont::GetStyle(
  	long *pValue)		//@parm Out parm to receive character style handle
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::GetStyle");
  
  	return GetParameter((long *)&_CF.sStyle, CFM_STYLE, 2, pValue);
  }
  
  /*
   *	ITextFont::GetSubscript(long * pValue)
   *
   *	@mfunc
   *		Property get method that gets the subscript state.
   *
   *	@rdesc
   *		HRESULT = (!pValue) ? E_INVALIDARG : NOERROR
   */
  STDMETHODIMP CTxtFont::GetSubscript(
  	long *pValue)		//@parm Out parm to receive tomBool
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::GetSubscript");
  
  	return EffectGetter(pValue, CFE_SUBSCRIPT);
  }
  
  /*
   *	ITextFont::GetSuperscript(long * pValue)
   *
   *	@mfunc
   *		Property get method that gets the superscript state.
   *
   *	@rdesc
   *		HRESULT = (!pValue) ? E_INVALIDARG : NOERROR
   */
  STDMETHODIMP CTxtFont::GetSuperscript(
  	long *pValue)		//@parm Out parm to receive tomBool
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::GetSuperscript");
  
  	return EffectGetter(pValue, CFE_SUPERSCRIPT);
  }
  
  /*
   *	ITextFont::GetUnderline(long * pValue)
   *
   *	@mfunc
   *		Property get method that gets the underline style.
   *
   *	@rdesc
   *		HRESULT = (!pValue) ? E_INVALIDARG : NOERROR
   */
  STDMETHODIMP CTxtFont::GetUnderline(
  	long *pValue)		//@parm Out parm to receive underline style
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::GetUnderline");
  
  	if (!pValue)
  	{
  		return E_INVALIDARG;
  	}
  
  	HRESULT hr = UpdateFormat();			// If live Font object, update
  	//  _CF to current _prg values
  	*pValue = 0;							// Default no underline
  
  	if (!(_CF.dwMask	& CFM_UNDERLINE))		// It's a NINCH
  	{
  		*pValue = tomUndefined;
  	}
  
  	else if (_CF.dwEffects & CFM_UNDERLINE)
  	{
  		*pValue = (LONG)_CF.bUnderlineType ? (LONG)_CF.bUnderlineType : tomTrue;
  	}
  
  	return hr;
  }
  
  /*
   *	ITextFont::GetWeight(long * pValue)
   *
   *	@mfunc
   *		Property get method that gets the font weight for
   *		the characters in a range.
   *
   *	@rdesc
   *		HRESULT = (!pValue) ? E_INVALIDARG : NOERROR;
   */
  STDMETHODIMP CTxtFont::GetWeight(
  	long *pValue)		//@parm Out parm to receive character style handle
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::GetWeight");
  
  	return GetParameter((long *)&_CF.wWeight, CFM_WEIGHT, 2, pValue);
  }
  
  /*
   *	ITextFont::IsEqual(ITextFont * pFont, long * pB)
   *
   *	@mfunc
   *		Method that sets *<p pB> = tomTrue if this text font has the
   *		same properties as *<p pFont>.  For this to be true, *<p pFont> has to
   *		belong to the same TOM engine as the present one. The IsEqual()
   *		method should ignore entries for which either font object has a
   *		tomUndefined value.
   *
   *	@rdesc
   *		HRESULT = (equal objects) ? NOERROR : S_FALSE
   *
   *	@devnote
   *		This implementation assumes that all properties are defined and that
   *		pFont belongs to RichEdit.  It would be nice to generalize this so
   *		that undefined properties are ignored in the comparison and so that
   *		pFont could belong to a different TOM engine.  This would help in
   *		using RichEdit Find dialogs to search for rich text in Word using
   *		TOM.
   */
  STDMETHODIMP CTxtFont::IsEqual(
  	ITextFont 	*pFont,		//@parm ITextFont to compare to
  	long 		*pB)			//@parm Out parm to receive comparison result
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::IsEqual");
  
  	UpdateFormat();							// Update _CFs in case they are
  	((CTxtFont *)pFont)->UpdateFormat();	//  attached to ranges
  
  	// Ignore differences in CharSet, since TOM thinks all CharSets are Unicode!
  	LONG CharSet = _CF.bCharSet;
  	_CF.bCharSet = ((CTxtFont *)pFont)->_CF.bCharSet;
  
  	HRESULT hr = IsTrue(SameVtables(this, pFont) &&
  			_CF.Compare(&((CTxtFont *)pFont)->_CF), pB);
  
  	_CF.bCharSet = CharSet;
  	return hr;
  }
  
  /*
   *	ITextFont::Reset(long Value)
   *
   *	@mfunc
   *		Method that resets the character formatting to the default
   *		values to 1) those defined by the RTF \plain control word (Value =
   *		tomDefault), and 2) all undefined values (Value = tomUndefined).
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR :
   *				  (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
   */
  STDMETHODIMP CTxtFont::Reset(
  	long Value)		//@parm Kind of reset (tomDefault or tomUndefined)
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::Reset");
  
  	HRESULT hr = CanChange(NULL);
  
  	if (hr != NOERROR)								// Takes care of zombie
  	{
  		return hr;    //  and protection
  	}
  
  	if (Value == tomDefault)
  	{
  		if (_prg)
  		{
  			CopyFormat(&_CF, _prg->GetPed()->GetCharFormat(-1), sizeof(CCharFormat));
  			FormatSetter(CFM_ALL2);
  		}
  		else
  		{
  			_CF.InitDefault(0);
  		}
  	}
  
  	else if (Value == tomUndefined && !_prg)			// Only applicable
  	{
  		_CF.dwMask = 0;    //  for clones
  	}
  
  	else
  	{
  		return E_INVALIDARG;
  	}
  
  	return NOERROR;
  }
  
  /*
   *	ITextFont::SetAllCaps(long Value)
   *
   *	@mfunc
   *		Property put method that sets the AllCaps state according to
   *		the value given by Value.
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR :
   *				  (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
   */
  STDMETHODIMP CTxtFont::SetAllCaps(
  	long Value)		//@parm New value. Default value: tomToggle
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::SetAllCaps");
  
  	return EffectSetter(Value, CFM_ALLCAPS | CFM_SMALLCAPS, CFE_ALLCAPS);
  }
  
  /*
   *	ITextFont::SetAnimation(long Value)
   *
   *	@mfunc
   *		Property put method that sets the animation type
   *
   *	@rdesc
   *		HRESULT = (Value defined) ? NOERROR : E_INVALIDARG
   */
  STDMETHODIMP CTxtFont::SetAnimation(
  	long Value)		//@parm New animation type
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::SetAnimation");
  
  	if (Value == tomUndefined)
  	{
  		return NOERROR;
  	}
  
  	if ((unsigned)Value > /*tomBlinkingColors*/18)
  	{
  		return E_INVALIDARG;
  	}
  
  	return SetParameter((long *)&_CF.bAnimation, CFM_ANIMATION, 1, Value);
  }
  
  /*
   *	ITextFont::SetBackColor(long Value)
   *
   *	@mfunc
   *		Property put method that sets the background color according
   *		to the value given by Value.
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR :
   *				  (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
   *	@devnote
   *		Legal values are tomUndefined, tomAutoColor (both negative) and
   *		in principle any positive values.  Currently wingdi.h only defines
   *		high bytes = 0, 1, 2, 4.  But more values might happen, so we only
   *		rule out negative values other than tomUndefined and tomAutoColor.
   */
  STDMETHODIMP CTxtFont::SetBackColor(
  	long Value)		//@parm New COLORREF value to use
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::SetBackColor");
  
  	if (Value == tomUndefined)					// NINCH
  	{
  		return NOERROR;
  	}
  
  	_CF.dwEffects |= CFE_AUTOBACKCOLOR;			// Default AutoBackColor
  	if (Value != tomAutoColor)
  	{
  		if (Value < 0)
  		{
  			return E_INVALIDARG;
  		}
  		_CF.dwEffects &= ~CFE_AUTOBACKCOLOR;	// Turn off AutoBackColor
  		_CF.crBackColor = (COLORREF)Value;		// Use new BackColor
  	}
  
  	return FormatSetter(CFM_BACKCOLOR);
  }
  
  /*
   *	ITextFont::SetBold(long Value)
   *
   *	@mfunc
   *		Property put method that sets the bold state according to
   *		the value given by Value.
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR :
   *				  (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
   */
  STDMETHODIMP CTxtFont::SetBold(
  	long Value)		//@parm New value. Default value: tomToggle
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::SetBold");
  
  	return EffectSetter(Value, CFM_BOLD, CFE_BOLD);
  }
  
  /*
   *	ITextFont::SetDuplicate(ITextFont *pFont)
   *
   *	@mfunc
   *		Property put method that sets this text font character
   *		formatting to that given by pFont.
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR :
   *				  (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
   */
  STDMETHODIMP CTxtFont::SetDuplicate(
  	ITextFont *pFont) 		//@parm Font object to apply to this font object
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::SetDuplicate");
  
  	DWORD		dwMask = 0;
  	BSTR		bstr;
  	CTxtRange 	*prg;
  	long		Value;
  	float		x;
  
  	if (IsZombie())							// Check for range zombie
  	{
  		return CO_E_RELEASED;
  	}
  
  	if (*(LONG *)this == *(LONG *)pFont)	// If same TOM engine, cast and
  	{
  		//  copy
  		((CTxtFont *)pFont)->UpdateFormat();
  		_CF = ((CTxtFont *)pFont)->_CF;
  		dwMask = _CF.dwMask;				// Use this mask in case this
  	}										//  font is active
  	else
  	{
  		// Need to call pFont for all font
  		prg = _prg;							//  properties
  		_prg = NULL;						// Be sure it's a clone during
  		//  transfer
  		pFont->GetStyle(&Value);
  		SetStyle(Value);
  
  		pFont->GetAllCaps(&Value);
  		SetAllCaps(Value);
  
  		pFont->GetAnimation(&Value);
  		SetAnimation(Value);
  
  		pFont->GetBackColor(&Value);
  		SetBackColor(Value);
  
  		pFont->GetBold(&Value);
  		SetBold(Value);
  
  		pFont->GetEmboss(&Value);
  		SetEmboss(Value);
  
  		pFont->GetForeColor(&Value);
  		SetForeColor(Value);
  
  		pFont->GetHidden(&Value);
  		SetHidden(Value);
  
  		pFont->GetImprint(&Value);
  		SetImprint(Value);
  
  		pFont->GetItalic(&Value);
  		SetItalic(Value);
  
  		pFont->GetKerning(&x);
  		SetKerning(x);
  
  		pFont->GetLanguageID(&Value);
  		SetLanguageID(Value);
  
  		pFont->GetName(&bstr);
  		SetName(bstr);
  		SysFreeString(bstr);
  
  		pFont->GetOutline(&Value);
  		SetOutline(Value);
  
  		pFont->GetPosition(&x);
  		SetPosition(x);
  
  		pFont->GetProtected(&Value);
  		SetProtected(Value);
  
  		pFont->GetShadow(&Value);
  		SetShadow(Value);
  
  		pFont->GetSize(&x);
  		SetSize(x);
  
  		pFont->GetSmallCaps(&Value);
  		SetSmallCaps(Value);
  
  		pFont->GetSpacing(&x);
  		SetSpacing(x);
  
  		pFont->GetStrikeThrough(&Value);
  		SetStrikeThrough(Value);
  
  		pFont->GetSubscript(&Value);
  		SetSubscript(Value);
  
  		pFont->GetSuperscript(&Value);
  		SetSuperscript(Value);
  
  		pFont->GetUnderline(&Value);
  		SetUnderline(Value);
  
  		_prg = prg;							// Restore original value
  	}
  	return FormatSetter(dwMask);			// Apply it unless !_prg
  }
  
  /*
   *	ITextFont::SetEmboss(long Value)
   *
   *	@mfunc
   *		Property put method that sets the embossed state according
   *		to the value given by Value
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR :
   *				  (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
   */
  STDMETHODIMP CTxtFont::SetEmboss(
  	long Value)		//@parm New value. Default value: tomToggle
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::SetEmboss");
  
  	return EffectSetter(Value, CFM_EMBOSS, CFE_EMBOSS);
  }
  
  /*
   *	ITextFont::SetForeColor(long Value)
   *
   *	@mfunc
   *		Property put method that sets the foreground color according
   *		to the value given by Value.
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR :
   *				  (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
   */
  STDMETHODIMP CTxtFont::SetForeColor(
  	long Value)		//@parm New COLORREF value to use
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::SetForeColor");
  
  	if (Value == tomUndefined)					// NINCH
  	{
  		return NOERROR;
  	}
  
  	_CF.dwEffects |= CFE_AUTOCOLOR;				// Default AutoColor
  	if (Value != tomAutoColor)
  	{
  		if (Value < 0)
  		{
  			return E_INVALIDARG;
  		}
  		_CF.dwEffects &= ~CFE_AUTOCOLOR;		// Turn off AutoColor
  		_CF.crTextColor = (COLORREF)Value;		// Use new TextColor
  	}
  
  	return FormatSetter(CFM_COLOR);
  }
  
  /*
   *	ITextFont::SetHidden(long Value)
   *
   *	@mfunc
   *		Property put method that sets the hidden state according to
   *		the value given by Value.
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR :
   *				  (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
   */
  STDMETHODIMP CTxtFont::SetHidden(
  	long Value)		//@parm New value. Default value: tomToggle
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::SetHidden");
  
  	return EffectSetter(Value, CFM_HIDDEN, CFE_HIDDEN);
  }
  
  /*
   *	ITextFont::SetImprint(long Value)
   *
   *	@mfunc
   *		Property put method that sets the imprint state according to
   *		the value given by Value.
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR :
   *				  (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
   */
  STDMETHODIMP CTxtFont::SetImprint(
  	long Value)		//@parm New value. Default value: tomToggle
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::SetImprint");
  
  	return EffectSetter(Value, CFM_IMPRINT, CFE_IMPRINT);
  }
  
  /*
   *	ITextFont::SetItalic(long Value)
   *
   *	@mfunc
   *		Property put method that sets the italic state according to
   *		the value given by Value.
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR :
   *				  (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
   */
  STDMETHODIMP CTxtFont::SetItalic(
  	long Value)		//@parm New value. Default value: tomToggle
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::SetItalic");
  
  	return EffectSetter(Value, CFM_ITALIC, CFE_ITALIC);
  }
  
  /*
   *	ITextFont::SetKerning(float Value)
   *
   *	@mfunc
   *		Property set method that sets the minimum kerning size,
   *		which is given in floating-point points.
   *
   *	@rdesc
   *		HRESULT = (Value < 0) ? E_INVALIDARG :
   *				  (if success) ? NOERROR :
   *				  (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
   */
  STDMETHODIMP CTxtFont::SetKerning(
  	float Value)		//@parm New value of minimum kerning size
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::SetKerning");
  
  	return SetParameter((long *)&_CF.wKerning, CFM_KERNING, -2, *(long *)&Value);
  }
  
  /*
   *	ITextFont::SetLanguageID(long Value)
   *
   *	@mfunc
   *		Property put method that sets the language ID (more
   *		generally LCID) according to the value given by Value.  See
   *		GetLanguageID() for more information.
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR :
   *				  (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
   */
  STDMETHODIMP CTxtFont::SetLanguageID(
  	long Value)		//@parm New LCID to use
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::SetLanguageID");
  
  	return SetParameter((long *)&_CF.lcid, CFM_LCID, 4, Value);
  }
  
  /*
   *	ITextFont::SetName(BSTR Name)
   *
   *	@mfunc
   *		Property put method that sets the font name to Name.
   *
   *	@rdesc
   *		HRESULT = (Name too long) ? E_INVALIDARG :
   *				  (if success) ? NOERROR :
   *				  (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
   */
  STDMETHODIMP CTxtFont::SetName(
  	BSTR Name)		//@parm New font name
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::SetName");
  
  	LONG cch = SysStringLen(Name);
  
  	if (cch > LF_FACESIZE)
  	{
  		return E_INVALIDARG;
  	}
  
  	if (!cch)									// NINCH
  	{
  		return NOERROR;
  	}
  
  #ifdef MACPORT
  	CStrInW  strinw(Name);
  	wcscpy(_CF.szFaceName, strinw);
  #else
  	wcscpy(_CF.szFaceName, Name);
  #endif
  
  	_CF.bCharSet = DEFAULT_CHARSET;
  
  	return FormatSetter(CFM_FACE + CFM_CHARSET);
  }
  
  /*
   *	ITextFont::SetOutline(long Value)
   *
   *	@mfunc
   *		Property put method that sets the outline state according to
   *		the value given by Value.
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR :
   *				  (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
   */
  STDMETHODIMP CTxtFont::SetOutline(
  	long Value)		//@parm New value. Default value: tomToggle
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::SetOutline");
  
  	return EffectSetter(Value, CFM_OUTLINE, CFE_OUTLINE);
  }
  
  /*
   *	ITextFont::SetPosition(float Value)
   *
   *	@mfunc
   *		Property set method that sets the character position
   *		relative to the baseline. The value is given in floating-point
   *		points.
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR :
   *				  (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
   */
  STDMETHODIMP CTxtFont::SetPosition(
  	float Value)		//@parm New value of relative vertical position
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::SetPosition");
  
  	return SetParameter(&_CF.yOffset, CFM_OFFSET, -4, *(long *)&Value);
  }
  
  /*
   *	ITextFont::SetProtected(long Value)
   *
   *	@mfunc
   *		Property put method that sets the protected state according
   *		to the value given by Value.
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR : S_FALSE
   */
  STDMETHODIMP CTxtFont::SetProtected(
  	long Value)		//@parm New value. Default value: tomToggle
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::SetProtected");
  
  	return EffectSetter(Value, CFM_PROTECTED, CFE_PROTECTED);
  }
  
  /*
   *	ITextFont::SetShadow(long Value)
   *
   *	@mfunc
   *		Property put method that sets the shadow state according to
   *		the value given by Value.
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR :
   *				  (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
   */
  STDMETHODIMP CTxtFont::SetShadow(
  	long Value)		//@parm New value. Default value: tomToggle
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::SetShadow");
  
  	return EffectSetter(Value, CFM_SHADOW, CFE_SHADOW);
  }
  
  /*
   *	ITextFont::SetSize(float Value)
   *
   *	@mfunc
   *		Property put method that sets the font size = Value (in
   *		floating-point points).
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR :
   *				  (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
   */
  STDMETHODIMP CTxtFont::SetSize(
  	float Value)		//@parm New font size to use
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::SetSize");
  
  	return SetParameter(&_CF.yHeight, CFM_SIZE, -4, *(long *)&Value);
  }
  
  /*
   *	ITextFont::SetSmallCaps(long Value)
   *
   *	@mfunc
   *		Property put method that sets the SmallCaps state according
   *		to the value given by Value.
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR :
   *				  (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
   */
  STDMETHODIMP CTxtFont::SetSmallCaps(
  	long Value)		//@parm New value. Default value: tomToggle
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::SetSmallCaps");
  
  	return EffectSetter(Value, CFM_ALLCAPS | CFM_SMALLCAPS, CFE_SMALLCAPS);
  }
  
  /*
   *	ITextFont::SetSpacing(float Value)
   *
   *	@mfunc
   *		Property set method that sets the intercharacter spacing,
   *		which is given in floating-point points.
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR :
   *				  (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
   */
  STDMETHODIMP CTxtFont::SetSpacing(
  	float Value)		//@parm New value of intercharacter spacing
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::SetSpacing");
  
  	return SetParameter((long *)&_CF.sSpacing, CFM_SPACING, -2, *(long *)&Value);
  }
  
  /*
   *	ITextFont::SetStrikeThrough(long Value)
   *
   *	@mfunc
   *		Property put method that sets the StrikeThrough state
   *		according to the value given by Value.
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR :
   *				  (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
   */
  STDMETHODIMP CTxtFont::SetStrikeThrough(
  	long Value)		//@parm New value. Default value: tomToggle
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::SetStrikeThrough");
  
  	return EffectSetter(Value, CFM_STRIKEOUT, CFE_STRIKEOUT);
  }
  
  /*
   *	ITextFont::SetStyle(long Value)
   *
   *	@mfunc
   *		Property put method that sets the character style handle for
   *		the characters in a range.  See GetStyle() for further discussion.
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR :
   *				  (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
   */
  STDMETHODIMP CTxtFont::SetStyle(
  	long Value)		//@parm New character style handle
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::SetStyle");
  
  	if (Value == tomUndefined)
  	{
  		return NOERROR;
  	}
  
  	if (Value < -32768 || Value > 32767)
  	{
  		return E_INVALIDARG;
  	}
  
  	return SetParameter((long *)&_CF.sStyle, CFM_STYLE, 2, Value);
  }
  
  /*
   *	ITextFont::SetSubscript(long Value)
   *
   *	@mfunc
   *		Property put method that sets the subscript state according
   *		to the value given by Value.
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR :
   *				  (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
   */
  STDMETHODIMP CTxtFont::SetSubscript(
  	long Value)		//@parm New value. Default value: tomToggle
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::SetSubscript");
  
  	return EffectSetter(Value, CFM_SUBSCRIPT | CFM_SUPERSCRIPT, CFE_SUBSCRIPT);
  }
  
  /*
   *	ITextFont::SetSuperscript(long Value)
   *
   *	@mfunc
   *		Property put method that sets the superscript state
   *		according to the value given by Value
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR :
   *				  (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
   */
  STDMETHODIMP CTxtFont::SetSuperscript(
  	long Value)		//@parm New value. Default value: tomToggle
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::SetSuperscript");
  
  	return EffectSetter(Value, CFM_SUBSCRIPT | CFM_SUPERSCRIPT, CFE_SUPERSCRIPT);
  }
  
  /*
   *	ITextFont::SetUnderline(long Value)
   *
   *	@mfunc
   *		Property put method that sets the underline style according
   *		to the value given by Value.
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR :
   *				  (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
   */
  STDMETHODIMP CTxtFont::SetUnderline(
  	long Value)		//@parm New value of underline type
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::SetUnderline");
  
  	_CF.bUnderlineType = 0;						// Default no underline type
  	if (Value < 0)								// tomTrue, tomUndefined, or
  	{
  		return EffectSetter(Value, CFM_UNDERLINETYPE | CFM_UNDERLINE, CFE_UNDERLINE);
  	}
  
  	if (Value > 15)								// Illegal underline type
  	{
  		return E_INVALIDARG;
  	}
  
  	_CF.bUnderlineType = (BYTE)Value;
  	_CF.dwEffects &= ~CFM_UNDERLINE;			// Default underlining is off
  	if (Value)
  	{
  		_CF.dwEffects |= CFM_UNDERLINE;    // It's on
  	}
  
  	return FormatSetter(CFM_UNDERLINETYPE + CFM_UNDERLINE);
  }
  
  /*
   *	ITextFont::SetWeight(long Value)
   *
   *	@mfunc
   *		Property put method that sets the font weight for
   *		the characters in a range.
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR :
   *				  (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
   */
  STDMETHODIMP CTxtFont::SetWeight(
  	long Value)		//@parm New character style handle
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFont::SetWeight");
  
  	if (Value == tomUndefined)					// NINCH
  	{
  		return NOERROR;
  	}
  
  	if ((unsigned)Value > 900)					// Valid values satisfy:
  	{
  		return E_INVALIDARG;    //  0 <= Value <= 900
  	}
  
  	return SetParameter((long *)&_CF.wWeight, CFM_WEIGHT, 2, Value);
  }
  
  
  //------------------------------- CTxtPara ------------------------------------
  
  /*
   *	CTxtPara::CTxtPara(prg)
   *
   *	@mfunc
   *		Constructor
   */
  CTxtPara::CTxtPara(CTxtRange *prg) : CTxtFormat(prg)
  {
  	_PF.dwMask = 0;				// Private CCharFormat is initially undefined
  }
  
  
  //------------------------- CTxtPara IUnknown Methods -------------------------------------
  
  /*	CTxtPara::IUnknown methods
   *
   *		See tomdoc.cpp for comments
   */
  STDMETHODIMP CTxtPara::QueryInterface(REFIID riid, void **ppv)
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::QueryInterface");
  
  	return ::QueryInterface(riid, IID_ITextPara, this, ppv, IsZombie());
  }
  
  STDMETHODIMP_(ULONG) CTxtPara::AddRef()
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::AddRef");
  
  	return ++_cRefs;
  }
  
  STDMETHODIMP_(ULONG) CTxtPara::Release()
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::Release");
  
  	_cRefs--;
  
  	if (!_cRefs)
  	{
  		delete this;
  		return 0;
  	}
  	return _cRefs;
  }
  
  
  //------------------------- CTxtPara IDispatch Methods -------------------------------------
  
  /*
   *	CTxtPara::GetTypeInfoCount(pcTypeInfo)
   *
   *	@mfunc
   *		Get the number of TYPEINFO elements (1)
   *
   *	@rdesc
   *		HRESULT = (pcTypeInfo) ? NOERROR : E_INVALIDARG;
   */
  STDMETHODIMP CTxtPara::GetTypeInfoCount(
  	UINT *pcTypeInfo)			//@parm Out parm to receive type-info count
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::GetTypeInfoCount");
  
  	if (!pcTypeInfo)
  	{
  		return E_INVALIDARG;
  	}
  
  	*pcTypeInfo = 1;
  	return NOERROR;
  }
  
  /*
   *	CTxtPara::GetTypeInfo(iTypeInfo, lcid, ppTypeInfo)
   *
   *	@mfunc
   *		Return ptr to type information object for ITextPara interface
   *
   *	@rdesc
   *		HRESULT
   */
  STDMETHODIMP CTxtPara::GetTypeInfo(
  	UINT		iTypeInfo,		//@parm Index of type info to return
  	LCID		lcid,			//@parm Local ID of type info
  	ITypeInfo **ppTypeInfo)		//@parm Out parm to receive type info
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::GetTypeInfo");
  
  	return ::GetTypeInfo(iTypeInfo, g_pTypeInfoPara, ppTypeInfo);
  }
  
  /*
   *	CTxtPara::GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid)
   *
   *	@mfunc
   *		Get DISPIDs for ITextPara methods and properties
   *
   *	@rdesc
   *		HRESULT
   */
  STDMETHODIMP CTxtPara::GetIDsOfNames(
  	REFIID		riid,			//@parm Interface ID to interpret names for
  	OLECHAR 	**rgszNames,		//@parm Array of names to be mapped
  	UINT		cNames,			//@parm Count of names to be mapped
  	LCID		lcid,			//@parm Local ID to use for interpretation
  	DISPID 	*rgdispid)		//@parm Out parm to receive name mappings
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::GetIDsOfNames");
  
  	HRESULT hr = GetTypeInfoPtrs();				// Ensure TypeInfo ptrs are OK
  	if (hr != NOERROR)
  	{
  		return hr;
  	}
  
  	return g_pTypeInfoPara->GetIDsOfNames(rgszNames, cNames, rgdispid);
  }
  
  /*
   *	CTxtPara::Invoke(dispidMember, riid, lcid, wFlags, pdispparams,
   *					  pvarResult, pexcepinfo, puArgError)
   *	@mfunc
   *		Invoke methods for the ITextPara interface
   *
   *	@rdesc
   *		HRESULT
   */
  STDMETHODIMP CTxtPara::Invoke(
  	DISPID		dispidMember,	//@parm Identifies member function
  	REFIID		riid,			//@parm Pointer to interface ID
  	LCID		lcid,			//@parm Locale ID for interpretation
  	USHORT		wFlags,			//@parm Flags describing context of call
  	DISPPARAMS *pdispparams,	//@parm Ptr to method arguments
  	VARIANT 	*pvarResult,		//@parm Out parm for result (if not NULL)
  	EXCEPINFO *pexcepinfo,		//@parm Out parm for exception info
  	UINT 		*puArgError)		//@parm Out parm for error
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::Invoke");
  
  	HRESULT hr = GetTypeInfoPtrs();				// Ensure TypeInfo ptrs are OK
  	if (hr != NOERROR)
  	{
  		return hr;
  	}
  
  	if (IsZombie())
  	{
  		return CO_E_RELEASED;
  	}
  
  	return g_pTypeInfoPara->Invoke(this, dispidMember, wFlags,
  			pdispparams, pvarResult, pexcepinfo, puArgError);
  }
  
  //------------------------ CTxtPara ITextPara Methods -------------------------------------
  
  /*
   *	ITextPara::AddTab(float tbPos, long tbAlign, long tbLeader)
   *
   *	@mfunc
   *		Method that adds a tab at the displacement tbPos, with type
   *		tbAlign, and leader style tbLeader.  The displacement is given in
   *		floating-point points.
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR :
   *				  (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
   */
  STDMETHODIMP CTxtPara::AddTab(
  	float	tbPos,			//@parm New tab displacement
  	long	tbAlign,		//@parm New tab type
  	long	tbLeader)		//@parm New tab style
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::AddTab");
  
  	HRESULT hr = UpdateFormat();			// If live Para object, update
  	//  _PF to current _prg values
  	if (hr != NOERROR)
  	{
  		return hr;    // Must be a zombie
  	}
  
  	hr = _PF.AddTab(FPPTS_TO_TWIPS(tbPos), tbAlign, tbLeader);
  	return hr == NOERROR ? FormatSetter(PFM_TABSTOPS) : hr;
  }
  
  /*
   *	ITextPara::CanChange(long * pbCanChange)
   *
   *	@mfunc
   *		Method that sets *pbCanChange = tomTrue if and only if the
   *		paragraph formatting can be changed.
   *
   *	@rdesc
   *		HRESULT = (can change char format) ? NOERROR : S_FALSE
   */
  STDMETHODIMP CTxtPara::CanChange(
  	long *pbCanChange)		//@parm Out parm to receive boolean value
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::CanChange");
  
  	return CTxtFormat::CanChange(pbCanChange);
  }
  
  /*
   *	ITextPara::ClearAllTabs()
   *
   *	@mfunc
   *		Method that clears all tabs, reverting to equally spaced
   *		tabs with the default tab spacing.
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR :
   *				  (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
   */
  STDMETHODIMP CTxtPara::ClearAllTabs()
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::ClearAllTabs");
  
  	_PF.cTabCount = 0;						// Signal to use default tab
  	return FormatSetter(PFM_TABSTOPS);
  }
  
  /*
   *	ITextPara::DeleteTab(float tbPos)
   *
   *	@mfunc
   *		Delete any tab at the displacement tbPos.  This displacement is
   *		given in floating-point points.
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR :
   *				  (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
   */
  STDMETHODIMP CTxtPara::DeleteTab(
  	float tbPos)		//@parm Displacement at which tab should be deleted
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::DeleteTab");
  
  	HRESULT hr = UpdateFormat();			// If live Para object, update
  	//  _PF to current _prg values
  	if (hr != NOERROR)
  	{
  		return hr;    // Must be a zombie
  	}
  
  	hr = _PF.DeleteTab(FPPTS_TO_TWIPS(tbPos));
  	return hr == NOERROR ? FormatSetter(PFM_TABSTOPS) : hr;
  }
  
  /*
   *	ITextPara::GetAlignment(long * pValue)
   *
   *	@mfunc
   *		Property get method that gets the current paragraph
   *		alignment value
   *
   *	@rdesc
   *		HRESULT = (!pValue) ? E_INVALIDARG : NOERROR;
   */
  STDMETHODIMP CTxtPara::GetAlignment(
  	long *pValue)		//@parm Out parm to receive paragraph alignment
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::GetAlignment");
  
  	if (!pValue)
  	{
  		return E_INVALIDARG;
  	}
  
  	HRESULT hr = UpdateFormat();			// If live Para object, update
  	//  _PF to current _prg values
  	if (_PF.wAlignment > 4)					//  value.  Fix bogus value since
  	{
  		_PF.wAlignment = 0;    //  array lookup can't use it
  	}
  
  	*pValue = (_PF.dwMask & PFM_ALIGNMENT)
  		? (LONG)g_rgREtoTOMAlign[_PF.wAlignment] : tomUndefined;
  
  	return hr;
  }
  
  /*
   *	ITextPara::GetDoNotHyphen(long *pValue)
   *
   *	@mfunc
   *		Property get method that gets the tomBool for whether to
   *		suppress hyphenation for the paragraph in a range.
   *
   *	@rdesc
   *		HRESULT = (!pValue) ? E_INVALIDARG : NOERROR;
   */
  STDMETHODIMP CTxtPara::GetDoNotHyphen(
  	long *pValue)		//@parm Out parm to receive tomBool
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::GetDoNotHyphen");
  
  	return EffectGetter(pValue, PFM_DONOTHYPHEN);
  }
  
  /*
   *	ITextPara::GetDuplicate(ITextPara **ppPara)
   *
   *	@mfunc
   *		Property get method that gets a clone of this text paragraph
   *		format object.
   *
   *	@rdesc
   *		HRESULT = (!ppPara) ? E_INVALIDARG :
   *				  (if success) ? NOERROR : S_FALSE
   */
  STDMETHODIMP CTxtPara::GetDuplicate(
  	ITextPara **ppPara)		//@parm Out parm to receive ITextPara clone
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::GetDuplicate");
  
  	if (!ppPara)
  	{
  		return E_INVALIDARG;
  	}
  
  	*ppPara = NULL;
  
  	if (IsZombie())
  	{
  		return CO_E_RELEASED;
  	}
  
  	CTxtPara *pPara = new CTxtPara(NULL);	// NULL creates a clone
  	if (!pPara)								//  (its _prg is NULL)
  	{
  		return E_OUTOFMEMORY;
  	}
  
  	if (_prg)								// If this is a live para object,
  	{
  		_prg->GetParaFormat(&pPara->_PF);    //  clone directly from range
  	}
  
  	else
  	{
  		pPara->_PF = _PF;    // Copy value of this object
  	}
  
  	*ppPara = pPara;						// Return ptr to clone
  	return NOERROR;
  }
  
  /*
   *	ITextPara::GetFirstIndent(float * pValue)
   *
   *	@mfunc
   *		Property get method that gets the amount used to indent the
   *		first line of a paragraph relative to the left indent, which is used
   *		for subsequent lines.  The amount is given in floating-point points.
   *
   *	@rdesc
   *		HRESULT = (!pValue) ? E_INVALIDARG : NOERROR;
   */
  STDMETHODIMP CTxtPara::GetFirstIndent(
  	float *pValue)		//@parm Out parm to receive first-line indent
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::GetFirstIndent");
  
  	HRESULT hr = GetParameter(&_PF.dxOffset, PFM_OFFSET, -4, (long *)pValue);
  	if (hr == NOERROR && *pValue != (float)tomUndefined)
  	{
  		*pValue = -*pValue;    // Defined as negative of
  	}
  	return hr;									//  RichEdit dxOffset
  }
  
  /*
   *	ITextPara::GetKeepTogether(long * pValue)
   *
   *	@mfunc
   *		Property get method that gets the tomBool for whether to
   *		keep the lines in a range together.
   *
   *	@rdesc
   *		HRESULT = (!pValue) ? E_INVALIDARG : NOERROR;
   */
  STDMETHODIMP CTxtPara::GetKeepTogether(
  	long *pValue)		//@parm Out parm to receive tomBool
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::GetKeepTogether");
  
  	return EffectGetter(pValue, PFM_KEEP);
  }
  
  /*
   *	ITextPara::GetKeepWithNext(long * pValue)
   *
   *	@mfunc
   *		Property get method that gets the tomBool for whether to
   *		keep the paragraphs in this range together.
   *
   *	@rdesc
   *		HRESULT = (!pValue) ? E_INVALIDARG : NOERROR;
   */
  STDMETHODIMP CTxtPara::GetKeepWithNext(
  	long *pValue)		//@parm Out parm to receive tomBool
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::GetKeepWithNext");
  
  	return EffectGetter(pValue, PFM_KEEPNEXT);
  }
  
  #define	PFM_LEFTINDENT (PFM_STARTINDENT + PFM_OFFSET)
  /*
   *	ITextPara::GetLeftIndent(float * pValue)
   *
   *	@mfunc
   *		Property get method that gets the amount used to indent all
   *		but the first line of a paragraph.  The amount is given in
   *		floating-point points and is relative to the left margin.
   *
   *	@rdesc
   *		HRESULT = (!pValue) ? E_INVALIDARG : NOERROR;
   *
   *	@devnote
   *		For the TOM left indent to be defined, both the RichEdit start
   *		indent and the offset must be defined (see XOR and AND in *pValue
   *		code).
   */
  STDMETHODIMP CTxtPara::GetLeftIndent(
  	float *pValue)		//@parm Out parm to receive left indent
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::GetLeftIndent");
  
  	if (!pValue)
  	{
  		return E_INVALIDARG;
  	}
  
  	HRESULT hr = UpdateFormat();			// If live Para object, update
  	//  _PF to current _prg values
  	*pValue = ((_PF.dwMask ^ PFM_LEFTINDENT) & PFM_LEFTINDENT)
  		? (float)tomUndefined
  		: TWIPS_TO_FPPTS(_PF.dxStartIndent + _PF.dxOffset);
  
  	return hr;
  }
  
  /*
   *	ITextPara::GetLineSpacing(float * pValue)
   *
   *	@mfunc
   *		Property get method that gets the line spacing value, which
   *		is given in floating-point points.
   *
   *	@rdesc
   *		HRESULT = (!pValue) ? E_INVALIDARG : NOERROR;
   */
  STDMETHODIMP CTxtPara::GetLineSpacing(
  	float *pValue)		//@parm Out parm to receive line spacing
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::GetLineSpacing");
  
  	return GetParameter(&_PF.dyLineSpacing, PFM_LINESPACING, -4,
  			(long *)pValue);
  }
  
  /*
   *	ITextPara::GetLineSpacingRule(long * pValue)
   *
   *	@mfunc
   *		Property get method that gets the line-spacing rule for this range
   *
   *	@rdesc
   *		HRESULT = (!pValue) ? E_INVALIDARG : NOERROR;
   */
  STDMETHODIMP CTxtPara::GetLineSpacingRule(
  	long *pValue)		//@parm Out parm to receive line spacing rule
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::GetLineSpacingRule");
  
  	return GetParameter((long *)&_PF.bLineSpacingRule, PFM_LINESPACING,
  			1, pValue);
  }
  
  /*
   *	ITextPara::GetListAlignment(long * pValue)
   *
   *	@mfunc
   *		Property get method that gets the kind of bullet/numbering text
   *		alignment to use with paragraphs.
   *
   *	@rdesc
   *		HRESULT = (!pValue) ? E_INVALIDARG : NOERROR;
   */
  STDMETHODIMP CTxtPara::GetListAlignment(
  	long *pValue)		//@parm Out parm to receive numbering alignment
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::GetListAlignment");
  
  	HRESULT hr = GetParameter((long *)&_PF.wNumberingStyle,
  			PFM_NUMBERINGSTYLE, 2, pValue);
  	if (hr == NOERROR && *pValue != tomUndefined)
  	{
  		*pValue &= 3;    // Kill all but alignment bits
  	}
  
  	return hr;
  }
  
  /*
   *	ITextPara::GetListLevelIndex(long * pValue)
   *
   *	@mfunc
   *		Property get method that gets the list level index to use
   *		with paragraphs.
   *
   *	@rdesc
   *		HRESULT = (!pValue) ? E_INVALIDARG : NOERROR;
   */
  STDMETHODIMP CTxtPara::GetListLevelIndex(
  	long *pValue)		//@parm Out parm to receive list level index
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::GetListLevelIndex");
  
  	HRESULT hr = GetParameter((long *)&_PF.wNumberingStyle,
  			PFM_NUMBERINGSTYLE, 2, pValue);
  	if (hr == NOERROR)
  	{
  		*pValue = (*pValue >> 4) & 0xf;    // Kill all but list level index
  	}
  	return hr;
  }
  
  /*
   *	ITextPara::GetListStart(long * pValue)
   *
   *	@mfunc
   *		Property get method that gets the numbering start value to use
   *		with paragraphs.
   *
   *	@rdesc
   *		HRESULT = (!pValue) ? E_INVALIDARG : NOERROR;
   */
  STDMETHODIMP CTxtPara::GetListStart(
  	long *pValue)			//@parm Out parm to receive numbering start value
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::GetListSpace");
  
  	return GetParameter((long *)&_PF.wNumberingStart, PFM_NUMBERINGSTART, 2,
  			pValue);
  }
  
  /*
   *	ITextPara::GetListTab(long * pValue)
   *
   *	@mfunc
   *		Property get method that gets the distance between the first indent
   *		and the start of text on the first line.
   *
   *	@rdesc
   *		HRESULT = (!pValue) ? E_INVALIDARG : NOERROR;
   */
  STDMETHODIMP CTxtPara::GetListTab(
  	float *pValue)			//@parm Out parm to receive list tab to text
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::GetListTab");
  
  	return GetParameter((long *)&_PF.wNumberingTab, PFM_NUMBERINGTAB, -2,
  			(long *)pValue);
  }
  
  /*
   *	ITextPara::GetListType(long * pValue)
   *
   *	@mfunc
   *		Property get method that gets the type of list to use
   *		with paragraphs.
   *
   *	@rdesc
   *		HRESULT = (!pValue) ? E_INVALIDARG : NOERROR;
   *
   *	@devnote
   *		TOM's values are:
   *
   *		List Type		Value	Meaning
   *		tomNoList			0		Turn off paragraph numbering
   *		tomListBullet		1		default is bullet
   *		tomNumberAsArabic	2		0, 1, 2, ...
   *		tomNumberAsLCLetter	3		a, b, c, ...
   *		tomNumberAsUCLetter	4		A, B, C, ...
   *		tomNumberAsLCRoman	5		i, ii, iii, ...
   *		tomNumberAsUCRoman	6		I, II, III, ...
   *		tomNumberAsSequence	7		ListStart is 1st Unicode to use
   *
   *		Nibble 2 of _PF.wNumberingStyle says whether to number with trailing
   *		parenthesis, both parentheses, follow by period, or leave plain. This
   *		This nibble needs to be returned in nibble 4 of *pValue.
   */
  STDMETHODIMP CTxtPara::GetListType(
  	long *pValue)		//@parm Out parm to receive type of list
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::GetListType");
  
  	HRESULT hr = GetParameter((long *)&_PF.wNumbering,
  			PFM_NUMBERING, 2, pValue);
  
  	if (hr == NOERROR)			// OR in Number style bits (see note above)
  	{
  		*pValue |= (_PF.wNumberingStyle << 8) & 0xf0000;
  	}
  	return hr;
  }
  
  /*
   *	ITextPara::GetNoLineNumber(long * pValue)
   *
   *	@mfunc
   *		Property get method that gets the tomBool for whether to
   *		suppress line numbering for the paragraphs in a range.
   *
   *	@rdesc
   *		HRESULT = (!pValue) ? E_INVALIDARG : NOERROR;
   */
  STDMETHODIMP CTxtPara::GetNoLineNumber(
  	long *pValue)		//@parm Out parm to receive tomBool
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::GetNoLineNumber");
  
  	return EffectGetter(pValue, PFM_NOLINENUMBER);
  }
  
  /*
   *	ITextPara::GetPageBreakBefore(long * pValue)
   *
   *	@mfunc
   *		Property get method that gets the tomBool for whether to
   *		eject the page before the paragraphs in this range.
   *
   *	@rdesc
   *		HRESULT = (!pValue) ? E_INVALIDARG : NOERROR;
   */
  STDMETHODIMP CTxtPara::GetPageBreakBefore(
  	long *pValue)		//@parm Out parm to receive tomBool
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::GetPageBreakBefore");
  
  	return EffectGetter(pValue, PFM_PAGEBREAKBEFORE);
  }
  
  /*
   *	ITextPara::GetRightIndent(float * pValue)
   *
   *	@mfunc
   *		Property get method that gets the amount used to indent the
   *		right margin of a paragraph relative to the right margin.  The
   *		amount is given in floating-point points.
   *
   *	@rdesc
   *		HRESULT = (!pValue) ? E_INVALIDARG : NOERROR;
   */
  STDMETHODIMP CTxtPara::GetRightIndent(
  	float *pValue)		//@parm Out parm to receive right indent
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::GetRightIndent");
  
  	return GetParameter(&_PF.dxRightIndent, PFM_RIGHTINDENT, -4,
  			(long *)pValue);
  }
  
  /*
   *	ITextPara::GetSpaceAfter(float * pValue)
   *
   *	@mfunc
   *		Property get method that gets the amount used to space vertically
   *		after a paragraph.  The amount is given in floating-point points.
   *
   *	@rdesc
   *		HRESULT = (!pValue) ? E_INVALIDARG : NOERROR;
   */
  STDMETHODIMP CTxtPara::GetSpaceAfter(
  	float *pValue)		//@parm Out parm to receive space-after value
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::GetSpaceAfter");
  
  	return GetParameter(&_PF.dySpaceAfter, PFM_SPACEAFTER, -4,
  			(long *)pValue);
  }
  
  /*
   *	ITextPara::GetSpaceBefore(float * pValue)
   *
   *	@mfunc
   *		Property get method that gets the amount used to space vertically
   *		before starting a paragraph.  The amount is given in floating-point
   *		points.
   *
   *	@rdesc
   *		HRESULT = (!pValue) ? E_INVALIDARG : NOERROR;
   */
  STDMETHODIMP CTxtPara::GetSpaceBefore(
  	float *pValue)		//@parm Out parm to receive space-before value
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::GetSpaceBefore");
  
  	return GetParameter(&_PF.dySpaceBefore, PFM_SPACEBEFORE, -4,
  			(long *)pValue);
  }
  
  /*
   *	ITextPara::GetStyle(long * pValue)
   *
   *	@mfunc
   *		Property get method that gets the style handle for the
   *		paragraphs in this range.
   *
   *	@rdesc
   *		HRESULT = (!pValue) ? E_INVALIDARG : NOERROR;
   */
  STDMETHODIMP CTxtPara::GetStyle(
  	long 	*pValue)		//@parm Out parm to receive paragraph style handle
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::GetStyle");
  
  	return GetParameter((long *)&_PF.sStyle, PFM_STYLE, 2, pValue);
  }
  
  /*
   *	ITextPara::GetTab(long iTab, float *ptbPos, long *ptAlign, long *ptbLeader)
   *
   *	@mfunc
   *		Method that gets tab parameters for the iTab th tab, that
   *		is, set *ptbPos, *ptbAlign, and *ptbLeader equal to the iTab th
   *		tab's displacement, alignment, and leader style, respectively.
   *		iTab has special values defined in the table below.  The
   *		displacement is given in floating-point points.
   *
   *	@rdesc
   *		HRESULT = (!pdxptab || !ptbt || !pstyle || no iTab tab) ?
   *				  E_INVALIDARG : (exists) ? NOERROR : S_FALSE
   */
  STDMETHODIMP CTxtPara::GetTab(
  	long	iTab,			//@parm Index of tab to retrieve info for
  	float 	*ptbPos,			//@parm Out parm to receive tab displacement
  	long 	*ptbAlign,		//@parm Out parm to receive tab type
  	long 	*ptbLeader)		//@parm Out parm to receive tab style
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::GetTab");
  
  	if (!ptbPos || !ptbAlign || !ptbLeader)
  	{
  		return E_INVALIDARG;
  	}
  
  	*ptbAlign = *ptbLeader = 0;
  
  	HRESULT hr = UpdateFormat();			// If live Para object, update
  	//  _PF to current _prg values
  	if (!(_PF.dwMask & PFM_TABSTOPS))		// Tabs are undefined (more than
  	{
  		//  one set of definitions)
  		*ptbPos = (float)tomUndefined;
  		return hr;
  	}
  
  	LONG dxTab = 0;							// Default 0 in case GetTab fails
  
  	if (iTab < 0 && iTab >= tomTabBack)		// Save *ptbPos if it's supposed
  	{
  		dxTab = FPPTS_TO_TWIPS(*ptbPos);    //  be used (in general might get
  	}
  	//  floating-point error)
  	hr = _PF.GetTab(iTab, &dxTab, ptbAlign, ptbLeader);
  	*ptbPos = TWIPS_TO_FPPTS(dxTab);
  
  	return (hr == NOERROR && !dxTab) ? S_FALSE : hr;
  }
  
  /*
   *	ITextPara::GetTabCount(long * pValue)
   *
   *	@mfunc
   *		Property get method that gets the tab count.
   *
   *	@rdesc
   *		HRESULT = (!pValue) ? E_INVALIDARG : NOERROR;
   */
  STDMETHODIMP CTxtPara::GetTabCount(
  	long 	*pValue)		//@parm Out parm to receive tab count
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::GetTabCount");
  
  	HRESULT hr = GetParameter((long *)&_PF.cTabCount, PFM_TABSTOPS, 2, pValue);
  
  	if (hr == NOERROR && _PF.cTabCount == 1 &&
  		(_PF.rgxTabs[0] & PFT_DEFAULT) == PFT_DEFAULT)
  	{
  		*pValue = 0;							// Our internal count of 1
  	}											//  for default tab should
  	return hr;									//  be 0 for TOM
  }
  
  /*
   *	ITextPara::GetWidowControl(long * pValue)
   *
   *	@mfunc
   *		Property get method that gets the tomBool for whether to
   *		control widows and orphans for the paragraphs in a range.
   *
   *	@rdesc
   *		HRESULT = (!pValue) ? E_INVALIDARG : NOERROR;
   */
  STDMETHODIMP CTxtPara::GetWidowControl(
  	long 	*pValue)		//@parm Out parm to receive tomBool
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::GetWidowControl");
  
  	return EffectGetter(pValue, PFM_NOWIDOWCONTROL);
  }
  
  /*
   *	ITextPara::IsEqual(ITextPara * pPara, long * pB)
   *
   *	@mfunc
   *		Method that sets pB = tomTrue if this range has the same
   *		properties as *pPara. The IsEqual() method ignores entries for which
   *		either para object has a tomUndefined value.
   *
   *	@rdesc
   *		HRESULT = (equal objects) ? NOERROR : S_FALSE
   */
  STDMETHODIMP CTxtPara::IsEqual(
  	ITextPara 	*pPara,		//@parm ITextPara to compare to
  	long 		*pB)			//@parm Out parm to receive comparison result
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::IsEqual");
  
  	UpdateFormat();							// Update _PFs in case they are
  	((CTxtPara *)pPara)->UpdateFormat();	//  attached to ranges
  
  	return IsTrue(SameVtables(this, pPara) &&
  			_PF.Compare(&((CTxtPara *)pPara)->_PF), pB);
  }
  
  /*
   *	ITextPara::Reset(long Value)
   *
   *	@mfunc
   *		Method that resets the paragraph formatting to the default
   *		values to 1) those defined by the RTF \pard control word (Value =
   *		tomDefault), and 2) all undefined values (Value = tomUndefined).
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR :
   *				  (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
   */
  STDMETHODIMP CTxtPara::Reset(
  	long Value)		//@parm Kind of reset (tomDefault or tomUndefined)
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::Reset");
  
  	HRESULT hr = CanChange(NULL);
  
  	if (hr != NOERROR)								// Takes care of zombie
  	{
  		return hr;    //  and protection
  	}
  
  	if (Value == tomDefault)
  	{
  		if (_prg)
  		{
  			CopyFormat(&_PF, _prg->GetPed()->GetParaFormat(-1), sizeof(CParaFormat));
  			FormatSetter(PFM_ALL2);
  		}
  		else
  		{
  			_PF.InitDefault();
  		}
  	}
  	else if (Value == tomUndefined && !_prg)			// Only applicable
  	{
  		_PF.dwMask = 0;    //  for clones
  	}
  
  	else
  	{
  		return E_INVALIDARG;
  	}
  
  	return NOERROR;
  }
  
  /*
   *	ITextPara::SetAlignment(long Value)
   *
   *	@mfunc
   *		Property put method that sets the paragraph alignment to Value
   *
   *	@rdesc
   *		HRESULT = (Value > 3) ? E_INVALIDARG :
   *				  (if success) ? NOERROR :
   *				  (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
   */
  STDMETHODIMP CTxtPara::SetAlignment(
  	long Value)		//@parm New paragraph alignment
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::SetAlignment");
  
  	if (Value == tomUndefined)					// NINCH
  	{
  		return NOERROR;
  	}
  
  	if ((DWORD)Value > 3)
  	{
  		return E_INVALIDARG;
  	}
  
  	_PF.wAlignment = g_rgTOMtoREAlign[Value];
  
  	return FormatSetter(PFM_ALIGNMENT);
  }
  
  /*
   *	ITextPara::SetDoNotHyphen(long Value)
   *
   *	@mfunc
   *		Property put method that sets the tomBool that controls the
   *		suppression of hyphenation for the paragraphs in the range.
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR :
   *				  (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
   */
  STDMETHODIMP CTxtPara::SetDoNotHyphen(
  	long Value)		//@parm New tomBool for suppressing hyphenation
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::SetDoNotHypen");
  
  	return EffectSetter(Value, PFM_DONOTHYPHEN);
  }
  
  /*
   *	ITextPara::SetDuplicate(ITextPara *pPara)
   *
   *	@mfunc
   *		Property put method that applies the paragraph formatting of pPara
   *		to this para object.  Note that tomUndefined values in pPara have
   *		no effect (NINCH - NoInputNoCHange).
   *
   *	@rdesc
   *		HRESULT = (!pPara) ? E_INVALIDARG :
   *				  (if success) ? NOERROR :
   *				  (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
   */
  STDMETHODIMP CTxtPara::SetDuplicate(
  	ITextPara *pPara)		//@parm New paragraph formatting
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::SetDuplicate");
  
  	DWORD		dwMask = 0;
  	long		iTab;
  	CTxtRange 	*prg;
  	long		tbAlign;
  	long		tbLeader;
  	float		tbPos;
  	long		Value;
  	float		x, y, z;
  
  	if (IsZombie())							// Check for range zombie
  	{
  		return CO_E_RELEASED;
  	}
  
  	if (*(LONG *)this == *(LONG *)pPara)	// If same TOM engine, cast and
  	{
  		//  copy
  		((CTxtPara *)pPara)->UpdateFormat();
  		_PF = ((CTxtPara *)pPara)->_PF;
  		dwMask = _PF.dwMask;				// Use this mask in case this
  	}										//  para is active
  	else
  	{
  		// Need to call pFont for all para
  		prg = _prg;							//  properties
  		_prg = NULL;						// Turn into clone during transfer
  
  		pPara->GetStyle(&Value);
  		SetStyle(Value);
  
  		pPara->GetAlignment(&Value);
  		SetAlignment(Value);
  
  		pPara->GetDoNotHyphen(&Value);
  		SetDoNotHyphen(Value);
  
  		pPara->GetKeepTogether(&Value);
  		SetKeepTogether(Value);
  
  		pPara->GetKeepWithNext(&Value);
  		SetKeepWithNext(Value);
  
  		pPara->GetFirstIndent(&x);
  		pPara->GetLeftIndent(&y);
  		pPara->GetRightIndent(&z);
  		SetIndents(x, y, z);
  
  		pPara->GetLineSpacingRule(&Value);
  		pPara->GetLineSpacing(&y);
  		SetLineSpacing(Value, y);
  
  		pPara->GetNoLineNumber(&Value);
  		SetNoLineNumber(Value);
  
  		pPara->GetListAlignment(&Value);
  		SetListAlignment(Value);
  
  		pPara->GetListLevelIndex(&Value);
  		SetListLevelIndex(Value);
  
  		pPara->GetListStart(&Value);
  		SetListStart(Value);
  
  		pPara->GetListTab(&x);
  		SetListTab(x);
  
  		pPara->GetListType(&Value);
  		SetListType(Value);
  
  		pPara->GetPageBreakBefore(&Value);
  		SetPageBreakBefore(Value);
  
  		pPara->GetSpaceBefore(&y);
  		SetSpaceBefore(y);
  
  		pPara->GetSpaceAfter(&y);
  		SetSpaceAfter(y);
  
  		pPara->GetWidowControl(&Value);
  		SetWidowControl(Value);
  
  		ClearAllTabs();
  		pPara->GetTabCount(&Value);
  		for (iTab = 0; iTab < Value; iTab++)
  		{
  			pPara->GetTab(iTab, &tbPos, &tbAlign, &tbLeader);
  			AddTab(tbPos, tbAlign, tbLeader);
  		}
  		_prg = prg;							// Restore original value
  	}
  	return FormatSetter(dwMask);			// Apply it unless !_prg
  }
  
  /*
   *	ITextPara::SetKeepTogether(long Value)
   *
   *	@mfunc
   *		Property put method that sets the tomBool that controls
   *		whether to keep the lines in a range together.
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR :
   *				  (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
   */
  STDMETHODIMP CTxtPara::SetKeepTogether(
  	long Value)		//@parm New tomBool for keeping lines together
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::SetKeepTogether");
  
  	return EffectSetter(Value, PFM_KEEP);
  }
  
  /*
   *	ITextPara::SetKeepWithNext(long Value)
   *
   *	@mfunc
   *		Property put method that sets the tomBool that controls
   *		whether to keep the paragraphs in a range together.
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR :
   *				  (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
   */
  STDMETHODIMP CTxtPara::SetKeepWithNext(
  	long Value)		//@parm New tomBool for keeping paragraphs together
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::SetKeepWithNext");
  
  	return EffectSetter(Value, PFM_KEEPNEXT);
  }
  
  /*
   *	ITextPara::SetIndents(float First, float Left, float Right)
   *
   *	@mfunc
   *		Method that sets the left indent of all but the first line
   *		of a paragraph equal to Left and sets the displacement of the first
   *		line of a paragraph relative to the left indent equal to First.  The
   *		left indent value is relative to the left margin. You can also set
   *		the right indent by giving the optional Right parameter a value (the
   *		(default is tomUndefined).  All indents are given in floating-point
   *		points.
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR :
   *				  (protected) ? E_ACCESSDENIED : E_INVALIDARG
   */
  STDMETHODIMP CTxtPara::SetIndents(
  	float First,	//@parm New first indent (1st-line offset relative to left indent)
  	float Left,		//@parm New left indent (left offset of all but 1st line)
  	float Right)	//@parm New right indent (right offset of all lines)
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::SetIndents");
  
  	DWORD	dwMask	= 0;
  	LONG	j = (First != (float)tomUndefined) + (Left == (float)tomUndefined);
  
  	if (IsZombie())
  	{
  		return CO_E_RELEASED;
  	}
  
  	if (j < 2)										// At least First or Left
  	{
  		//  defined
  		if (j == 1)									// Only one defined: need
  		{
  			UpdateFormat();    //  current _PF.dxOffset
  		}
  
  		if (First != (float)tomUndefined)
  		{
  			j = FPPTS_TO_TWIPS(First);
  			if (Left == (float)tomUndefined)
  			{
  				_PF.dxStartIndent += _PF.dxOffset	// Cancel current offset
  					+ j;							//  and add in new one
  			}
  			_PF.dxOffset = -j;						// Offset for all but 1st
  			dwMask = PFM_OFFSET + PFM_STARTINDENT;	//  line
  		}
  		if (Left != (float)tomUndefined)
  		{
  			_PF.dxStartIndent =  FPPTS_TO_TWIPS(Left) - _PF.dxOffset;
  			dwMask |= PFM_STARTINDENT;
  		}
  	}
  
  	if (Right != (float)tomUndefined)
  	{
  		_PF.dxRightIndent = FPPTS_TO_TWIPS(Right);
  		dwMask |= PFM_RIGHTINDENT;
  	}
  
  	return dwMask ? FormatSetter(dwMask) : NOERROR;
  }
  
  /*
   *	ITextPara::SetLineSpacing(long Rule, float Spacing)
   *
   *	@mfunc
   *		Method that sets the paragraph line spacing rule to Rule and
   *		the line spacing to Spacing. If the line spacing rule treats the
   *		Spacing value as a linear dimension, then that dimension is given in
   *		floating-point points.
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR :
   *				  (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
   */
  STDMETHODIMP CTxtPara::SetLineSpacing(
  	long	Rule,		//@parm Value of new line-spacing rule
  	float	Spacing)	//@parm Value of new line spacing
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::SetLineSpacing");
  
  	LONG j = (Rule == tomUndefined) + (Spacing == (float)tomUndefined);
  
  	if (j == 2)
  	{
  		return NOERROR;
  	}
  
  	if (j == 1 || (DWORD)Rule > 5 || Spacing < 0)
  	{
  		return E_INVALIDARG;
  	}
  
  	_PF.bLineSpacingRule = (BYTE)Rule;			// Default as if both are OK
  	_PF.dyLineSpacing	 = (SHORT)FPPTS_TO_TWIPS(Spacing);
  
  	return FormatSetter(PFM_LINESPACING);
  }
  
  /*
   *	ITextPara::SetListAlignment (long Value)
   *
   *	@mfunc
   *		Property put method that sets the kind of List alignment to be
   *		used for paragraphs.
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR :
   *				  (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
   */
  STDMETHODIMP CTxtPara::SetListAlignment(
  	long Value)		//@parm Value of new list alignment
  {
  	if (Value == tomUndefined)
  	{
  		return NOERROR;
  	}
  
  	if ((unsigned)Value > tomAlignRight)
  	{
  		return E_INVALIDARG;
  	}
  
  	long	Style;
  	HRESULT hr = GetParameter((long *)&_PF.wNumberingStyle,
  			PFM_NUMBERINGSTYLE, 2, &Style);
  	if (hr != NOERROR)
  	{
  		return hr;
  	}
  
  	return SetParameter((long *)&_PF.wNumberingStyle, PFM_NUMBERINGSTYLE,
  			2, (Style & ~3) | (Value & 3));
  }
  
  /*
   *	ITextPara::SetListLevelIndex (long Value)
   *
   *	@mfunc
   *		Property put method that sets the kind of list level index to be
   *		used for paragraphs.
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR :
   *				  (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
   */
  STDMETHODIMP CTxtPara::SetListLevelIndex(
  	long Value)
  {
  	if (Value == tomUndefined)
  	{
  		return NOERROR;
  	}
  
  	if ((unsigned)Value > 15)
  	{
  		return E_INVALIDARG;
  	}
  
  	long	Style;
  	HRESULT hr = GetParameter((long *)&_PF.wNumberingStyle,
  			PFM_NUMBERINGSTYLE, 2, &Style);
  	if (hr != NOERROR)
  	{
  		return hr;
  	}
  
  	return SetParameter((long *)&_PF.wNumberingStyle, PFM_NUMBERINGSTYLE,
  			2, (Style & ~0xf0) | (Value << 4));
  }
  
  /*
   *	ITextPara::SetListStart (long Value)
   *
   *	@mfunc
   *		Property put method that sets the starting number to use for
   *		paragraph numbering
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR :
   *				  (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
   */
  STDMETHODIMP CTxtPara::SetListStart(
  	long Value)		//@parm New numbering start value
  {
  	if (Value == tomUndefined)
  	{
  		return NOERROR;
  	}
  
  	if (Value < 0)
  	{
  		return E_INVALIDARG;
  	}
  
  	return SetParameter((long *)&_PF.wNumberingStart, PFM_NUMBERINGSTART,
  			2, Value);
  }
  
  /*
   *	ITextPara::SetListTab (long Value)
   *
   *	@mfunc
   *		Property put method that sets the distance between the first indent
   *		and the start of text on the first line.
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR :
   *				  (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
   */
  STDMETHODIMP CTxtPara::SetListTab(
  	float Value)		//@parm New numbering tab value
  {
  	if (Value == (float)tomUndefined)
  	{
  		return NOERROR;
  	}
  
  	if (Value < 0)
  	{
  		return E_INVALIDARG;
  	}
  
  	return SetParameter((long *)&_PF.wNumberingTab, PFM_NUMBERINGTAB,
  			-2, *(long *)&Value);
  }
  
  /*
   *	ITextPara::SetListType (long Value)
   *
   *	@mfunc
   *		Property put method that sets the kind of List to be
   *		used for paragraphs.
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR :
   *				  (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
   */
  STDMETHODIMP CTxtPara::SetListType(
  	long Value)		//@parm New List code
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::SetListType");
  
  	if (Value == tomUndefined)
  	{
  		return NOERROR;
  	}
  
  	if ((unsigned)Value > 0xf0000)
  	{
  		return E_INVALIDARG;
  	}
  
  	long	Style;
  	HRESULT hr = GetParameter((long *)&_PF.wNumberingStyle,
  			PFM_NUMBERINGSTYLE, 2, &Style);
  	if (hr != NOERROR)
  	{
  		return hr;
  	}
  
  	_PF.wNumbering		= (WORD)Value;
  	_PF.wNumberingStyle = (Style & ~0xf00) | ((Value >> 8) & 0xf00);
  
  	return FormatSetter(PFM_NUMBERING | PFM_NUMBERINGSTYLE);
  }
  
  /*
   *	ITextPara::SetNoLineNumber (long Value)
   *
   *	@mfunc
   *		Property put method that sets the tomBool that controls
   *		whether to suppress the numbering of paragraphs in a range.
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR :
   *				  (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
   */
  STDMETHODIMP CTxtPara::SetNoLineNumber(
  	long Value)		//@parm New tomBool for suppressing line numbering
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::SetNoLineNumber");
  
  	return EffectSetter(Value, PFM_NOLINENUMBER);
  }
  
  /*
   *	ITextPara::SetPageBreakBefore (long Value)
   *
   *	@mfunc
   *		Property put method that sets the tomBool that controls
   *		whether to eject the page before each paragraph in a range.
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR :
   *				  (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
   */
  STDMETHODIMP CTxtPara::SetPageBreakBefore(
  	long Value)		//@parm New tomBool for ejecting page before paragraphs
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::SetPageBreakBefore");
  
  	return EffectSetter(Value, PFM_PAGEBREAKBEFORE);
  }
  
  /*
   *	ITextPara::SetRightIndent (float Value)
   *
   *	@mfunc
   *		Property put method that sets the amount to indent the right
   *		margin of paragraph equal to Value, which is given in floating-point
   *		points.
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR :
   *				  (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
   */
  STDMETHODIMP CTxtPara::SetRightIndent(
  	float Value)		//@parm New right indent
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::SetRightIndent");
  
  	return SetParameter(&_PF.dxRightIndent, PFM_RIGHTINDENT, -4,
  			*(long *)&Value);
  }
  
  /*
   *	ITextPara::SetSpaceAfter(float Value)
   *
   *	@mfunc
   *		Property put method that sets the amount to space vertically
   *		after finishing a paragraph equal to Value, which is given in
   *		floating-point points.
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR :
   *				  (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
   */
  STDMETHODIMP CTxtPara::SetSpaceAfter(
  	float Value)		//@parm New space-after value
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::SetSpaceAfter");
  
  	if (Value == (float)tomUndefined)
  	{
  		return NOERROR;
  	}
  
  	if (Value < 0)
  	{
  		return E_INVALIDARG;
  	}
  
  	return SetParameter(&_PF.dySpaceAfter, PFM_SPACEAFTER, -4,
  			*(long *)&Value);
  }
  
  /*
   *	ITextPara::SetSpaceBefore(float Value)
   *
   *	@mfunc
   *		Property put method that sets the amount to space vertically
   *		before starting a paragraph equal to Value, which is given in
   *		floating-point points.
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR :
   *				  (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
   */
  STDMETHODIMP CTxtPara::SetSpaceBefore(
  	float Value)		//@parm New space-before value
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::SetSpaceBefore");
  
  	if (Value == (float)tomUndefined)
  	{
  		return NOERROR;
  	}
  
  	if (Value < 0)
  	{
  		return E_INVALIDARG;
  	}
  
  	return SetParameter(&_PF.dySpaceBefore, PFM_SPACEBEFORE, -4,
  			*(long *)&Value);
  }
  
  /*
   *	ITextPara::SetStyle(long Value)
   *
   *	@mfunc
   *		Property put method that sets the paragraph style handle for
   *		the paragraphs in a range.  See GetStyle() for further discussion.
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR :
   *				  (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
   */
  STDMETHODIMP CTxtPara::SetStyle(
  	long Value)		//@parm New paragraph style handle
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::SetStyle");
  
  	if (Value == tomUndefined)
  	{
  		return NOERROR;
  	}
  
  	if (Value < -32768 || Value > 32767)
  	{
  		return E_INVALIDARG;
  	}
  
  	return SetParameter((long *)&_PF.sStyle, PFM_STYLE, 2, Value);
  }
  
  /*
   *	ITextPara::SetWidowControl(long Value)
   *
   *	@mfunc
   *		Property put method that sets the tomBool that controls the
   *		suppression of widows and orphans.
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR :
   *				  (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
   */
  STDMETHODIMP CTxtPara::SetWidowControl(
  	long Value)		//@parm New tomBool for suppressing widows and orphans
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtPara::SetWidowControl");
  
  	return EffectSetter(Value, PFM_NOWIDOWCONTROL);
  }
  
  
  //----------------------------- CTxtFont Helpers -------------------------------------
  
  /*
   *	@doc INTERNAL
   *
   *	CTxtFont::EffectGetter (ptomBool, dwMask)
   *
   *	@mfunc
   *		Set *<p ptomBool> = state of bit given by the bit mask <p dwMask>
   *
   *	@rdesc
   *		HRESULT = (!<p ptomBool>) ? E_INVALIDARG : NOERROR
   */
  HRESULT CTxtFont::EffectGetter(
  	long 	*ptomBool,		//@parm Out parm to receive tomBool
  	DWORD	dwMask) 		//@parm Bit mask identifying effect to retrieve
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEINTERN, "CTxtFont::EffectGetter");
  
  	if (!ptomBool)
  	{
  		return E_INVALIDARG;
  	}
  
  	HRESULT hr = UpdateFormat();			// If live Font object, update
  	//  _CF to current _prg values
  	*ptomBool = !(_CF.dwMask   & dwMask) ? tomUndefined :
  		(_CF.dwEffects & dwMask) ? tomTrue : tomFalse;
  
  	return hr;
  }
  
  /*
   *	CTxtFont::EffectSetter (Value, dwMask, dwEffect)
   *
   *	@mfunc
   *		Mask off this range's effect bits identified by <p dwMask> and set
   *		effect bit given by <p dwEffect> equal to value given by <p Value>
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR : S_FALSE
   */
  HRESULT CTxtFont::EffectSetter(
  	long 	Value,		//@parm Value to set effect bit to
  	DWORD	dwMask, 	//@parm Bit mask identifying effect(s) to turn off
  	DWORD	dwEffect)	//@parm Effect bit to set
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEINTERN, "CTxtFont::EffectSetter");
  
  	if (Value == tomUndefined)					// Do nothing (NINCH)
  	{
  		return NOERROR;
  	}
  
  	if (Value == tomToggle)
  	{
  		if (_prg)								// If live Font object, update
  		{
  			//  _CF.dwEffects for toggling
  			if (_prg->IsZombie())
  			{
  				return CO_E_RELEASED;
  			}
  
  			LONG iFormat = _prg->_iFormat;		// Default iFormat for IP
  			LONG cch	 = _prg->GetCch();
  
  			if (cch)								// Range is nondegenerate
  			{
  				CFormatRunPtr rp(_prg->_rpCF);
  				if (cch > 0)						// Get iFormat at cpFirst
  				{
  					rp.AdvanceCp(-cch);
  				}
  				iFormat = rp.GetFormat();
  			}
  			_CF.dwEffects = _prg->GetPed()->GetCharFormat(iFormat)->dwEffects;
  		}
  		_CF.dwEffects ^= dwEffect;				// Toggle effect(s)
  	}
  	else
  	{
  		_CF.dwEffects &= ~dwMask;				// Default effect(s) off
  		if (Value)
  		{
  			if (Value != tomTrue)
  			{
  				return E_INVALIDARG;
  			}
  			_CF.dwEffects |= dwEffect;			// Turn an effect on
  		}
  	}
  	return FormatSetter(dwMask);
  }
  
  /*
   *	CTxtFont::FormatSetter (dwMask)
   *
   *	@mfunc
   *		Set this CCharFormat or _prg's with mask <p dwMask>
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR : S_FALSE
   *				  (protected) ? E_ACCESSDENIED : E_OUTOFMEMORY
   */
  HRESULT CTxtFont::FormatSetter(
  	DWORD	 dwMask)	//@parm Mask for value
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEINTERN, "CTxtFont::FormatSetter");
  
  	if (_prg)
  	{
  		_CF.dwMask = dwMask;					// Only set data defined by
  		return _prg->CharFormatSetter(&_CF);	//  dwMask
  	}
  
  	_CF.dwMask |= dwMask;						// Collect data in font clone
  	return NOERROR;
  }
  
  /*
   *	CTxtFont::GetParameter (pParm, dwMask, Type, pValue)
   *
   *	@mfunc
   *		If _prg is defined (not clone), update _CF to range value.
   *		Set *pValue = *pParm unless NINCHed, in which case set it to
   *		Type < 0 ? (float)tomUndefined : tomUndefined.  |Type| gives
   *		the byte length of the pParm field.
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR : S_FALSE
   */
  HRESULT	CTxtFont::GetParameter(
  	long 	*pParm,		//@parm Address of _CF member to get
  	DWORD	dwMask,		//@parm _CF member mask for NINCH checking
  	long	Type,		//@parm # bytes of parameter or 0 for float
  	long 	*pValue)		//@parm Out parm to receive value
  {
  	UpdateFormat();							// If live Font object, update
  	//  _CF to current _prg values
  	return CTxtFormat::GetParameter(pParm, _CF.dwMask & dwMask, Type, pValue);
  }
  
  /*
   *	CTxtFont::SetParameter (pParm, dwMask, Type, Value)
   *
   *	@mfunc
   *		Set parameter at address pParm with mask big dwMask to the value
   *		Value performing type conversions indicated by Type
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR : S_FALSE
   */
  HRESULT	CTxtFont::SetParameter(
  	long 	*pParm,		//@parm Address of _CF member to get
  	DWORD	dwMask,		//@parm _CF member mask for NINCH checking
  	long	Type,		//@parm # bytes of parameter or 0 for float
  	long 	Value)		//@parm Out parm to receive value
  {
  	HRESULT hr = CTxtFormat::SetParameter(pParm, Type, Value);
  	return hr == NOERROR ? FormatSetter(dwMask) : hr;
  }
  
  /*
   *	CTxtFont::UpdateFormat ()
   *
   *	@mfunc
   *		Update format if this font object is attached to a live range.
   *		Set _PF.dwMask = 0 if attached to zombied range.
   *
   *	@rdesc
   *		HRESULT = (attached to zombied range)
   *				? CO_E_RELEASED : NOERROR
   */
  HRESULT CTxtFont::UpdateFormat()
  {
  	if (_prg)
  	{
  		if (_prg->IsZombie())
  		{
  			_CF.dwMask = 0;					// Nothing defined
  			return CO_E_RELEASED;
  		}
  		_prg->GetCharFormat(&_CF);
  	}
  	return NOERROR;
  }
  
  
  //----------------------------- CTxtPara Helpers -------------------------------------
  
  /*
   *	@doc INTERNAL
   *
   *	CTxtPara::EffectGetter (ptomBool, dwMask)
   *
   *	@mfunc
   *		Set *<p ptomBool> = state of bit given by the bit mask <p dwMask>
   *
   *	@rdesc
   *		HRESULT = (!<p ptomBool>) ? E_INVALIDARG : NOERROR
   */
  HRESULT CTxtPara::EffectGetter(
  	long 	*ptomBool,		//@parm Out parm to receive tomBool
  	DWORD	dwMask) 		//@parm Bit mask identifying effect to retrieve
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEINTERN, "CTxtPara::EffectGetter");
  
  	if (!ptomBool)
  	{
  		return E_INVALIDARG;
  	}
  
  	HRESULT hr = UpdateFormat();			// If live Para object, update
  	//  _PF to current _prg values
  	*ptomBool = !(_PF.dwMask   & dwMask) ? tomUndefined :
  		(_PF.wEffects & (dwMask >> 16)) ? tomTrue : tomFalse;
  	return hr;
  }
  
  /*
   *	CTxtPara::EffectSetter (Value, dwMask)
   *
   *	@mfunc
   *		Set this range's effect bit as given by <p dwMask> equal to value
   *		given by <p Value>
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR : S_FALSE
   *
   *	@devnote
   *		Note that the mask bits for paragraph effects are in the high word
   *		of _PF.dwMask, but the effects are stored in the WORD wEffects, which
   *		is an alias for the CHARFORMAT wReserved.
   */
  HRESULT CTxtPara::EffectSetter(
  	long 	Value,		//@parm Value to set effect bit to
  	DWORD	dwMask) 	//@parm Bit mask identifying effect to set
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEINTERN, "CTxtPara::EffectSetter");
  
  	if (Value == tomUndefined)					// Do nothing (NINCH)
  	{
  		return NOERROR;
  	}
  
  	DWORD	dwEffects = _PF.wEffects << 16;		// Move effects over to where
  	//  mask is
  	if (Value == tomToggle)
  	{
  		if (_prg)								// If live Para object, update
  		{
  			//  _PF.wEffects for toggling
  			if (_prg->IsZombie())
  			{
  				return CO_E_RELEASED;
  			}
  
  			_prg->GetParaFormat(&_PF);
  			dwEffects = _PF.wEffects << 16;
  		}
  		if (_PF.dwMask & dwMask)					// Effect is defined:
  		{
  			dwEffects ^=  dwMask;    //  toggle it
  		}
  		else									// Effect is NINCHed
  		{
  			dwEffects &= ~dwMask;    // Turn it all off
  		}
  	}
  	else
  	{
  		dwEffects &= ~dwMask;					// Default effect off
  		if (Value)
  		{
  			if (Value != tomTrue)
  			{
  				return E_INVALIDARG;
  			}
  			dwEffects |= dwMask;				// Turn it on
  		}
  	}
  
  	_PF.wEffects = (WORD)(dwEffects >> 16);
  	return FormatSetter(dwMask);
  }
  
  /*
   *	CTxtPara::FormatSetter (dwMask)
   *
   *	@mfunc
   *		Set this CParaFormat or _prg's with mask <p dwMask>
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR : S_FALSE
   */
  HRESULT CTxtPara::FormatSetter(
  	DWORD	 dwMask)	//@parm Mask for value
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEINTERN, "CTxtPara::FormatSetter");
  
  	if (_prg)
  	{
  		_PF.dwMask = dwMask;					// Only set data defined by
  		return _prg->ParaFormatSetter(&_PF);	//  dwMask
  	}
  
  	_PF.dwMask |= dwMask;						// Collect data in para clone
  	return NOERROR;
  }
  
  /*
   *	CTxtPara::GetParameter (pParm, dwMask, Type, pValue)
   *
   *	@mfunc
   *		If _prg is defined (not clone), update _PF to range value.
   *		Set *pValue = *pParm unless NINCHed, in which case set it to
   *		Type < 0 ? (float)tomUndefined : tomUndefined.  |Type| gives
   *		the byte length of the pParm field.
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR : S_FALSE
   */
  HRESULT	CTxtPara::GetParameter(
  	long 	*pParm,		//@parm Address of _PF member to get
  	DWORD	dwMask,		//@parm _PF member mask for NINCH checking
  	long	Type,		//@parm # bytes of parameter or 0 for float
  	long 	*pValue)		//@parm Out parm to receive value
  {
  	UpdateFormat();							// If live Para object, update
  	//  _PF to current _prg values
  	return CTxtFormat::GetParameter(pParm, _PF.dwMask & dwMask, Type, pValue);
  }
  
  /*
   *	CTxtPara::SetParameter (pParm, dwMask, Type, Value)
   *
   *	@mfunc
   *		Set parameter at address pParm with mask big dwMask to the value
   *		Value performing type conversions indicated by Type
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR : S_FALSE
   */
  HRESULT	CTxtPara::SetParameter(
  	long 	*pParm,		//@parm Address of _PF member to get
  	DWORD	dwMask,		//@parm _PF member mask for NINCH checking
  	long	Type,		//@parm # bytes of parameter or 0 for float
  	long 	Value)		//@parm Out parm to receive value
  {
  	HRESULT hr = CTxtFormat::SetParameter(pParm, Type, Value);
  	return hr == NOERROR ? FormatSetter(dwMask) : hr;
  }
  
  /*
   *	CTxtPara::UpdateFormat ()
   *
   *	@mfunc
   *		Update format if this para object is attached to a live range.
   *		Set _PF.dwMask = 0 if attached to zombied range.
   *
   *	@rdesc
   *		HRESULT = (attached to zombied range)
   *				? CO_E_RELEASED : NOERROR
   */
  HRESULT CTxtPara::UpdateFormat()
  {
  	if (_prg)
  	{
  		if (_prg->IsZombie())
  		{
  			_PF.dwMask = 0;					// Nothing defined
  			return CO_E_RELEASED;
  		}
  		_prg->GetParaFormat(&_PF);
  	}
  	return NOERROR;
  }
  
  //---------------------------- CTxtFormat Methods --------------------------------
  
  /*
   *	CTxtFormat::CTxtFormat(prg)
   *
   *	@mfunc
   *		Constructor
   */
  CTxtFormat::CTxtFormat(CTxtRange *prg)
  {
  	_cRefs	= 1;
  	_prg	= prg;					// NULL for clone
  	if (prg)
  	{
  		prg->AddRef();
  	}
  }
  
  /*
   *	CTxtFormat::~CTxtFormat()
   *
   *	@mfunc
   *		Destructor
   */
  CTxtFormat::~CTxtFormat()
  {
  	if (_prg)
  	{
  		_prg->Release();
  	}
  }
  
  /*
   *	CTxtFormat::CanChange (pbCanChange)
   *
   *	@func
   *		Set *<p pbCanChange> = tomTRUE iff this format object can be changed
   *
   *	@rdesc
   *		HRESULT = (can change format) ? NOERROR : S_FALSE
   *
   *	@devnote
   *		This method is shared by ITextFont and ITextPara.  If pRange is NULL,
   *		the object is a clone, i.e., unattached to a CTxtRange
   */
  HRESULT CTxtFormat::CanChange(
  	long *pbCanChange)		//@parm Out parm to receive boolean value
  {
  	TRACEBEGIN(TRCSUBSYSTOM, TRCSCOPEEXTERN, "CTxtFormat::CanChange");
  
  	LONG	fCanChange = tomTrue;
  	HRESULT hr;
  
  	if (_prg)
  	{
  		hr = _prg->CanEdit(pbCanChange);
  		if (hr != NOERROR)						// S_FALSE or CO_E_RELEASED
  		{
  			return hr;
  		}
  
  		if (!_prg->GetPed()->IsRich())
  		{
  			fCanChange = tomFalse;
  		}
  	}
  	if (pbCanChange)
  	{
  		*pbCanChange = fCanChange;
  	}
  
  	return fCanChange ? NOERROR : S_FALSE;
  }
  
  /*
   *	CTxtFormat::GetParameter (pParm, fDefined, Type, pValue)
   *
   *	@mfunc
   *		Set *pValue = *pLong unless NINCHed. Perform conversions specified by
   *		Type.  If Type > 0, it is treated as length of an unsigned integer,
   *		i.e., 1, 2, or 4 bytes.  If it is negative, the output is a float
   *		and the input has a length given by |Type|, so -2 converts a WORD
   *		into a float, unless dwMask = CFM_SPACING, in which case it converts
   *		a short into a float.  -4 converts a long into a float.
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR : S_FALSE
   */
  HRESULT	CTxtFormat::GetParameter(
  	long 	*pParm,		//@parm Ptr to _CF or _PF member to get
  	DWORD	dwMask,		//@parm Nonzero iff defined
  	long	Type,		//@parm # bytes of integer parameter or 0 for float
  	long 	*pValue)		//@parm Out parm to receive value
  {
  	if (!pValue)
  	{
  		return E_INVALIDARG;
  	}
  
  	Assert(pParm && sizeof(long) == 4 && sizeof(float) == 4);
  
  	HRESULT hr = NOERROR;
  
  	if (IsZombie())							// Check if attached to zombied
  	{
  		//  range
  		dwMask = 0;							// Nothing defined
  		hr = CO_E_RELEASED;					// Setup return for following if
  	}
  
  	if (!dwMask)								// Parameter isn't defined
  	{
  		if (Type < 0)
  		{
  			*(float *)pValue = (float)tomUndefined;
  		}
  		else
  		{
  			*pValue = tomUndefined;
  		}
  		return hr;
  	}
  
  	long Value = *pParm;					// Default long value (Type = 4)
  	switch (Type)							// Handle other cases
  	{
  	case 1:									// BYTE quantity
  		Value &= 0xff;
  		break;
  
  	case 2:									// WORD quantity
  		Value &= 0xffff;
  		if (dwMask == CFM_STYLE ||
  			dwMask == PFM_STYLE)
  		{
  			Value = *(SHORT *)pParm;		// Styles need sign extension
  		}
  		break;
  
  	case -2:								// float from WORD or SHORT
  		Value &= 0xffff;					// Kill stuff above 16-bit data
  		if (dwMask == CFM_SPACING)			// Need sign extension for this
  		{
  			Value = *(SHORT *)pParm;    // Fall thru to case -4
  		}
  
  	case -4:								// float value
  		*(float *)&Value = TWIPS_TO_FPPTS(Value);
  	}
  
  	*pValue = Value;						// In all cases, return in a long
  	return NOERROR;
  }
  
  /*
   *	CTxtFormat::SetParameter (pParm, fDefined, Type, Value)
   *
   *	@mfunc
   *		Set *pParm = Value unless NINCHed. Perform conversions specified by
   *		Type.  If Type > 0, it is treated as length of an unsigned integer,
   *		i.e., 1, 2, or 4 bytes.  If it is negative, the output is a float
   *		and the input has a length given by |Type|, so -2 converts a WORD
   *		into a float, unless dwMask = CFM_SPACING, in which case it converts
   *		a short into a float.  -4 converts a long into a float.
   *
   *	@rdesc
   *		HRESULT = (if success) ? NOERROR : S_FALSE
   */
  HRESULT	CTxtFormat::SetParameter(
  	long 	*pParm,		//@parm Ptr to _CF or _PF member to set
  	long	Type,		//@parm # bytes of integer parameter or < 0 for float
  	long 	Value)		//@parm New value for *pParm
  {
  	Assert(pParm);
  
  	if (IsZombie())									// Check if attached to
  	{
  		return CO_E_RELEASED;    //  zombied range
  	}
  
  	if (Type > 0 && Value == tomUndefined)			// Undefined parameter
  	{
  		return NOERROR;    // NINCH
  	}
  
  	if (Type < 0)									// Value is a float
  	{
  		if (*(float *)&Value == (float)tomUndefined)	// Undefined parameter
  		{
  			return NOERROR;    // NINCH
  		}
  		Type = -Type;
  		Value = FPPTS_TO_TWIPS(*(float *)&Value);	// Convert to a long
  	}
  
  	if (Type == 1)
  	{
  		if ((DWORD)Value > 255)
  		{
  			return E_INVALIDARG;
  		}
  		*(BYTE *)pParm = (BYTE)Value;
  	}
  	else if (Type == 2)
  	{
  		if (Value < -32768 || Value > 65535)
  		{
  			return E_INVALIDARG;    // Doesn't fit in 16 bits
  		}
  		*(WORD *)pParm = (WORD)Value;
  	}
  	else
  	{
  		*pParm = Value;
  	}
  
  	return NOERROR;
  }
  
  /*
   *	CTxtFormat::IsTrue (f, pB)
   *
   *	@mfunc
   *		Return *<p pB> = tomTrue iff <p f> is nonzero and pB isn't NULL
   *
   *	@rdesc
   *		HRESULT = (f) ? NOERROR : S_FALSE
   */
  HRESULT CTxtFormat::IsTrue(BOOL f, long *pB)
  {
  	if (pB)
  	{
  		*pB = tomFalse;
  	}
  
  	if (IsZombie())									// Check if attached to
  	{
  		return CO_E_RELEASED;    //  zombied range
  	}
  
  	if (f)
  	{
  		if (pB)
  		{
  			*pB = tomTrue;
  		}
  		return NOERROR;
  	}
  
  	return S_FALSE;
  }
  
  