/* AbiWord
 * Copyright (C) 1998 AbiSource, Inc.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 * 02111-1307, USA.
 */

/******************************************************************
** This file is considered private to ie_exp_RTF.cpp
** This is a PL_Listener.  It's purpose is to actually write
** the contents of the document to the RTF file.
******************************************************************/

#include <string.h>
#include <stdlib.h>
#include <time.h>

#include "ut_rand.h"
#include "ut_locale.h"
#include "ut_debugmsg.h"
#include "ut_string.h"
#include "ut_units.h"
#include "ut_png.h"
#include "ut_bytebuf.h"
#include "ut_math.h"
#include "ie_exp_RTF_listenerWriteDoc.h"
#include "ie_exp_RTF_AttrProp.h"
#include "pd_Document.h"
#include "pd_Style.h"
#include "pf_Frag_Strux.h"
#include "xap_App.h"
#include "pp_AttrProp.h"
#include "pp_Property.h"
#include "px_ChangeRecord.h"
#include "px_CR_Object.h"
#include "px_CR_Span.h"
#include "px_CR_Strux.h"
#include "pt_Types.h"
#include "fl_AutoNum.h"
#include "fl_AutoLists.h"
#include "fl_BlockLayout.h"
#include "fp_Run.h"
#include "fl_Layout.h"
#include "fl_TableLayout.h"
#include "fl_FrameLayout.h"
#include "ut_rand.h"

#include "xap_EncodingManager.h"
#include "ut_string_class.h"

static UT_sint32 convertInchToTwips(double inch)
{
	return static_cast<UT_sint32>(inch*1440.0 +0.5);
}


static UT_sint32 convertTwipsToEMU(UT_sint32 twip)
{
	return static_cast<UT_sint32>(914400.0*static_cast<double>(twip)/1440.0);
}

void s_RTF_ListenerWriteDoc::_closeSection(void)
{
	m_apiThisSection = 0;
	m_sdh = NULL;
	return;
}

void s_RTF_ListenerWriteDoc::_closeBlock(PT_AttrPropIndex  nextApi)
{
	if(!m_bInBlock)
		return;
	
	// first reset ie's char direciton info
	m_pie->setCharRTL(UT_BIDI_UNSET);
//
// Force the output of char properties for blank lines or list items.
//
	bool bInList = false;
	const XML_Char * szListid=NULL;
	const PP_AttrProp * pBlockAP = NULL;
	xxx_UT_DEBUGMSG(("SEVIOR: Close Block \n"));
	
	if(m_bInSpan)
	{
		_closeSpan();
	}

	if(m_sdh && m_pDocument->getStruxType(m_sdh) == PTX_Block)
  	{
//
// This is a blankline or list item
//
// output the character properties for this break.
//
		const PP_AttrProp * pSpanAP = NULL;
		m_pDocument->getSpanAttrProp(m_sdh,0,true,&pSpanAP);
		xxx_UT_DEBUGMSG(("SEVIOR: Close Block -open span \n"));
		_openSpan(m_apiThisBlock,pSpanAP);
	}

	m_bBlankLine = false;
	
	xxx_UT_DEBUGMSG(("Doing write par now \n"));
	m_pie->_rtf_keyword("par");
	_closeSpan();

	m_apiThisBlock = 0;
	m_sdh = NULL;
	return;
}

void s_RTF_ListenerWriteDoc::_closeSpan(void)
{
	if (!m_bInSpan)
		return;

	m_pie->_rtf_close_brace();
	m_bInSpan = false;
	return;
}

// Frame Background

static void s_background_properties (const XML_Char * pszBgStyle, const XML_Char * pszBgColor,
									 const XML_Char * pszBackgroundColor,
									 PP_PropertyMap::Background & background)
{
	if (pszBgStyle)
		{
			if (strcmp (pszBgStyle, "0") == 0)
				{
					background.m_t_background = PP_PropertyMap::background_none;
				}
			else if (strcmp (pszBgStyle, "1") == 0)
				{
					if (pszBgColor)
						{
							background.m_t_background = PP_PropertyMap::background_type (pszBgColor);
							if (background.m_t_background == PP_PropertyMap::background_solid)
								UT_parseColor (pszBgColor, background.m_color);
						}

				}
		}

	if (pszBackgroundColor)
		{
			background.m_t_background = PP_PropertyMap::background_type (pszBackgroundColor);
			if (background.m_t_background == PP_PropertyMap::background_solid)
				UT_parseColor (pszBackgroundColor, background.m_color);
		}
}

static void s_border_properties (const XML_Char * border_color, const XML_Char * border_style, const XML_Char * border_width,
								 const XML_Char * color, PP_PropertyMap::Line & line)
{
	/* frame-border properties:
	 * 
	 * (1) color      - defaults to value of "color" property
	 * (2) line-style - defaults to solid (in contrast to "none" in CSS)
	 * (3) thickness  - defaults to 1 layout unit (??, vs "medium" in CSS)
	 */
	line.reset ();

	PP_PropertyMap::TypeColor t_border_color = PP_PropertyMap::color_type (border_color);
	if (t_border_color)
		{
			line.m_t_color = t_border_color;
			if (t_border_color == PP_PropertyMap::color_color)
				UT_parseColor (border_color, line.m_color);
		}
	else if (color)
		{
			PP_PropertyMap::TypeColor t_color = PP_PropertyMap::color_type (color);

			line.m_t_color = t_color;
			if (t_color == PP_PropertyMap::color_color)
				UT_parseColor (color, line.m_color);
		}

	line.m_t_linestyle = PP_PropertyMap::linestyle_type (border_style);
	if (!line.m_t_linestyle)
		line.m_t_linestyle = PP_PropertyMap::linestyle_solid;

	line.m_t_thickness = PP_PropertyMap::thickness_type (border_width);
	if (line.m_t_thickness == PP_PropertyMap::thickness_length)
		{
			if (UT_determineDimension (border_width, (UT_Dimension)-1) == DIM_PX)
				{
					double thickness = UT_LAYOUT_RESOLUTION * UT_convertDimensionless (border_width);
					line.m_thickness = static_cast<UT_sint32>(thickness / UT_PAPER_UNITS_PER_INCH);
				}
			else
				line.m_thickness = convertInchToTwips(UT_convertToInches (border_width));

			if (!line.m_thickness)
				{
					double thickness = UT_LAYOUT_RESOLUTION;
					line.m_thickness = static_cast<UT_sint32>(thickness / UT_PAPER_UNITS_PER_INCH);
				}
		}
	else // ??
		{
			double thickness = UT_LAYOUT_RESOLUTION;
			line.m_thickness = static_cast<UT_sint32>(thickness / UT_PAPER_UNITS_PER_INCH);
		}
}

void s_RTF_ListenerWriteDoc::_writeSPNumProp(const char * prop, UT_sint32 val)
{
	m_pie->_rtf_open_brace();
	m_pie->_rtf_keyword("sp");
	m_pie->_rtf_open_brace();
	m_pie->_rtf_keyword("sn ");
	m_pie->write(prop);
	m_pie->_rtf_close_brace();
	m_pie->_rtf_open_brace();
	m_pie->_rtf_keyword("sv ");
	UT_UTF8String sTmp = UT_UTF8String_sprintf("%d",val);
	m_pie->write(sTmp.utf8_str());
	m_pie->_rtf_close_brace();
	m_pie->_rtf_close_brace();
}



/*!
 * OK export all the TOC properties in RTF format. 
 */
void s_RTF_ListenerWriteDoc::_writeTOC(PT_AttrPropIndex apiTOC)
{
//
// OK get TOC properties
//
	const PP_AttrProp * pSectionAP = NULL;
	m_pDocument->getAttrProp(apiTOC,&pSectionAP);
	m_pie->_rtf_open_brace();
	m_pie->_rtf_keyword("field");
	m_pie->_rtf_keyword("fdledit");
	m_pie->_rtf_open_brace();
	m_pie->_rtf_keyword("*");
	m_pie->_rtf_keyword("fldinst ");
	m_pie->_rtf_open_brace();
	m_pie->_rtf_keyword(" TOC ");
//
// For now just close it all up. Later we'll have to worry about exporting
// bookmarks and the text of each heading in the TOC
//
	m_pie->_rtf_close_brace();
	m_pie->_rtf_close_brace();
	m_pie->_rtf_close_brace();


	// I can't think of any properties we need for now.
	// If we need any later, we'll add them. -PL
	const XML_Char *pszTOCPID = NULL;
	if(!pSectionAP || !pSectionAP->getProperty("toc-id",pszTOCPID))
	{
	}
	else
	{
	}
#if 0
	m_sNumOff1 = "0.5in";
	m_sNumOff2 = "0.5in";
	m_sNumOff3 = "0.5in";
	m_sNumOff4 = "0.5in";
#endif


	const XML_Char *pszINDENT = NULL;
	if(!pSectionAP || !pSectionAP->getProperty("toc-indent1",pszINDENT))
	{
	}
	else
	{
	}
	pszINDENT = NULL;
	if(!pSectionAP || !pSectionAP->getProperty("toc-indent2",pszINDENT))
	{
	}
	else
	{
	}

	pszINDENT = NULL;
	if(!pSectionAP || !pSectionAP->getProperty("toc-indent3",pszINDENT))
	{
	}
	else
	{
	}

	pszINDENT = NULL;
	if(!pSectionAP || !pSectionAP->getProperty("toc-indent4",pszINDENT))
	{
	}
	else
	{
	}

	const XML_Char *pszTOCSRC = NULL;
	if(!pSectionAP || !pSectionAP->getProperty("toc-source-style1",pszTOCSRC))
	{
	}
	else
	{
	}
	pszTOCSRC = NULL;
	if(!pSectionAP || !pSectionAP->getProperty("toc-source-style2",pszTOCSRC))
	{
	}
	else
	{
	}
	pszTOCSRC = NULL;
	if(!pSectionAP || !pSectionAP->getProperty("toc-source-style3",pszTOCSRC))
	{
	}
	else
	{
	}
	pszTOCSRC = NULL;
	if(!pSectionAP || !pSectionAP->getProperty("toc-source-style4",pszTOCSRC))
	{
	}
	else
	{
	}
	const XML_Char * pszTOCDEST = NULL;
	if(!pSectionAP || !pSectionAP->getProperty("toc-dest-style1",pszTOCDEST))
	{
	}
	else
	{
	}
	pszTOCDEST = NULL;
	if(!pSectionAP || !pSectionAP->getProperty("toc-dest-style2",pszTOCDEST))
	{
	}
	else
	{
	}
	pszTOCDEST = NULL;
	if(!pSectionAP || !pSectionAP->getProperty("toc-dest-style3",pszTOCDEST))
	{
	}
	else
	{
	}
	pszTOCDEST = NULL;
	if(!pSectionAP || !pSectionAP->getProperty("toc-dest-style4",pszTOCDEST))
	{
	}
	else
	{
	}
	const XML_Char * pszTOCHEADING = NULL;
	if(!pSectionAP || !pSectionAP->getProperty("toc-heading",pszTOCHEADING))
	{
	}
	else
	{
	}

	const XML_Char * pszTOCHEADINGStyle = NULL;
	if(!pSectionAP || !pSectionAP->getProperty("toc-heading-style",pszTOCHEADINGStyle))
	{
	}
	else
	{
	}


	const XML_Char * pszTOCHASHEADING = NULL;
	if(!pSectionAP || !pSectionAP->getProperty("toc-has-heading",pszTOCHASHEADING))
	{
	}
	else
	{
		if(UT_stricmp(pszTOCHASHEADING,"1") == 0)
		{
		}
		else
		{
		}
	}
//
// TOC Label
//
	const XML_Char * pszTOCLABEL = NULL;
	if(!pSectionAP || !pSectionAP->getProperty("toc-has-label1",pszTOCLABEL))
	{
	}
	else
	{
		if(UT_stricmp(pszTOCLABEL,"1") == 0)
		{
		}
		else
		{
		}
	}
	pszTOCLABEL = NULL;
	if(!pSectionAP || !pSectionAP->getProperty("toc-has-label2",pszTOCLABEL))
	{
	}
	else
	{
		if(UT_stricmp(pszTOCLABEL,"1") == 0)
		{
		}
		else
		{
		}
	}
	pszTOCLABEL = NULL;
	if(!pSectionAP || !pSectionAP->getProperty("toc-has-label3",pszTOCLABEL))
	{
	}
	else
	{
		if(UT_stricmp(pszTOCLABEL,"1") == 0)
		{
		}
		else
		{
		}
	}
	pszTOCLABEL = NULL;
	if(!pSectionAP || !pSectionAP->getProperty("toc-has-label4",pszTOCLABEL))
	{
	}
	else
	{
		if(UT_stricmp(pszTOCLABEL,"1") == 0)
		{
		}
		else
		{
		}
	}
//
// TOC Label Inherits
//
	const XML_Char * pszTOCLABELINHERITS = NULL;
	if(!pSectionAP || !pSectionAP->getProperty("toc-label-inherits1",pszTOCLABELINHERITS))
	{
	}
	else
	{
		if(UT_stricmp(pszTOCLABELINHERITS,"1") == 0)
		{
		}
		else
		{
		}
	}
	pszTOCLABELINHERITS = NULL;
	if(!pSectionAP || !pSectionAP->getProperty("toc-label-inherits2",pszTOCLABELINHERITS))
	{
	}
	else
	{
		if(UT_stricmp(pszTOCLABELINHERITS,"1") == 0)
		{
		}
		else
		{
		}
	}
	pszTOCLABELINHERITS = NULL;
	if(!pSectionAP || !pSectionAP->getProperty("toc-label-inherits3",pszTOCLABELINHERITS))
	{
	}
	else
	{
		if(UT_stricmp(pszTOCLABELINHERITS,"1") == 0)
		{
		}
		else
		{
		}
	}
	pszTOCLABELINHERITS = NULL;
	if(!pSectionAP || !pSectionAP->getProperty("toc-label-inherits4",pszTOCLABELINHERITS))
	{
	}
	else
	{
		if(UT_stricmp(pszTOCLABELINHERITS,"1") == 0)
		{
		}
		else
		{
		}
	}
//
// TOC Label Type
//
	const XML_Char * pszTOCLABELTYPE = NULL;
	if(!pSectionAP || !pSectionAP->getProperty("toc-label-type1",pszTOCLABELTYPE))
	{
	}
	else
	{
	}
	pszTOCLABELTYPE = NULL;
	if(!pSectionAP || !pSectionAP->getProperty("toc-label-type2",pszTOCLABELTYPE))
	{
	}
	else
	{
	}
	pszTOCLABELTYPE = NULL;
	if(!pSectionAP || !pSectionAP->getProperty("toc-label-type3",pszTOCLABELTYPE))
	{
	}
	else
	{
	}
	pszTOCLABELTYPE = NULL;
	if(!pSectionAP || !pSectionAP->getProperty("toc-label-type4",pszTOCLABELTYPE))
	{
	}
	else
	{
	}
//
// TOC Label Before Text
//
	const XML_Char * pszTOCSTRBEFORE = NULL;
	if(!pSectionAP || !pSectionAP->getProperty("toc-label-before1",pszTOCSTRBEFORE))
	{
	}
	else
	{
	}
	pszTOCSTRBEFORE = NULL;
	if(!pSectionAP || !pSectionAP->getProperty("toc-label-before2",pszTOCSTRBEFORE))
	{
	}
	else
	{
	}
	pszTOCSTRBEFORE = NULL;
	if(!pSectionAP || !pSectionAP->getProperty("toc-label-before3",pszTOCSTRBEFORE))
	{
	}
	else
	{
	}
	pszTOCSTRBEFORE = NULL;
	if(!pSectionAP || !pSectionAP->getProperty("toc-label-before4",pszTOCSTRBEFORE))
	{
	}
	else
	{
	}
//
// TOC Label After Text
//
	const XML_Char * pszTOCSTRAFTER = NULL;
	if(!pSectionAP || !pSectionAP->getProperty("toc-label-after1",pszTOCSTRAFTER))
	{
	}
	else
	{
	}
	pszTOCSTRAFTER = NULL;
	if(!pSectionAP || !pSectionAP->getProperty("toc-label-after2",pszTOCSTRAFTER))
	{
	}
	else
	{
	}
	pszTOCSTRAFTER = NULL;
	if(!pSectionAP || !pSectionAP->getProperty("toc-label-after2",pszTOCSTRAFTER))
	{
	}
	else
	{
	}
	pszTOCSTRAFTER = NULL;
	if(!pSectionAP || !pSectionAP->getProperty("toc-label-after4",pszTOCSTRAFTER))
	{
	}
	else
	{
	}
//
// TOC Label Initial Value
//
	const XML_Char * pszTOCLABELSTART = NULL;
	if(!pSectionAP || !pSectionAP->getProperty("toc-label-start1",pszTOCLABELSTART))
	{
	}
	else
	{
	}
	pszTOCLABELSTART = NULL;
	if(!pSectionAP || !pSectionAP->getProperty("toc-label-start2",pszTOCLABELSTART))
	{
	}
	else
	{
	}
	pszTOCLABELSTART = NULL;
	if(!pSectionAP || !pSectionAP->getProperty("toc-label-start3",pszTOCLABELSTART))
	{
	}
	else
	{
	}
	pszTOCLABELSTART = NULL;
	if(!pSectionAP || !pSectionAP->getProperty("toc-label-start4",pszTOCLABELSTART))
	{
	}
	else
	{
	}
//
// TOC Page Number Type
//
	const XML_Char * pszTOCPAGETYPE = NULL;
	if(!pSectionAP || !pSectionAP->getProperty("toc-page-type1",pszTOCPAGETYPE))
	{
	}
	else
	{
	}
	pszTOCPAGETYPE = NULL;
	if(!pSectionAP || !pSectionAP->getProperty("toc-page-type2",pszTOCPAGETYPE))
	{
	}
	else
	{
	}
	pszTOCPAGETYPE = NULL;
	if(!pSectionAP || !pSectionAP->getProperty("toc-page-type3",pszTOCPAGETYPE))
	{
	}
	else
	{
	}
	pszTOCPAGETYPE = NULL;
	if(!pSectionAP || !pSectionAP->getProperty("toc-page-type4",pszTOCPAGETYPE))
	{
	}
	else
	{
	}
//
// TOC TAB leader
//
	const XML_Char * pszTOCTABTYPE = NULL;
	if(!pSectionAP || !pSectionAP->getProperty("toc-tab-leader1",pszTOCTABTYPE))
	{
	}
	else
	{
		if(UT_stricmp(pszTOCTABTYPE,"none") == 0)
		{
		}
		else if(UT_stricmp(pszTOCTABTYPE,"dot") == 0)
		{
		}
		else if(UT_stricmp(pszTOCTABTYPE,"hyphen") == 0)
		{
		}
		else if(UT_stricmp(pszTOCTABTYPE,"underline") == 0)
		{
		}
		else
		{
		}
	}
	pszTOCTABTYPE = NULL;
	if(!pSectionAP || !pSectionAP->getProperty("toc-tab-leader2",pszTOCTABTYPE))
	{
	}
	else
	{
		if(UT_stricmp(pszTOCTABTYPE,"none") == 0)
		{
		}
		else if(UT_stricmp(pszTOCTABTYPE,"dot") == 0)
		{
		}
		else if(UT_stricmp(pszTOCTABTYPE,"hyphen") == 0)
		{
		}
		else if(UT_stricmp(pszTOCTABTYPE,"underline") == 0)
		{
		}
		else
		{
		}
	}
	pszTOCTABTYPE = NULL;
	if(!pSectionAP || !pSectionAP->getProperty("toc-tab-leader3",pszTOCTABTYPE))
	{
	}
	else
	{
		if(UT_stricmp(pszTOCTABTYPE,"none") == 0)
		{
		}
		else if(UT_stricmp(pszTOCTABTYPE,"dot") == 0)
		{
		}
		else if(UT_stricmp(pszTOCTABTYPE,"hyphen") == 0)
		{
		}
		else if(UT_stricmp(pszTOCTABTYPE,"underline") == 0)
		{
		}
		else
		{
		}
	}
	pszTOCTABTYPE = NULL;
	if(!pSectionAP || !pSectionAP->getProperty("toc-tab-leader4",pszTOCTABTYPE))
	{
	}
	else
	{
		if(UT_stricmp(pszTOCTABTYPE,"none") == 0)
		{
		}
		else if(UT_stricmp(pszTOCTABTYPE,"dot") == 0)
		{
		}
		else if(UT_stricmp(pszTOCTABTYPE,"hyphen") == 0)
		{
		}
		else if(UT_stricmp(pszTOCTABTYPE,"underline") == 0)
		{
		}
		else
		{
		}
	}

	pszTOCTABTYPE = NULL;
	if(pSectionAP && pSectionAP->getProperty("toc-range-bookmark",pszTOCTABTYPE))
	{
	}
	else
	{
	}

}

/*!
 * OK export all the frame properties in RTF format. Use the \shp definitions
 * for this.
 */
void s_RTF_ListenerWriteDoc::_openFrame(PT_AttrPropIndex apiFrame)
{
	if(m_bInFrame)
	{
		UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
		return;
	}
//
// OK get frame properties
//
	const PP_AttrProp * pSectionAP = NULL;
	m_pDocument->getAttrProp(apiFrame,&pSectionAP);
	m_apiThisFrame = apiFrame;
	m_bInFrame = true;

	const XML_Char *pszFrameType = NULL;
	const XML_Char *pszWrapMode = NULL;
	const XML_Char *pszPositionTo = NULL;
	const XML_Char *pszXpos = NULL;
	const XML_Char *pszYpos = NULL;
	const XML_Char *pszColXpos = NULL;
	const XML_Char *pszColYpos = NULL;
	const XML_Char *pszPageXpos = NULL;
	const XML_Char *pszPageYpos = NULL;
	const XML_Char *pszWidth = NULL;
	const XML_Char *pszHeight = NULL;
	const XML_Char *pszXpad = NULL;
	const XML_Char *pszYpad = NULL;

	const XML_Char * pszColor = NULL;
	const XML_Char * pszBorderColor = NULL;
	const XML_Char * pszBorderStyle = NULL;
	const XML_Char * pszBorderWidth = NULL;
	
	FL_FrameType iFrameType = FL_FRAME_TEXTBOX_TYPE;
	FL_FrameFormatMode iFramePositionTo = FL_FRAME_POSITIONED_TO_BLOCK;
	FL_FrameWrapMode iFrameWrapMode = FL_FRAME_ABOVE_TEXT;
	UT_sint32 iXpos = convertInchToTwips(UT_convertToInches("0.0in"));
	UT_sint32 iYpos = convertInchToTwips(UT_convertToInches("0.0in"));
	UT_sint32 iXColumn = convertInchToTwips(UT_convertToInches("0.0in"));
	UT_sint32 iYColumn = convertInchToTwips(UT_convertToInches("0.0in"));
	UT_sint32 iXPage = convertInchToTwips(UT_convertToInches("0.0in"));
	UT_sint32 iYPage = convertInchToTwips(UT_convertToInches("0.0in"));
	UT_sint32 iWidth = convertInchToTwips(UT_convertToInches("1.0in"));
	UT_sint32 iHeight = convertInchToTwips(UT_convertToInches("1.0in"));
	UT_sint32 iXpad = convertInchToTwips(UT_convertToInches("0.03in"));
	UT_sint32 iYpad = convertInchToTwips(UT_convertToInches("0.03in"));

	PP_PropertyMap::Line leftLine;
	PP_PropertyMap::Line rightLine;
	PP_PropertyMap::Line topLine;
	PP_PropertyMap::Line botLine;
	PP_PropertyMap::Background  background;
				
// Frame Type

	if(!pSectionAP || !pSectionAP->getProperty("frame-type",pszFrameType))
	{
		iFrameType = FL_FRAME_TEXTBOX_TYPE;
	}
	else if(strcmp(pszFrameType,"textbox") == 0)
	{
		iFrameType = FL_FRAME_TEXTBOX_TYPE;
	}
	else if(strcmp(pszFrameType,"image") == 0)
	{
		iFrameType = FL_FRAME_WRAPPER_IMAGE;
	}
	else 
	{
		UT_DEBUGMSG(("Unknown Frame Type %s \n",pszFrameType));
		UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
		iFrameType = FL_FRAME_TEXTBOX_TYPE;
	}

// Position-to

	if(!pSectionAP || !pSectionAP->getProperty("position-to",pszPositionTo))
	{
		iFramePositionTo = FL_FRAME_POSITIONED_TO_BLOCK;
	}
	else if(strcmp(pszPositionTo,"block-above-text") == 0)
	{
		iFramePositionTo = FL_FRAME_POSITIONED_TO_BLOCK;
	}
	else if(strcmp(pszPositionTo,"column-above-text") == 0)
	{
		iFramePositionTo = FL_FRAME_POSITIONED_TO_COLUMN;
	}
	else if(strcmp(pszPositionTo,"page-above-text") == 0)
	{
		iFramePositionTo = FL_FRAME_POSITIONED_TO_PAGE;
	}
	else 
	{
		UT_DEBUGMSG(("Unknown Position to %s \n",pszPositionTo));
		UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
		iFramePositionTo =  FL_FRAME_POSITIONED_TO_BLOCK;
	}

// wrap-mode

	if(!pSectionAP || !pSectionAP->getProperty("wrap-mode",pszWrapMode))
	{
		iFrameWrapMode = FL_FRAME_ABOVE_TEXT;
	}
	else if(strcmp(pszWrapMode,"above-text") == 0)
	{
		iFrameWrapMode = FL_FRAME_ABOVE_TEXT;
	}
	else if(strcmp(pszWrapMode,"below-text") == 0)
	{
		iFrameWrapMode = FL_FRAME_BELOW_TEXT;
	}
	else if(strcmp(pszWrapMode,"wrapped-to-right") == 0)
	{
		iFrameWrapMode = FL_FRAME_WRAPPED_TO_RIGHT;
	}
	else if(strcmp(pszWrapMode,"wrapped-to-left") == 0)
	{
		iFrameWrapMode = FL_FRAME_WRAPPED_TO_LEFT;
	}
	else if(strcmp(pszWrapMode,"wrapped-both") == 0)
	{
		iFrameWrapMode = FL_FRAME_WRAPPED_BOTH_SIDES;
	}
	else 
	{
		UT_DEBUGMSG(("Unknown wrap-mode %s \n",pszWrapMode));
		UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
		iFrameWrapMode = FL_FRAME_ABOVE_TEXT;
	}

// Xpos

	if(!pSectionAP || !pSectionAP->getProperty("xpos",pszXpos))
	{
		iXpos = convertInchToTwips(UT_convertToInches("0.0in"));
	}
	else
	{
		iXpos = convertInchToTwips(UT_convertToInches(pszXpos));
	}
	UT_DEBUGMSG(("xpos for frame is %s \n",pszXpos));
// Ypos

	if(!pSectionAP || !pSectionAP->getProperty("ypos",pszYpos))
	{
		iYpos = convertInchToTwips(UT_convertToInches("0.0in"));
	}
	else
	{
		iYpos = convertInchToTwips(UT_convertToInches(pszYpos));
	}
	UT_DEBUGMSG(("ypos for frame is %s \n",pszYpos));

// ColXpos

	if(!pSectionAP || !pSectionAP->getProperty("frame-col-xpos",pszColXpos))
	{
		iXColumn = convertInchToTwips(UT_convertToInches("0.0in"));
	}
	else
	{
		iXColumn = convertInchToTwips(UT_convertToInches(pszColXpos));
	}
	UT_DEBUGMSG(("ColXpos for frame is %s \n",pszColXpos));

// colYpos

	if(!pSectionAP || !pSectionAP->getProperty("frame-col-ypos",pszColYpos))
	{
		iYColumn = convertInchToTwips(UT_convertToInches("0.0in"));
	}
	else
	{
		iYColumn = convertInchToTwips(UT_convertToInches(pszColYpos));
	}
	UT_DEBUGMSG(("ColYpos for frame is %s units %d \n",pszColYpos,iYColumn));


// PageXpos

	if(!pSectionAP || !pSectionAP->getProperty("frame-page-xpos",pszPageXpos))
	{
		iXPage = convertInchToTwips(UT_convertToInches("0.0in"));
	}
	else
	{
		iXPage = convertInchToTwips(UT_convertToInches(pszPageXpos));
	}
	UT_DEBUGMSG(("PageXpos for frame is %s \n",pszPageXpos));
// PageYpos

	if(!pSectionAP || !pSectionAP->getProperty("frame-page-ypos",pszPageYpos))
	{
		iYPage = UT_convertToLogicalUnits("0.0in");
	}
	else
	{
		iYPage = UT_convertToLogicalUnits(pszPageYpos);
	}
	UT_DEBUGMSG(("PageYpos for frame is %s units %d \n",pszColYpos,iYPage));

// Width

	if(!pSectionAP || !pSectionAP->getProperty("frame-width",pszWidth))
	{
		iWidth = convertInchToTwips(UT_convertToInches("1.0in"));
	}
	else
	{
		iWidth = convertInchToTwips(UT_convertToInches(pszWidth));
	}

// Height

	if(!pSectionAP || !pSectionAP->getProperty("frame-height",pszHeight))
	{
		iHeight = convertInchToTwips(UT_convertToInches("1.0in"));
	}
	else
	{
		iHeight = convertInchToTwips(UT_convertToInches(pszHeight));
	}

// Xpadding


	if(!pSectionAP || !pSectionAP->getProperty("xpad",pszXpad))
	{
		iXpad = convertInchToTwips(UT_convertToInches("0.03in"));
	}
	else
	{
		iXpad = convertInchToTwips(UT_convertToInches(pszXpad));
	}


// Ypadding


	if(!pSectionAP || !pSectionAP->getProperty("ypad",pszYpad))
	{
		iYpad = convertInchToTwips(UT_convertToInches("0.03in"));
	}
	else
	{
		iYpad = convertInchToTwips(UT_convertToInches(pszYpad));
	}


	/* Frame-border properties:
	 */

	pSectionAP->getProperty ("color", pszColor);

	pSectionAP->getProperty ("bot-color",pszBorderColor);
	pSectionAP->getProperty ("bot-style",pszBorderStyle);
	pSectionAP->getProperty ("bot-thickness",pszBorderWidth);

	s_border_properties (pszBorderColor, pszBorderStyle, pszBorderWidth, pszColor, botLine);

	pszBorderColor = NULL;
	pszBorderStyle = NULL;
	pszBorderWidth = NULL;

	pSectionAP->getProperty ("left-color", pszBorderColor);
	pSectionAP->getProperty ("left-style", pszBorderStyle);
	pSectionAP->getProperty ("left-thickness", pszBorderWidth);

	s_border_properties (pszBorderColor, pszBorderStyle, pszBorderWidth, pszColor, leftLine);

	pszBorderColor = NULL;
	pszBorderStyle = NULL;
	pszBorderWidth = NULL;

	pSectionAP->getProperty ("right-color",pszBorderColor);
	pSectionAP->getProperty ("right-style",pszBorderStyle);
	pSectionAP->getProperty ("right-thickness", pszBorderWidth);

	s_border_properties (pszBorderColor, pszBorderStyle, pszBorderWidth, pszColor, rightLine);

	pszBorderColor = NULL;
	pszBorderStyle = NULL;
	pszBorderWidth = NULL;

	pSectionAP->getProperty ("top-color",  pszBorderColor);
	pSectionAP->getProperty ("top-style",  pszBorderStyle);
	pSectionAP->getProperty ("top-thickness",pszBorderWidth);

	s_border_properties (pszBorderColor, pszBorderStyle, pszBorderWidth, pszColor, topLine);

	/* Frame fill
	 */
	background.reset ();

	const XML_Char * pszBgStyle = NULL;
	const XML_Char * pszBgColor = NULL;
	const XML_Char * pszBackgroundColor = NULL;

	pSectionAP->getProperty ("bg-style",    pszBgStyle);
	pSectionAP->getProperty ("bgcolor",     pszBgColor);
	pSectionAP->getProperty ("background-color", pszBackgroundColor);

	s_background_properties (pszBgStyle, pszBgColor, pszBackgroundColor, background);

//
// OK got all the props of the frame.
//
	m_pie->_rtf_open_brace();
	m_pie->_rtf_keyword("shp");
	m_pie->_rtf_open_brace();
	m_pie->_rtf_keyword("*");
	m_pie->_rtf_keyword("shpinst");
	if( iFramePositionTo == FL_FRAME_POSITIONED_TO_BLOCK)
	{
		m_pie->_rtf_keyword("shpz",0); // All at z= 0;
		m_pie->_rtf_keyword("shpbxmargin");
		m_pie->_rtf_keyword("shpbypara"); // position relative to next paragraph
		if(iFrameWrapMode <= FL_FRAME_BELOW_TEXT)
		{
			m_pie->_rtf_keyword("shpwr",3); // no text wrapping
			m_pie->_rtf_keyword("shpfblwtxt",0); // text below frame
		}
		else if(iFrameWrapMode > FL_FRAME_BELOW_TEXT)
		{
			m_pie->_rtf_keyword("shpwr",2); // text wrapping
			m_pie->_rtf_keyword("shpwrk",0); // text wrap both sides
			m_pie->_rtf_keyword("shpfblwtxt",0); // text below frame
		}
	}
	else if( iFramePositionTo == FL_FRAME_POSITIONED_TO_COLUMN)
	{
		iXpos = iXColumn;
		iYpos = iYColumn;
		m_pie->_rtf_keyword("shpz",0); // All at z= 0;
		m_pie->_rtf_keyword("shpbxmargin");
		m_pie->_rtf_keyword("shpbymargin"); // position relative to margin
		if(iFrameWrapMode <= FL_FRAME_BELOW_TEXT)
		{
			m_pie->_rtf_keyword("shpwr",3); // no text wrapping
			m_pie->_rtf_keyword("shpfblwtxt",0); // text below frame
		}
		else if(iFrameWrapMode >= FL_FRAME_BELOW_TEXT)
		{
			m_pie->_rtf_keyword("shpwr",2); // text wrapping
			m_pie->_rtf_keyword("shpwrk",0); // text wrap both sides
			m_pie->_rtf_keyword("shpfblwtxt",0); // text below frame
		}
	}
	else if( iFramePositionTo == FL_FRAME_POSITIONED_TO_PAGE)
	{
		iXpos = iXPage;
		iYpos = iYPage;
		m_pie->_rtf_keyword("shpz",0); // All at z= 0;
		m_pie->_rtf_keyword("shpbxmargin");
		m_pie->_rtf_keyword("shpbypage"); // position relative to page

		if(iFrameWrapMode <= FL_FRAME_BELOW_TEXT)
		{
			m_pie->_rtf_keyword("shpwr",3); // no text wrapping
			m_pie->_rtf_keyword("shpfblwtxt",0); // text below frame
		}
		else if(iFrameWrapMode >= FL_FRAME_BELOW_TEXT)
		{
			m_pie->_rtf_keyword("shpwr",2); // text wrapping
			m_pie->_rtf_keyword("shpwrk",0); // text wrap both sides
			m_pie->_rtf_keyword("shpfblwtxt",0); // text below frame
		}
	}
	else
	{
		UT_ASSERT_HARMLESS(UT_NOT_IMPLEMENTED);
		m_pie->_rtf_keyword("shpz",0); // All at z= 0;
		m_pie->_rtf_keyword("shpbypara");
		m_pie->_rtf_keyword("shpwr",3);
		m_pie->_rtf_keyword("shpbxmargin");
        m_pie->_rtf_keyword("shpfblwtxt",0);
	}
	m_pie->_rtf_keyword("shpleft",iXpos);
	m_pie->_rtf_keyword("shptop",iYpos);
	UT_sint32 iRight = iXpos + iWidth;
	UT_sint32 iBot = iYpos + iHeight;
	m_pie->_rtf_keyword("shpbottom",iBot);
	m_pie->_rtf_keyword("shpright",iRight);
	UT_uint32 lid = UT_rand();
	m_pie->_rtf_keyword("shplid",lid);


// OK Shape properties now

	if(iFrameType == FL_FRAME_TEXTBOX_TYPE)
	{
		_writeSPNumProp("shapeType",202); // Textbox

		if(background.m_t_background != PP_PropertyMap::background_none)
		{
			UT_RGBColor color = background.m_color;
			UT_sint32 iCol = color.m_red+color.m_grn*256+color.m_blu*256*256;
			if(iCol != 0)
			{
				_writeSPNumProp("fillColor",iCol); // Background color
				_writeSPNumProp("fillType",0); // solid color
			}
		}
	}
	else
	{
//
// Image name
//
		const XML_Char * pszDataID = NULL;
		pSectionAP->getAttribute(PT_STRUX_IMAGE_DATAID, (const XML_Char *&)pszDataID);
		if(pszDataID != NULL)
		{
			const UT_ByteBuf * pbb = NULL;
			const void * pToken = NULL;
			void * pHandle = NULL;
			bool bFoundDataItem = m_pDocument->getDataItemDataByName(static_cast<const char*>(pszDataID),&pbb,&pToken,&pHandle);
			if (!bFoundDataItem)
			{
				UT_DEBUGMSG(("RTF_Export: cannot get dataitem for image\n"));
				return;
			}

			// get the width/height of the image from the image itself.

			UT_sint32 iImageWidth, iImageHeight;
			UT_PNG_getDimensions(pbb,iImageWidth,iImageHeight);

	// compute scale factors...

			double dImageWidth = static_cast<double>(iImageWidth);
			double dImageHeight = static_cast<double>(iImageHeight);
			dImageWidth = UT_convertDimToInches(dImageWidth,DIM_PT);
			dImageHeight = UT_convertDimToInches(dImageHeight,DIM_PT);
			_writeSPNumProp("shapeType",75);  // Image

// OK the sp stuff for the image inside the "pib" tag.
//
			xxx_UT_DEBUGMSG(("export frame image braceLevel %d \n",m_pie->m_braceLevel));
			m_pie->_rtf_open_brace();
			m_pie->_rtf_keyword("sp");
			m_pie->_rtf_open_brace();
			m_pie->_rtf_keyword("sn ");
			m_pie->write("pib");
			m_pie->_rtf_close_brace();
			m_pie->_rtf_open_brace();
			m_pie->_rtf_keyword("sv ");


			m_pie->_rtf_open_brace();
			{
				m_pie->_rtf_keyword("pict");
				
				m_pie->_rtf_keyword("pngblip");
				
				// <pictsize>

				m_pie->_rtf_keyword("picw",iImageWidth);
				m_pie->_rtf_keyword("pich",iImageHeight);

				m_pie->_rtf_keyword("picwgoal",iWidth);
				double dWidth = static_cast<double>(iWidth)/1440.;
				double scalex = 100.0*dWidth/dImageWidth;
				UT_uint32 iscalex = static_cast<UT_uint32>(scalex);
				m_pie->_rtf_keyword("picscalex",iscalex);
					
				m_pie->_rtf_keyword("pichgoal",iHeight);
				double dHeight = static_cast<double>(iHeight)/1440.;
				double scaley = 100.0*dHeight/dImageHeight;
				UT_uint32 iscaley = static_cast<UT_uint32>(scaley);
				m_pie->_rtf_keyword("picscaley",iscaley);

				// TODO deal with <metafileinfo>
				
				// <data>

				// TODO create meaningful values for bliptag and bliduid...
				// we emit "\bliptag<N>{\*\blipuid <N16>}"
				// where <N> is an integer.
				// where <N16> is a 16-byte integer in hex.

				m_pie->_rtf_nl();
				UT_uint32 tag = UT_newNumber ();
				m_pie->_rtf_keyword("bliptag",tag);
				m_pie->_rtf_open_brace();
				{
					m_pie->_rtf_keyword("*");
					m_pie->_rtf_keyword("blipuid");
					UT_String buf;
					UT_String_sprintf(buf,"%032x",tag);
					m_pie->_rtf_chardata(buf.c_str(),buf.size());
				}
				m_pie->_rtf_close_brace();
			}

			UT_uint32 lenData = pbb->getLength();
			const UT_Byte * pData = pbb->getPointer(0);
			UT_uint32 k;

			for (k=0; k<lenData; k++)
			{
				if (k%32==0)
					m_pie->_rtf_nl();
				UT_String buf;
				UT_String_sprintf(buf,"%02x",pData[k]);
				m_pie->_rtf_chardata(buf.c_str(),2);
			}
			m_pie->_rtf_close_brace();  // close pict
			m_pie->_rtf_close_brace(); // close sv
			m_pie->_rtf_close_brace(); // close sp
			xxx_UT_DEBUGMSG(("finish export frame image braceLevel %d \n",m_pie->m_braceLevel));
		}

	}

	_writeSPNumProp("dxTextLeft",convertTwipsToEMU(iXpad));
	_writeSPNumProp("dxTextRight",convertTwipsToEMU(iXpad));
	_writeSPNumProp("dxTextTop",convertTwipsToEMU(iYpad));
	_writeSPNumProp("dxTextBottom",convertTwipsToEMU(iYpad));
	m_bTextBox = false;

	if(iFrameType == FL_FRAME_TEXTBOX_TYPE)
	{
		m_pie->_rtf_open_brace();
        m_pie->_rtf_keyword("shptxt"); // Is a text box
		m_bTextBox = true;
	}
	m_bInSpan = false;
	m_bJustOpennedFrame = true;
}



void s_RTF_ListenerWriteDoc::_closeFrame(void)
{
	if(!m_bInFrame) // Can happen dragging frames around
	{
		return;
	}
	m_pie->_rtf_close_brace();
	m_pie->_rtf_close_brace();
	if(m_bTextBox)
	{
		m_pie->_rtf_close_brace();
	}
	m_bInFrame = false;
	m_bJustOpennedFrame = false;
}

void s_RTF_ListenerWriteDoc::_openSpan(PT_AttrPropIndex apiSpan,  const PP_AttrProp * pInSpanAP)
{
	if (m_bInSpan)
	{
		if (m_apiLastSpan == apiSpan)
			return;
		_closeSpan();
	}

	m_pie->_rtf_open_brace();

	const PP_AttrProp * pSpanAP = NULL;
	const PP_AttrProp * pBlockAP = NULL;
	const PP_AttrProp * pSectionAP = NULL;
    
    const PP_AttrProp * pDeepestAP = NULL; // the one that is the most local
    
    bool bHaveSpanProps = 0;
	bool bHaveSectionProps = m_pDocument->getAttrProp(m_apiThisSection,&pSectionAP);
    bool bHaveBlockProps = m_pDocument->getAttrProp(m_apiThisBlock,&pBlockAP);
	if(pInSpanAP == NULL)
	{
        bHaveSpanProps = m_pDocument->getAttrProp(apiSpan,&pSpanAP);
	}
	else
	{
		pSpanAP = pInSpanAP;
	}

    if (bHaveSpanProps && (0 != pSpanAP)) 
	{
        pDeepestAP = pSpanAP;
    }
    else if (bHaveBlockProps  && (0 != pBlockAP)) 
	{
        pDeepestAP = pBlockAP;
    }
    else if (bHaveSectionProps && (0 != pSectionAP)) 
	{
        pDeepestAP = pSectionAP;
    }
    
    if (NULL != pDeepestAP) 
	{
		const XML_Char * styleSzValue = 0;
		bool have_style  = pDeepestAP->getAttribute (PT_STYLE_ATTRIBUTE_NAME, 
													 styleSzValue);
        if (!have_style) 
		{
            if (bHaveBlockProps && (0 != pBlockAP)) 
			{
                have_style = pBlockAP->getAttribute (PT_STYLE_ATTRIBUTE_NAME, 
													 styleSzValue);
            }
        }
        if (!have_style) 
		{
            if (bHaveSectionProps && (0 != pSectionAP)) 
			{
                have_style = pSectionAP->getAttribute (PT_STYLE_ATTRIBUTE_NAME, 													   styleSzValue);
            }
        }
        
        if (have_style) 
		{
            int styleID = m_pie->_getStyleNumber(styleSzValue);
            char* styleType = "s";
            
            //get the style from the styleName
            PD_Style* pStyle = NULL;
			m_pDocument->getStyle(styleSzValue,&pStyle);
            UT_ASSERT(pStyle);
            if (pStyle && pStyle->isCharStyle()) 
			{
                styleType = "cs";
            }
            m_pie->_rtf_keyword(styleType, styleID);
        }
    }
   
	m_pie->_write_charfmt(s_RTF_AttrPropAdapter_AP(pSpanAP, pBlockAP, pSectionAP, m_pDocument));
	m_bBlankLine = false;
	m_bInSpan = true;
	m_apiLastSpan = apiSpan;
}

void s_RTF_ListenerWriteDoc::_outputData(const UT_UCSChar * data, UT_uint32 length, PT_DocPosition pos, bool bIgnorePosition)
{
	UT_String sBuf;
	const UT_UCSChar * pData;
	char mbbuf[30];
	int mblen;

	#define FlushBuffer() do {m_pie->_rtf_chardata(sBuf.c_str(), sBuf.size()); sBuf.clear();} while (0)

	UT_return_if_fail(sizeof(UT_Byte) == sizeof(char));

	for (pData=data; (pData<data+length); /**/)
	{
		// first handle direciton issues
		UT_BidiCharType type;

		if(  !bIgnorePosition
		   && m_pDocument->exportGetVisDirectionAtPos(pos + (pData - data),type)
		  )
		{
			if(m_pie->isCharRTL() != UT_BIDI_LTR && !FRIBIDI_IS_RTL(type))
			{
				// changing from rtl to ltr
				FlushBuffer();

// when reading this rtf back into AW, we do not want
// the ltrch converted into an override -- issue
// custom abinodiroverride keyword

				m_pie->_rtf_keyword("abinodiroverride"); 
				m_pie->_rtf_keyword("ltrch");
				m_pie->setCharRTL(UT_BIDI_LTR);
			}
			else if(m_pie->isCharRTL() != UT_BIDI_RTL && FRIBIDI_IS_RTL(type))
			{
				// changing from ltr to rtl
				FlushBuffer();

// when reading this rtf back into AW, we do not want
// the ltrch converted into an override -- issue
// custom abinodiroverride keyword

				m_pie->_rtf_keyword("abinodiroverride"); 
				m_pie->_rtf_keyword("rtlch");
				m_pie->setCharRTL(UT_BIDI_RTL);
			}
	   }
			
		switch (*pData)
		{
		case '\\':
		case '{':
		case '}':
			sBuf += '\\';
			sBuf += (char)*pData++;
			break;

		case UCS_LF:					// LF -- representing a Forced-Line-Break
			FlushBuffer();
			m_pie->_rtf_keyword("line");
			pData++;
			break;

		case UCS_VTAB:					// VTAB -- representing a Forced-Column-Break
			FlushBuffer();
			m_pie->_rtf_keyword("column");
			pData++;
			break;

		case UCS_FF:					// FF -- representing a Forced-Page-Break
			FlushBuffer();
			m_pie->_rtf_keyword("page");
			pData++;
			break;

		case UCS_NBSP:					// NBSP -- non breaking space
			FlushBuffer();
			m_pie->_rtf_keyword("~");
			m_pie->m_bLastWasKeyword = false;       // no space needed afterward
			
			pData++;
			break;

		case UCS_TAB:					// TAB -- a tab
			FlushBuffer();
			m_pie->_rtf_keyword("tab");
			pData++;
			break;

		default:
			// remove supperfluous direction markers ...
			if(*pData == UCS_LRM && m_pie->isCharRTL() == UT_BIDI_LTR)
			{
				pData++;
				continue;
			}
			else if(*pData == UCS_RLM && m_pie->isCharRTL() == UT_BIDI_RTL)
			{
				pData++;
				continue;
			}
		
			
			if (XAP_EncodingManager::get_instance()->cjk_locale())
			{
				/*FIXME: can it happen that wctomb will fail under CJK locales? */
				m_wctomb.wctomb_or_fallback(mbbuf,mblen,*pData++);
				if (mbbuf[0] & 0x80)
				{
					FlushBuffer();
					for(int i=0;i<mblen;++i) {
						unsigned char c = mbbuf[i];
						m_pie->_rtf_nonascii_hex2(c);
					}
				}
				else
				{
					for(int i=0;i<mblen;++i) {
						switch (mbbuf[i])
						{
							case '\\':
							case '{':
							case '}':
								sBuf += '\\';
						}
						sBuf += mbbuf[i];
					}
				}
			} else if (!m_pie->m_atticFormat)
			{
				if (*pData > 0x00ff)		// emit unicode character
				{
					FlushBuffer();

					// RTF spec says that we should emit an ASCII-equivalent
					// character for each unicode character, so that dumb/older
					// readers don't lose a char.  i don't have a good algorithm
					// for deciding how to do this, so i'm not going to put out
					// any chars.  so i'm setting \uc0 before emitting \u<u>.
					// TODO decide if we should be smarter here and do a \uc1\u<u><A> ??
					// TODO if so, we may need to begin a sub-brace level to avoid
					// TODO polluting the global context w/r/t \uc.

					UT_UCSChar lc = XAP_EncodingManager::get_instance()->try_UToWindows(*pData);
					m_pie->_rtf_keyword("uc",lc && lc<256 ? 1 : 0);
					unsigned short ui = ((unsigned short)(*pData));	// RTF is limited to +/-32K ints
					signed short si = *((signed short *)(&ui));		// so we need to write negative
					m_pie->_rtf_keyword("u",si);					// numbers for large unicode values.
					if (lc && lc <256)
						m_pie->_rtf_nonascii_hex2(lc);
					pData++;
				}
				else if (*pData > 0x007f)
				{
					FlushBuffer();

					// for chars between 7f and ff, we could just send them
					// out as is, or we could send them out in hex or as a
					// unicode sequence.  when i originally did this, i chose
					// hex, so i'm not going to change it now.

					m_pie->_rtf_nonascii_hex2(*pData);
					pData++;
				}
				else
				{
					sBuf += (char)*pData++;
				}
			} else {
				/*
				    wordpad (and probably word6/7) don't understand
				    \uc0\u<UUUU> format at all.
				*/
				UT_UCSChar c = *pData++;
				UT_UCSChar lc = XAP_EncodingManager::get_instance()->try_UToWindows(c);
				if (lc==0 || lc >255)
				{
					/*
					    can't be represented in windows encoding.
					    So emit unicode (though attic apps won't understand it.
					    This branch is shamelessly copied from
					    branch if (*pData > 0x00ff) above.
					*/
					FlushBuffer();

					// RTF spec says that we should emit an ASCII-equivalent
					// character for each unicode character, so that dumb/older
					// readers don't lose a char.  i don't have a good algorithm
					// for deciding how to do this, so i'm not going to put out
					// any chars.  so i'm setting \uc0 before emitting \u<u>.
					// TODO decide if we should be smarter here and do a \uc1\u<u><A> ??
					// TODO if so, we may need to begin a sub-brace level to avoid
					// TODO polluting the global context w/r/t \uc.

					m_pie->_rtf_keyword("uc",0);
					unsigned short ui = ((unsigned short)(*pData));	// RTF is limited to +/-32K ints
					signed short si = *((signed short *)(&ui));		// so we need to write negative
					m_pie->_rtf_keyword("u",si);					// numbers for large unicode values.
				}
				else
				{
					if (lc > 0x007f)
					{
						FlushBuffer();

						// for chars between 7f and ff, we could just send them
						// out as is, or we could send them out in hex or as a
						// unicode sequence.  when i originally did this, i chose
						// hex, so i'm not going to change it now.

						m_pie->_rtf_nonascii_hex2(lc);
					}
					else
					{
						sBuf += static_cast<char>(lc);
					}
				}
			};
			break;
		}
	}

	FlushBuffer();
}

s_RTF_ListenerWriteDoc::s_RTF_ListenerWriteDoc(PD_Document * pDocument,
											   IE_Exp_RTF * pie,
											   bool bToClipboard, 
											   bool bHasMultiBlock)
  : m_wctomb(XAP_EncodingManager::get_instance()->getNative8BitEncodingName())
{
	// The overall syntax for an RTF file is:
	//
	// <file> := '{' <header> <document> '}'
	//
	// We are responsible for <document>
	//
	// <document> := <info>? <docfmt>* <section>+

	m_pDocument = pDocument;
	m_pie = pie;
	m_bInSpan = false;
	m_bInBlock = false;
	m_apiLastSpan = 0;
	m_apiThisSection = 0;
	m_apiThisBlock = 0;
	m_apiThisFrame = 0;
	m_bInFrame = false;
	m_bJustOpennedFrame = false;
	m_sdh = NULL;
	m_bToClipboard = bToClipboard;
	m_bStartedList = false;
	m_bBlankLine = true;
	m_Table.setDoc(m_pDocument);
	m_iCurRow = -1;
	m_bNewTable = false;
	m_iLeft = -1;
	m_iRight = -1;
	m_iTop = -1;
	m_iBot = -1;
	m_LastLinestyle = PP_PropertyMap::linestyle_solid;
	m_sLastColor = "000000";
	_setTabEaten(false);
	_setListBlock(false);

	// when we are going to the clipboard, we should implicitly
	// assume that we are starting in the middle of a section
	// and block.  when going to a file we should not.
	m_bJustStartingDoc = !m_bToClipboard;
	m_bJustStartingSection = !m_bToClipboard;

	m_wctomb.setOutCharset(XAP_EncodingManager::get_instance()->WindowsCharsetName());
	// TODO emit <info> if desired
	m_currID = 0;
	_rtf_info ();
	_rtf_docfmt();						// deal with <docfmt>
	m_apiSavedBlock = 0;
	m_sdhSavedBlock = NULL;
	m_bOpennedFootnote = false;
	m_iFirstTop = 0;
	m_bHyperLinkOpen = false;
	m_bOpenBlockForSpan = bHasMultiBlock;
	m_bTextBox = false;
	// <section>+ will be handled by the populate code.
}

s_RTF_ListenerWriteDoc::~s_RTF_ListenerWriteDoc()
{
	_closeSpan();
	_closeBlock();
	_closeSection();
}

bool s_RTF_ListenerWriteDoc::populate(PL_StruxFmtHandle /*sfh*/,
									  const PX_ChangeRecord * pcr)
{
	m_posDoc = pcr->getPosition();
	switch (pcr->getType())
	{
	case PX_ChangeRecord::PXT_InsertSpan:
		{
			const PX_ChangeRecord_Span * pcrs = static_cast<const PX_ChangeRecord_Span *> (pcr);

			PT_AttrPropIndex api = pcr->getIndexAP();

			PT_BufIndex bi = pcrs->getBufIndex();
			const UT_UCSChar * pData = m_pDocument->getPointer(bi);
//
// Code to deal with the tab following a list label. Eat it!!
//
			UT_uint32 length = pcrs->getLength();
			if(_isListBlock() && !_isTabEaten())
			{
				if(*pData == UCS_TAB)
				{
					_setTabEaten(true);
					pData++;
					length--;
					if(length == 0)
						return true;
				}
			}

			if (m_bOpenBlockForSpan)
			{
				m_bOpenBlockForSpan = false;

				pf_Frag * pf1 = m_pDocument->getFragFromPosition(pcr->getPosition());
				if (pf1 != NULL)
				{
					// scan backwards for the block props of this span.
					while (pf1 != NULL)
					{
						if (pf1->getType() == pf_Frag::PFT_Strux)
						{
							m_apiThisBlock = ((pf_Frag_Strux*)pf1)->getIndexAP();
							break;
						}
						pf1 = pf1->getPrev();
					}
				}
			}

			_openSpan(api);
			_outputData(pData,length,pcr->getPosition(),false);

			return true;
		}

	case PX_ChangeRecord::PXT_InsertObject:
		{
			const PX_ChangeRecord_Object * pcro = static_cast<const PX_ChangeRecord_Object *> (pcr);
			PT_AttrPropIndex api = pcr->getIndexAP();
			switch (pcro->getObjectType())
			{
			case PTO_Image:
				_closeSpan();
				_writeImageInRTF(pcro);
				return true;

				//#if 0
			// TODO deal with these other inline objects....

			case PTO_Field:
				_closeSpan();
				_openTag("field","/",false,api);
				return true;

				//#endif

			case PTO_Bookmark:
				_closeSpan ();
				_writeBookmark(pcro);
				return true;
			case PTO_Hyperlink:
			{
				_closeSpan ();
				const PP_AttrProp * pAP = NULL;
				m_pDocument->getAttrProp(api,&pAP);
				const XML_Char * pName;
				const XML_Char * pValue;
				bool bFound = false;
				UT_uint32 k = 0;
				while(pAP->getNthAttribute(k++, pName, pValue))
				{
					bFound = (0 == UT_XML_strnicmp(pName,"xlink:href",10));
					if(bFound)
						break;
				}
				if(bFound)
				{
					//this is the start of the hyperlink
					_writeHyperlink(pcro);
				}
				else
				{
//
// This is the end of hyperlink marker, signified by no xlink::href tag
//
					m_bHyperLinkOpen = false;
					m_pie->_rtf_close_brace();
					m_pie->_rtf_close_brace();
				}
			    return true;
			}
			default:
				return false;
			}
		}

	case PX_ChangeRecord::PXT_InsertFmtMark:
		return true;

	default:
		UT_ASSERT_NOT_REACHED();
		return false;
	}
}

/*!
 * This method writes out the all the boiler plate needed before every field
 * definition.
 */
void s_RTF_ListenerWriteDoc::_writeFieldPreamble(const PP_AttrProp * pSpanAP)
{
	const PP_AttrProp * pBlockAP = NULL;
	const PP_AttrProp * pSectionAP = NULL;

	m_pDocument->getAttrProp(m_apiThisSection,&pSectionAP);
	m_pDocument->getAttrProp(m_apiThisBlock,&pBlockAP);
	m_pie->_rtf_open_brace();
	m_pie->_rtf_keyword("field");
	m_pie->_rtf_open_brace();
	m_pie->_rtf_keyword("*");
	m_pie->_rtf_keyword("fldinst");
	m_pie->write(" ");
	m_pie->_rtf_open_brace();
	m_pie->_write_charfmt(s_RTF_AttrPropAdapter_AP(pSpanAP, pBlockAP, pSectionAP, m_pDocument));
	m_pie->write(" ");
}


/*!
 * This method writes out the current field value and closes all the braces.
 */
void s_RTF_ListenerWriteDoc::_writeFieldTrailer(void)
{
	const UT_UCSChar * szFieldValue = _getFieldValue();
	if(szFieldValue == NULL)
	{
		m_pie->_rtf_close_brace();
		return;
	}
	m_pie->_rtf_open_brace();
	m_pie->_rtf_keyword("fldrslt");
	m_pie->write(" ");
	m_pie->_rtf_open_brace();
	m_pie->_rtf_keyword("noproof");
	m_pie->write(" ");
	UT_uint32 len = UT_UCS4_strlen(szFieldValue);
	_outputData(szFieldValue,len,0,true);
	m_pie->_rtf_close_brace();
	m_pie->_rtf_close_brace();
	m_pie->_rtf_close_brace();
}

/*!
 * This method returns the field value at the current document location.
 * If there is not a field at the current document location return NULL.
 */
const UT_UCSChar * s_RTF_ListenerWriteDoc::_getFieldValue(void)
{
//
// Grab the first format handle in the PieceTable and turn it into a layout class.
// Check that is it a block.
//
	if(m_sdh == NULL)
	{
		m_pDocument->getStruxOfTypeFromPosition(m_posDoc,PTX_Block,&m_sdh);
	}
	PL_StruxFmtHandle sfh = m_pDocument->getNthFmtHandle(m_sdh,0);
	fl_Layout * pL = const_cast<fl_Layout *>(reinterpret_cast<const fl_Layout *>(sfh));
	UT_return_val_if_fail(pL,NULL);
	if(pL && pL->getType() != PTX_Block)
	{
	  UT_return_val_if_fail(0, NULL);
	}
	fl_BlockLayout* pBL = static_cast<fl_BlockLayout *>(pL);
	bool bDirection;
	UT_sint32 x, y, x2, y2, height;
	fp_Run * pRun = pBL->findPointCoords(m_posDoc,false,x,y,x2,y2,height,bDirection);
//
// Check the run to make sure it is a field.
//
	while(pRun && pRun->getType() == FPRUN_FMTMARK)
	{
		pRun = pRun->getNextRun();
	}
	if((pRun== NULL) || pRun->getType() != FPRUN_FIELD )
	{
	  UT_return_val_if_fail(0, NULL);
	}
//
// Now get the value of this field
//
	return static_cast<fp_FieldRun *>(pRun)->getValue();
}

void	 s_RTF_ListenerWriteDoc::_openTag(const char * szPrefix, const char * szSuffix,
								 bool bNewLineAfter, PT_AttrPropIndex api)
{
	 xxx_UT_DEBUGMSG(("TODO: Write code to go in here. In _openTag, szPrefix = %s  szSuffix = %s api = %x \n",szPrefix,szSuffix,api));
	 if(UT_XML_strcmp(szPrefix,"field") == 0)
	 {
		 const PP_AttrProp * pSpanAP = NULL;
		 const XML_Char * pszType = NULL;
		 m_pDocument->getAttrProp(api, &pSpanAP);
		 pSpanAP->getAttribute("type", pszType);
		 if(pszType == NULL)
		 {
			 return;
		 }
		 if(UT_XML_strcmp(pszType,"list_label") == 0)
		 {
			 return;
		 }

		 if(UT_XML_strcmp(pszType,"footnote_ref") == 0)
		 {
			 _openSpan(api,pSpanAP);
             m_pie->_rtf_keyword("chftn");
			 return;
		 }
		 else if(UT_XML_strcmp(pszType,"footnote_anchor") == 0)
		 {
			 _openSpan(api,pSpanAP);
             m_pie->_rtf_keyword("chftn");
			 return;
		 }
		 else if(UT_XML_strcmp(pszType,"endnote_ref") == 0)
		 {
			 _openSpan(api,pSpanAP);
             m_pie->_rtf_keyword("chftn");
			 return;
		 }
		 else if(UT_XML_strcmp(pszType,"endnote_anchor") == 0)
		 {
			 _openSpan(api,pSpanAP);
             m_pie->_rtf_keyword("chftn");
			 return;
		 }
		 else if(UT_XML_strcmp(pszType,"page_number") == 0)
		 {
			 _writeFieldPreamble(pSpanAP);
			 m_pie->write("PAGE ");
             m_pie->_rtf_close_brace();
             m_pie->_rtf_close_brace();
			 _writeFieldTrailer();
			 return;
		 }
		 else if(UT_XML_strcmp(pszType,"time") == 0)
		 {
			 _writeFieldPreamble(pSpanAP);
			 m_pie->write("TIME ");
             m_pie->_rtf_close_brace();
             m_pie->_rtf_close_brace();
			 _writeFieldTrailer();
			 return;
		 }
		 else if(UT_XML_strcmp(pszType,"page_ref") == 0)
		 {
             m_pie->_rtf_open_brace();
			 m_pie->_rtf_keyword("*");
			 m_pie->_rtf_keyword("abifieldDpageDref"); // abiword extension for now.
             m_pie->_rtf_close_brace();
			 return;
		 }
		 else if(UT_XML_strcmp(pszType,"page_count") == 0)
		 {
			 _writeFieldPreamble(pSpanAP);
			 m_pie->write("NUMPAGES ");
             m_pie->_rtf_close_brace();
             m_pie->_rtf_close_brace();
             m_pie->_rtf_close_brace();
			 return;
		 }
		 else if(UT_XML_strcmp(pszType,"date") == 0)
		 {
			 _writeFieldPreamble(pSpanAP);
			 m_pie->write("TIME  \\");
			 m_pie->_rtf_keyword("@");
			 m_pie->write(" \"dddd, MMMM dd, yyyy\" ");
             m_pie->_rtf_close_brace();
             m_pie->_rtf_close_brace();
			 _writeFieldTrailer();
			 return;
		 }
		 else if(UT_XML_strcmp(pszType,"date_mmddyy") == 0)
		 {
			 _writeFieldPreamble(pSpanAP);
			 m_pie->write("DATE ");
             m_pie->_rtf_close_brace();
             m_pie->_rtf_close_brace();
			 _writeFieldTrailer();
			 return;
		 }
		 else if(UT_XML_strcmp(pszType,"date_ddmmyy") == 0)
		 {
			 _writeFieldPreamble(pSpanAP);
			 m_pie->write("TIME \\");
			 m_pie->_rtf_keyword("@");
			 m_pie->write(" ""m/d/yy"" ");
             m_pie->_rtf_close_brace();
             m_pie->_rtf_close_brace();
			 _writeFieldTrailer();
			 return;
		 }
		 else if(UT_XML_strcmp(pszType,"date_mdy") == 0)
		 {
			 _writeFieldPreamble(pSpanAP);
			 m_pie->write("TIME  \\");
			 m_pie->_rtf_keyword("@");
			 m_pie->write(" \"MMMM d, yyyy\" ");
             m_pie->_rtf_close_brace();
             m_pie->_rtf_close_brace();
			 _writeFieldTrailer();
			 return;
		 }
		 else if(UT_XML_strcmp(pszType,"date_mthdy") == 0)
		 {
			 _writeFieldPreamble(pSpanAP);
			 m_pie->write("TIME  \\");
			 m_pie->_rtf_keyword("@");
			 m_pie->write(" \"MMM d, yy\" ");
             m_pie->_rtf_close_brace();
             m_pie->_rtf_close_brace();
			 _writeFieldTrailer();
			 return;
		 }
		 else if(UT_XML_strcmp(pszType,"date_dfl") == 0)
		 {
			 _writeFieldPreamble(pSpanAP);
			 m_pie->write("SAVEDATE  ");
             m_pie->_rtf_close_brace();
             m_pie->_rtf_close_brace();
			 _writeFieldTrailer();
			 return;
		 }
		 else if(UT_XML_strcmp(pszType,"date_ntdfl") == 0)
		 {
			 _writeFieldPreamble(pSpanAP);
			 m_pie->write("TIME  \\");
			 m_pie->_rtf_keyword("@");
			 m_pie->write(" \"MM-d-yy\" ");
             m_pie->_rtf_close_brace();
             m_pie->_rtf_close_brace();
			 _writeFieldTrailer();
			 return;
		 }
		 else if(UT_XML_strcmp(pszType,"date_wkday") == 0)
		 {
			 _writeFieldPreamble(pSpanAP);
			 m_pie->write("TIME  \\");
			 m_pie->_rtf_keyword("@");
			 m_pie->write(" \"dddd\" ");
             m_pie->_rtf_close_brace();
             m_pie->_rtf_close_brace();
			 _writeFieldTrailer();
			 return;
		 }
		 else if(UT_XML_strcmp(pszType,"date_doy") == 0)
		 {
             m_pie->_rtf_open_brace();
			 m_pie->_rtf_keyword("*");
			 m_pie->_rtf_keyword("abifieldDdateDdoy"); // abiword extension for now.
             m_pie->_rtf_close_brace();
			 return;
		 }
		 else if(UT_XML_strcmp(pszType,"time_miltime") == 0)
		 {
			 _writeFieldPreamble(pSpanAP);
			 m_pie->write("TIME  \\");
			 m_pie->_rtf_keyword("@");
			 m_pie->write(" \"HH:mm:ss\" ");
             m_pie->_rtf_close_brace();
             m_pie->_rtf_close_brace();
			 _writeFieldTrailer();
			 return;
		 }
		 else if(UT_XML_strcmp(pszType,"time_ampm") == 0)
		 {
			 _writeFieldPreamble(pSpanAP);
			 m_pie->write("TIME  \\");
			 m_pie->_rtf_keyword("@");
			 m_pie->write(" \"h:mm:ss am/pm\" ");
             m_pie->_rtf_close_brace();
             m_pie->_rtf_close_brace();
			 _writeFieldTrailer();
			 return;
		 }
		 else if(UT_XML_strcmp(pszType,"time_zone") == 0)
		 {
             m_pie->_rtf_open_brace();
			 m_pie->_rtf_keyword("*");
			 m_pie->_rtf_keyword("abifieldDtimeDzone"); // abiword extension for now.
             m_pie->_rtf_close_brace();
			 return;
		 }
		 else if(UT_XML_strcmp(pszType,"time_epoch") == 0)
		 {
             m_pie->_rtf_open_brace();
			 m_pie->_rtf_keyword("*");
			 m_pie->_rtf_keyword("abifieldDtimeDepoch"); // abiword extension for now.
             m_pie->_rtf_close_brace();
			 return;
		 }
		 else if(UT_XML_strcmp(pszType,"word_count") == 0)
		 {
			 _writeFieldPreamble(pSpanAP);
			 m_pie->write("NUMWORDS ");
             m_pie->_rtf_close_brace();
             m_pie->_rtf_close_brace();
			 _writeFieldTrailer();
			 return;
		 }
		 else if(UT_XML_strcmp(pszType,"char_count") == 0)
		 {
			 _writeFieldPreamble(pSpanAP);
  			 m_pie->write("NUMCHARS  ");
             m_pie->_rtf_close_brace();
             m_pie->_rtf_close_brace();
			 _writeFieldTrailer();
			 return;
		 }
		 else if(UT_XML_strcmp(pszType,"line_count") == 0)
		 {
             m_pie->_rtf_open_brace();
			 m_pie->_rtf_keyword("*");
			 m_pie->_rtf_keyword("abifieldDlineDcount"); // abiword extension for now.
             m_pie->_rtf_close_brace();
			 return;
		 }
		 else if(UT_XML_strcmp(pszType,"para_count") == 0)
		 {
             m_pie->_rtf_open_brace();
			 m_pie->_rtf_keyword("*");
			 m_pie->_rtf_keyword("abifieldDparaDcount"); // abiword extension for now.
             m_pie->_rtf_close_brace();
			 UT_DEBUGMSG(("SEVIOR: paragraph count field here \n"));
			 return;
		 }
		 else if(UT_XML_strcmp(pszType,"nbsp_count") == 0)
		 {
             m_pie->_rtf_open_brace();
			 m_pie->_rtf_keyword("*");
			 m_pie->_rtf_keyword("abifieldDnbspDcount"); // abiword extension for now.
             m_pie->_rtf_close_brace();
			 return;
		 }
		 else if(UT_XML_strcmp(pszType,"file_name") == 0)
		 {
             m_pie->_rtf_open_brace();
			 m_pie->_rtf_keyword("*");
			 m_pie->_rtf_keyword("abifieldDfileDname"); // abiword extension for now.
             m_pie->_rtf_close_brace();
			 UT_DEBUGMSG(("SEVIOR: File Name field here \n"));
			 return;
		 }
		 else if(UT_XML_strcmp(pszType,"app_ver") == 0)
		 {
             m_pie->_rtf_open_brace();
			 m_pie->_rtf_keyword("*");
			 m_pie->_rtf_keyword("abifieldDappDver"); // abiword extension for now.
             m_pie->_rtf_close_brace();
			 return;
		 }
		 else if(UT_XML_strcmp(pszType,"app_id") == 0)
		 {
             m_pie->_rtf_open_brace();
			 m_pie->_rtf_keyword("*");
			 m_pie->_rtf_keyword("abifieldDappDid"); // abiword extension for now.
             m_pie->_rtf_close_brace();
			 UT_DEBUGMSG(("SEVIOR: Application ID field here \n"));
			 return;
		 }
		 else if(UT_XML_strcmp(pszType,"app_options") == 0)
		 {
             m_pie->_rtf_open_brace();
			 m_pie->_rtf_keyword("*");
			 m_pie->_rtf_keyword("abifieldDappDoptions"); // abiword extension for now.
             m_pie->_rtf_close_brace();
			 return;
		 }
		 else if(UT_XML_strcmp(pszType,"app_target") == 0)
		 {
             m_pie->_rtf_open_brace();
			 m_pie->_rtf_keyword("*");
			 m_pie->_rtf_keyword("abifieldDappDtarget"); // abiword extension for now.
             m_pie->_rtf_close_brace();
			 return;
		 }
		 else if(UT_XML_strcmp(pszType,"app_compiledate") == 0)
		 {
             m_pie->_rtf_open_brace();
			 m_pie->_rtf_keyword("*");
			 m_pie->_rtf_keyword("abifieldDappDcompiledate"); // abiword extension for now.
             m_pie->_rtf_close_brace();
			 return;
		 }
		 else if(UT_XML_strcmp(pszType,"app_compiletime") == 0)
		 {
             m_pie->_rtf_open_brace();
			 m_pie->_rtf_keyword("*");
			 m_pie->_rtf_keyword("abifieldDappDcompiletime"); // abiword extension for now.
             m_pie->_rtf_close_brace();
			 return;
		 }
		 else
		 {
			 UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
			 return;
		 }
	 }
}


/*!
 * This exports all the properties in a cell strux by extending rtf with
 * a \*\abicellproperties keyword.
 * Code mostly copied for fl_TableLayout::_lookupProperties. This code should
 * should be updated when new properties are defined.
 */
void s_RTF_ListenerWriteDoc::_export_AbiWord_Cell_props(PT_AttrPropIndex api, bool bFill)
{
//
// Export abiword table properties as an extension
// Use these for cutting and pasting within abiword.
//
	UT_String sCellProps;
	sCellProps.clear();
	_fillCellProps(api, sCellProps);
	UT_String sTopAttach = "top-attach";
	UT_String sTop = UT_String_getPropVal(sCellProps,sTopAttach);
	UT_String sBotAttach = "bot-attach";
	UT_String sBot = UT_String_getPropVal(sCellProps,sBotAttach);
	if(bFill)
	{
		UT_String sLeftAttach = "left-attach";
		m_iFirstTop = atoi(sTop.c_str());
		UT_String sLeft = UT_String_getPropVal(sCellProps,sLeftAttach);
		UT_sint32 iFirstLeft = atoi(sLeft.c_str());
//
// Export cells to the left of the current cell.
//
		UT_sint32 i = 0;
		UT_String sRightAttach = "right-attach";
		UT_String sTempProps;
		UT_String sTTop = "0";
		UT_String sTBot = "1";
		for(i=0;i< iFirstLeft;i++)
		{
			sTempProps.clear();
			UT_String sLeft = UT_String_sprintf("%d",i);
			UT_String sRight = UT_String_sprintf("%d",i+1);
			UT_String_setProperty(sTempProps,sLeftAttach,sLeft);
			UT_String_setProperty(sTempProps,sRightAttach,sRight);
			UT_String_setProperty(sTempProps,sTopAttach,sTTop);
			UT_String_setProperty(sTempProps,sBotAttach,sTBot);
//
// Export an open and close cell extension. The Abi importer will place 
// a paragraph in for us
//
			m_pie->_rtf_open_brace();
			m_pie->_rtf_keyword("*");
			m_pie->_rtf_keyword("abicellprops ",sTempProps.c_str());
			m_pie->_rtf_close_brace();
			m_pie->_rtf_open_brace();
			m_pie->_rtf_keyword("*");
			m_pie->_rtf_keyword("abiendcell");
			m_pie->_rtf_close_brace();
		}
	}
//
// Adjust the top and bottom attaches for the offset within the table if the
// select starts before the start of the table
//
	if(m_iFirstTop > 0)
	{
		UT_sint32 iTop = atoi(sTop.c_str()) - m_iFirstTop;
		sTop = UT_String_sprintf("%d",iTop);
		UT_String_setProperty(sCellProps,sTopAttach,sTop);
		UT_sint32 iBot = atoi(sBot.c_str()) - m_iFirstTop;
		sBot = UT_String_sprintf("%d",iBot);
		UT_String_setProperty(sCellProps,sBotAttach,sBot);
	}
	xxx_UT_DEBUGMSG(("Cell props are %s \n",sCellProps.c_str()));
	m_pie->_rtf_open_brace();
	m_pie->_rtf_keyword("*");
	m_pie->_rtf_keyword("abicellprops ",sCellProps.c_str());
	m_pie->_rtf_close_brace();
}

/*!
 * Convience function to lookup a property via const char * string.
 * If the property is not present sVal is returned with zero size.
 */
void s_RTF_ListenerWriteDoc::_getPropString(const UT_String sPropString, const char * szProp, UT_String & sVal)
{
	sVal.clear();
	const UT_String sProp(szProp);
	sVal = UT_String_getPropVal(sPropString,sProp);
}

/*!
 * Export all the properties of this cell to the RTF stream.
 * api is the Attribute Property Index.
 * sTableProps is the UT_String containing all the Table Properties defined
 * for the table. 
 */
void s_RTF_ListenerWriteDoc::_exportCellProps(PT_AttrPropIndex  api, UT_String & sTableProps)
{
	UT_String sCellProps;
	UT_String sWork;
	UT_sint32 iThick =1;
	UT_sint32 iColor =0;
	bool bDrawBorder = true;
	_fillCellProps(api,sCellProps);

//
// Alignements of cells
//
	m_pie->_rtf_keyword("clvertalt"); // only top alignment for now.
//
// Other aligments are:
// \clvertalc Vertical center alignment.
// \clvertalb Virtical Bottom alignemnt.
//
// Text flow:
//
	m_pie->_rtf_keyword("cltxlrtb"); // Only left to right, top to bottom now
//
// Others:
// \cltxtbrl Text in a cell flows right to left and top to bottom.
// \cltxbtlr Text in a cell flows left to right and bottom to top.
// \cltxlrtbv Text in a cell flows left to right and top to bottom, vertical.
// \cltxtbrlv Text in a cell flows top to bottom and right to left, vertical.

//
// Top Border definitions
//
	_getPropString(sCellProps,"top-style",sWork);
	bDrawBorder = true;
	if(sWork.size()>0)
	{
		PP_PropertyMap::TypeLineStyle linestyle = PP_PropertyMap::linestyle_type(sWork.c_str());
		if(linestyle == PP_PropertyMap::linestyle_inherit)
		{
			linestyle = m_LastLinestyle;
		}
		else if (linestyle == PP_PropertyMap::linestyle_none)
		{
			bDrawBorder = false;
		}
		m_LastLinestyle = linestyle;
		if(bDrawBorder)
		{
			m_pie->_rtf_keyword("clbrdrt"); // cell top border
			if(linestyle == PP_PropertyMap::linestyle_solid)
			{
				m_pie->_rtf_keyword("brdrs"); // plain border
			}
			else if( linestyle == PP_PropertyMap::linestyle_dotted)
			{
				m_pie->_rtf_keyword("brdrdot"); // plain border
			}
			else if( linestyle == PP_PropertyMap::linestyle_dashed)
			{
				m_pie->_rtf_keyword("brdrdash"); // plain border
			}
		}

	}
	else
	{
		m_pie->_rtf_keyword("clbrdrt"); // cell top border
		m_pie->_rtf_keyword("brdrs"); // plain border
	}
	if(bDrawBorder)
	{
		_getPropString(sCellProps,"top-thickness",sWork);
		if(sWork.size()>0)
		{
			m_pie->_rtf_keyword_ifnotdefault_twips("brdrw",sWork.c_str(),-1);
		}
		else
		{
			m_pie->_rtf_keyword("brdrw",10*iThick); //border thickness
		}
		_getPropString(sCellProps,"top-color",sWork);
		if(sWork.size()>0)
		{
			bool bWriteColor = true;
			if (strcmp (sWork.c_str(), "inherit") == 0)
			{
				iColor =  m_pie->_findOrAddColor(m_sLastColor.c_str());
			}
			else if(strcmp (sWork.c_str(), "transperent") == 0)
			{
				bWriteColor = false;
				iColor = m_pie->_findOrAddColor(sWork.c_str());
			}
			else
			{
				iColor =  m_pie->_findOrAddColor(sWork.c_str());
			}
			m_sLastColor = sWork;
			if(bWriteColor)
			{
				m_pie->_rtf_keyword("brdrcf",iColor);
			}
		}
	}
	m_pie->write(" ");											

//
//write out the background colour of the cell
//
	_getPropString(sCellProps,"background-color",sWork);
	if(sWork.size()>0)
	{
		bool bWriteColor = true;
		if (strcmp (sWork.c_str(), "inherit") == 0)
		{
			iColor =  m_pie->_findOrAddColor(m_sLastColor.c_str());
		}
		else if(strcmp (sWork.c_str(), "transperent") == 0)
		{
			bWriteColor = false;
			iColor = m_pie->_findOrAddColor(sWork.c_str());
		}
		else
		{
			iColor =  m_pie->_findOrAddColor(sWork.c_str());
		}
		m_sLastColor = sWork;

		if(bWriteColor)
		{
			m_pie->_rtf_keyword("clcbpat",iColor); // cell background color
		}
	}
//
// Background style
//
	_getPropString(sCellProps,"bg-style",sWork);
	if(sWork.size() > 0)
	{
//
// We haven't implemented this yet
//
// Allowed patterns are:
// \clbghoriz,\clbgvert,\clbgfdiag,clbgbdiag,\clbgcross,\clbgdcross,
// \clbgdkhor,\clbgdkvert,\clbgdkfdiag,clbgdkbdiag,\clbgdkcross,\clbgdkdcross
//
// Pattern Line color is
//
// \clcfpatN
	}

//
// Left Border Definitions
//
	_getPropString(sCellProps,"left-style",sWork);
	bDrawBorder = true;
	if(sWork.size()>0)
	{
		PP_PropertyMap::TypeLineStyle linestyle = PP_PropertyMap::linestyle_type(sWork.c_str());
		if(linestyle == PP_PropertyMap::linestyle_inherit)
		{
			linestyle = m_LastLinestyle;
		}
		else if (linestyle == PP_PropertyMap::linestyle_none)
		{
			bDrawBorder = false;
		}
		m_LastLinestyle = linestyle;
		if(bDrawBorder)
		{
			m_pie->_rtf_keyword("clbrdrl"); // cell left border
			if(linestyle == PP_PropertyMap::linestyle_solid)
			{
				m_pie->_rtf_keyword("brdrs"); // plain border
			}
			else if( linestyle == PP_PropertyMap::linestyle_dotted)
			{
				m_pie->_rtf_keyword("brdrdot"); // plain border
			}
			else if( linestyle == PP_PropertyMap::linestyle_dashed)
			{
				m_pie->_rtf_keyword("brdrdash"); // plain border
			}
		}

	}
	else
	{
		m_pie->_rtf_keyword("clbrdrl"); // cell left border
		m_pie->_rtf_keyword("brdrs"); // plain border
	}
	if(bDrawBorder)
	{
		_getPropString(sCellProps,"left-thickness",sWork);
		if(sWork.size()>0)
		{
			m_pie->_rtf_keyword_ifnotdefault_twips("brdrw",sWork.c_str(),-1);
		}
		else
		{
			m_pie->_rtf_keyword("brdrw",10*iThick); //border thickness
		}
		_getPropString(sCellProps,"left-color",sWork);
		if(sWork.size()>0)
		{
			bool bWriteColor = true;
			if (strcmp (sWork.c_str(), "inherit") == 0)
			{
				iColor =  m_pie->_findOrAddColor(m_sLastColor.c_str());
			}
			else if(strcmp (sWork.c_str(), "transperent") == 0)
			{
				bWriteColor = false;
				iColor = m_pie->_findOrAddColor(sWork.c_str());
			}
			else
			{
				iColor =  m_pie->_findOrAddColor(sWork.c_str());
			}
			m_sLastColor = sWork;
			if(bWriteColor)
			{
				m_pie->_rtf_keyword("brdrcf",iColor);
			}
		}
	}
	m_pie->write(" ");											
//
// Bottom Border Definitions
//
	_getPropString(sCellProps,"bot-style",sWork);
	bDrawBorder = true;
	if(sWork.size()>0)
	{
		PP_PropertyMap::TypeLineStyle linestyle = PP_PropertyMap::linestyle_type(sWork.c_str());
		if(linestyle == PP_PropertyMap::linestyle_inherit)
		{
			linestyle = m_LastLinestyle;
		}
		else if (linestyle == PP_PropertyMap::linestyle_none)
		{
			bDrawBorder = false;
		}
		m_LastLinestyle = linestyle;
		if(bDrawBorder)
		{
			m_pie->_rtf_keyword("clbrdrb"); // cell bottom border
			if(linestyle == PP_PropertyMap::linestyle_solid)
			{
				m_pie->_rtf_keyword("brdrs"); // plain border
			}
			else if( linestyle == PP_PropertyMap::linestyle_dotted)
			{
				m_pie->_rtf_keyword("brdrdot"); // plain border
			}
			else if( linestyle == PP_PropertyMap::linestyle_dashed)
			{
				m_pie->_rtf_keyword("brdrdash"); // plain border
			}
		}

	}
	else
	{
		m_pie->_rtf_keyword("clbrdrb"); // cell bottom border
		m_pie->_rtf_keyword("brdrs"); // plain border
	}
	if(bDrawBorder)
	{
		_getPropString(sCellProps,"bot-thickness",sWork);
		if(sWork.size()>0)
		{
			m_pie->_rtf_keyword_ifnotdefault_twips("brdrw",sWork.c_str(),-1);
		}
		else
		{
			m_pie->_rtf_keyword("brdrw",10*iThick); //border thickness
		}
		_getPropString(sCellProps,"bot-color",sWork);
		if(sWork.size()>0)
		{
			bool bWriteColor = true;
			if (strcmp (sWork.c_str(), "inherit") == 0)
			{
				iColor =  m_pie->_findOrAddColor(m_sLastColor.c_str());
			}
			else if(strcmp (sWork.c_str(), "transperent") == 0)
			{
				bWriteColor = false;
				iColor = m_pie->_findOrAddColor(sWork.c_str());
			}
			else
			{
				iColor =  m_pie->_findOrAddColor(sWork.c_str());
			}
			m_sLastColor = sWork;
			if(bWriteColor)
			{
				m_pie->_rtf_keyword("brdrcf",iColor);
			}
		}
	}
	m_pie->write(" ");											
//
// Right Border Definitions
//
	_getPropString(sCellProps,"right-style",sWork);
	bDrawBorder = true;
	if(sWork.size()>0)
	{
		PP_PropertyMap::TypeLineStyle linestyle = PP_PropertyMap::linestyle_type(sWork.c_str());
		if(linestyle == PP_PropertyMap::linestyle_inherit)
		{
			linestyle = m_LastLinestyle;
		}
		else if (linestyle == PP_PropertyMap::linestyle_none)
		{
			bDrawBorder = false;
		}
		m_LastLinestyle = linestyle;
		if(bDrawBorder)
		{
			m_pie->_rtf_keyword("clbrdrr"); // cell right border
			if(linestyle == PP_PropertyMap::linestyle_solid)
			{
				m_pie->_rtf_keyword("brdrs"); // plain border
			}
			else if( linestyle == PP_PropertyMap::linestyle_dotted)
			{
				m_pie->_rtf_keyword("brdrdot"); // plain border
			}
			else if( linestyle == PP_PropertyMap::linestyle_dashed)
			{
				m_pie->_rtf_keyword("brdrdash"); // plain border
			}
		}

	}
	else
	{
		m_pie->_rtf_keyword("clbrdrr"); // cell right border
		m_pie->_rtf_keyword("brdrs"); // plain border
	}
	if(bDrawBorder)
	{
		_getPropString(sCellProps,"right-thickness",sWork);
		if(sWork.size()>0)
		{
			m_pie->_rtf_keyword_ifnotdefault_twips("brdrw",sWork.c_str(),-1);
		}
		else
		{
			m_pie->_rtf_keyword("brdrw",10*iThick); //border thickness
		}
		_getPropString(sCellProps,"right-color",sWork);
		if(sWork.size()>0)
		{
			bool bWriteColor = true;
			if (strcmp (sWork.c_str(), "inherit") == 0)
			{
				iColor =  m_pie->_findOrAddColor(m_sLastColor.c_str());
			}
			else if(strcmp (sWork.c_str(), "transperent") == 0)
			{
				bWriteColor = false;
				iColor = m_pie->_findOrAddColor(sWork.c_str());
			}
			else
			{
				iColor =  m_pie->_findOrAddColor(sWork.c_str());
			}
			m_sLastColor = sWork;
			if(bWriteColor)
			{
				m_pie->_rtf_keyword("brdrcf",iColor);
			}
		}
	}
	m_pie->write(" ");											
}

/*!
 * Fill the supplied UT_String with all the properties defined for a cell
 */
void s_RTF_ListenerWriteDoc::_fillCellProps(PT_AttrPropIndex api, UT_String & sCellProps)
{
	const PP_AttrProp* pSectionAP = NULL;
	m_pDocument->getAttrProp(api, &pSectionAP);
	const XML_Char* pszHomogeneous = NULL;
	pSectionAP->getProperty("homogeneous", pszHomogeneous);
	UT_String sPropVal;
	UT_String sProp;
	const XML_Char* pszLeftOffset = NULL;
	const XML_Char* pszTopOffset = NULL;
	const XML_Char* pszRightOffset = NULL;
	const XML_Char* pszBottomOffset = NULL;
	pSectionAP->getProperty("cell-margin-left", pszLeftOffset);
	pSectionAP->getProperty("cell-margin-top", pszTopOffset);
	pSectionAP->getProperty("cell-margin-right", pszRightOffset);
	pSectionAP->getProperty("cell-margin-bottom", pszBottomOffset);

	if(pszLeftOffset && pszLeftOffset[0])
	{
		sProp = "cell-margin-left";
		sPropVal= pszLeftOffset;
		UT_String_setProperty(sCellProps,sProp,sPropVal);
	}

	if(pszTopOffset && pszTopOffset[0])
	{
		sProp = "cell-margin-top";
		sPropVal= pszTopOffset;
		UT_String_setProperty(sCellProps,sProp,sPropVal);
	}

	if(pszRightOffset && pszRightOffset[0])
	{
		sProp = "cell-margin-right";
		sPropVal= pszRightOffset;
		UT_String_setProperty(sCellProps,sProp,sPropVal);
	}

	if(pszBottomOffset && pszBottomOffset[0])
	{
		sProp = "cell-margin-bottom";
		sPropVal= pszBottomOffset;
		UT_String_setProperty(sCellProps,sProp,sPropVal);
	}
	const XML_Char* pszLeftAttach = NULL;
	const XML_Char* pszRightAttach = NULL;
	const XML_Char* pszTopAttach = NULL;
	const XML_Char* pszBottomAttach = NULL;
	pSectionAP->getProperty("left-attach", pszLeftAttach);
	pSectionAP->getProperty("right-attach", pszRightAttach);
	pSectionAP->getProperty("top-attach", pszTopAttach);
	pSectionAP->getProperty("bot-attach", pszBottomAttach);
	xxx_UT_DEBUGMSG(("CellLayout _lookupProps top %s bot %s left %s right %s \n",pszTopAttach,pszBottomAttach,pszLeftAttach,pszRightAttach)); 
	if(pszLeftAttach && pszLeftAttach[0])
	{
		sProp = "left-attach";
		sPropVal= pszLeftAttach;
		UT_String_setProperty(sCellProps,sProp,sPropVal);
	}
	if(pszRightAttach && pszRightAttach[0])
	{
		sProp = "right-attach";
		sPropVal= pszRightAttach;
		UT_String_setProperty(sCellProps,sProp,sPropVal);
	}
	if(pszTopAttach && pszTopAttach[0])
	{
		sProp = "top-attach";
		sPropVal= pszTopAttach;
		UT_String_setProperty(sCellProps,sProp,sPropVal);
	}
	if(pszBottomAttach && pszBottomAttach[0])
	{
		sProp = "bot-attach";
		sPropVal= pszBottomAttach;
		UT_String_setProperty(sCellProps,sProp,sPropVal);
	}

	/* cell-border properties:
	 */
	const XML_Char * pszColor = NULL;
	pSectionAP->getProperty ("color", pszColor);
	if (pszColor)
	{
		sProp = "color";
		sPropVal= pszColor;
		UT_String_setProperty(sCellProps,sProp,sPropVal);
	}
	const XML_Char * pszBorderColor = NULL;
	const XML_Char * pszBorderStyle = NULL;
	const XML_Char * pszBorderWidth = NULL;

	pSectionAP->getProperty ("bot-color",       pszBorderColor);
	if (pszBorderColor && *pszBorderColor)
	{
		sProp = "bot-color";
		sPropVal= pszBorderColor;
		UT_String_setProperty(sCellProps,sProp,sPropVal);
	}
	pSectionAP->getProperty ("bot-style",       pszBorderStyle);
	if (pszBorderStyle && *pszBorderStyle)
	{
		sProp = "bot-style";
		sPropVal= pszBorderStyle;
		UT_String_setProperty(sCellProps,sProp,sPropVal);
	}

	pSectionAP->getProperty ("bot-thickness",   pszBorderWidth);
	if (pszBorderWidth && *pszBorderWidth)
	{
		sProp = "bot-thickness";
		sPropVal= pszBorderWidth;
		UT_String_setProperty(sCellProps,sProp,sPropVal);
	}
	pszBorderColor = NULL;
	pszBorderStyle = NULL;
	pszBorderWidth = NULL;

	pSectionAP->getProperty ("left-color",      pszBorderColor);
	pSectionAP->getProperty ("left-style",      pszBorderStyle);
	pSectionAP->getProperty ("left-thickness",  pszBorderWidth);

	if (pszBorderColor && *pszBorderColor)
	{
		sProp = "left-color";
		sPropVal= pszBorderColor;
		UT_String_setProperty(sCellProps,sProp,sPropVal);
	}
	if (pszBorderStyle && *pszBorderStyle)
	{
		sProp = "left-style";
		sPropVal= pszBorderStyle;
		UT_String_setProperty(sCellProps,sProp,sPropVal);
	}
	if (pszBorderWidth && *pszBorderWidth)
	{
		sProp = "left-thickness";
		sPropVal= pszBorderWidth;
		UT_String_setProperty(sCellProps,sProp,sPropVal);
	}

	pszBorderColor = NULL;
	pszBorderStyle = NULL;
	pszBorderWidth = NULL;

	pSectionAP->getProperty ("right-color",     pszBorderColor);
	pSectionAP->getProperty ("right-style",     pszBorderStyle);
	pSectionAP->getProperty ("right-thickness", pszBorderWidth);

	if (pszBorderColor && *pszBorderColor)
	{
		sProp = "right-color";
		sPropVal= pszBorderColor;
		UT_String_setProperty(sCellProps,sProp,sPropVal);
	}
	if (pszBorderStyle && *pszBorderStyle)
	{
		sProp = "right-style";
		sPropVal= pszBorderStyle;
		UT_String_setProperty(sCellProps,sProp,sPropVal);
	}
	if (pszBorderWidth && *pszBorderWidth)
	{
		sProp = "right-thickness";
		sPropVal= pszBorderWidth;
		UT_String_setProperty(sCellProps,sProp,sPropVal);
	}

	pszBorderColor = NULL;
	pszBorderStyle = NULL;
	pszBorderWidth = NULL;

	pSectionAP->getProperty ("top-color",       pszBorderColor);
	pSectionAP->getProperty ("top-style",       pszBorderStyle);
	pSectionAP->getProperty ("top-thickness",   pszBorderWidth);
	if (pszBorderColor && *pszBorderColor)
	{
		sProp = "top-color";
		sPropVal= pszBorderColor;
		UT_String_setProperty(sCellProps,sProp,sPropVal);
	}
	if (pszBorderStyle && *pszBorderStyle)
	{
		sProp = "top-style";
		sPropVal= pszBorderStyle;
		UT_String_setProperty(sCellProps,sProp,sPropVal);
	}
	if (pszBorderWidth && *pszBorderWidth)
	{
		sProp = "top-thickness";
		sPropVal= pszBorderWidth;
		UT_String_setProperty(sCellProps,sProp,sPropVal);
	}

	/* cell fill
	 */
	const XML_Char * pszBgStyle = NULL;
	const XML_Char * pszBgColor = NULL;
	const XML_Char * pszBackgroundColor = NULL;

	pSectionAP->getProperty ("bg-style",         pszBgStyle);
	if (pszBgStyle && *pszBgStyle)
	{
		sProp = "bg-style";
		sPropVal= pszBgStyle;
		UT_String_setProperty(sCellProps,sProp,sPropVal);
	}
	pSectionAP->getProperty ("bgcolor",          pszBgColor);
	if (pszBgColor && *pszBgColor)
	{
		sProp = "bgcolor";
		sPropVal= pszBgColor;
		UT_String_setProperty(sCellProps,sProp,sPropVal);
	}
	pSectionAP->getProperty ("background-color", pszBackgroundColor);
	if (pszBackgroundColor && *pszBackgroundColor)
	{
		sProp = "background-color";
		sPropVal= pszBackgroundColor;
		UT_String_setProperty(sCellProps,sProp,sPropVal);
	}
}


void s_RTF_ListenerWriteDoc::_open_cell(PT_AttrPropIndex api)
{
//
// If we copy text to the clipboard we may not cover a open table strux.
// Put this in to prevent crashes.
//
	if(m_Table.getNestDepth() < 1)
	{
		_open_table(api,true);
		_export_AbiWord_Cell_props(api,true);
	}
	else
	{
		_export_AbiWord_Cell_props(api,false);
	}

	UT_sint32 iOldRow = m_iTop;
	UT_sint32 i =0;
	UT_sint32 iOldRight = m_iRight;
	xxx_UT_DEBUGMSG(("Setting cell API 1 NOW!!!!!!!!!!!!!!!!! %d \n",api));
	PT_AttrPropIndex prevAPI = api;
	m_Table.OpenCell(api);
	bool bNewRow = false;
	xxx_UT_DEBUGMSG(("iOldRow %d newTop %d \n",iOldRow,m_Table.getTop()));
	if(	(m_Table.getLeft() < iOldRight) || m_bNewTable)
	{
		xxx_UT_DEBUGMSG(("NEW ROW DETECTED !!!!!!!!!!!!!!!!!\n"));
		if(m_bNewTable)
		{
			m_pie->_rtf_open_brace();
			if(m_Table.getNestDepth() > 1)
			{
				m_pie->_rtf_close_brace();
			}
			else
			{
				_newRow();
			}
		}
		else
		{
			bNewRow = true;
//
// Now we have to output cell markers for all the vertically merged cells
// on the right edge of previous row
//
			UT_sint32 count = m_Table.getPrevNumRightMostVMerged();
			UT_sint32 i =0;
			for(i=0; i<count; i++)
			{

				if(m_Table.getNestDepth() < 2)
				{
					UT_sint32 iRight = getRightOfCell(m_Table.getCurRow() -1,m_iRight + i);
					if(iRight == (m_iRight + i +1))
					{
						m_pie->_rtf_keyword("cell");
					}
				}
				else
				{
					UT_sint32 iRight = getRightOfCell(m_Table.getCurRow() -1,m_iRight + i);
					if(iRight == (m_iRight + i +1))
					{
						m_pie->_rtf_keyword("nestcell");
					}
				}
			}
			if(m_Table.getNestDepth() < 2)
			{
				m_pie->_rtf_keyword("row");
				_newRow();
			}
			else
			{
				m_Table.OpenCell(prevAPI);
				_newRow();
				m_Table.OpenCell(api);
				m_pie->_rtf_keyword("nestrow");
			}
			if(m_Table.getNestDepth() > 1)
			{
//
// This closes off the \nesttableprops
				m_pie->_rtf_close_brace();
			}
		}
	}
//
// reset api. It may have been screwed in _newRow
//
	xxx_UT_DEBUGMSG(("Setting cell API 1 NOW!!!!!!!!!!!!!!!!! %d \n",api));
	m_Table.OpenCell(api);
	if(bNewRow)
	{
//
// Output cell markers for all vertically merged cells at the start of the row
//
//
// fix me have to handle horizontally and vertically merged cells at the
// left of a table.

		if(m_Table.getNestDepth() < 2)
		{
			for(i = 0; i < m_Table.getLeft(); i++)
			{
				m_pie->_rtf_keyword("cell");
			}
		}
		else
		{
			for(i = 0; i < m_Table.getLeft(); i++)
			{
				xxx_UT_DEBUGMSG(("Writing nestcell in wrong spot 1 \n"));
				m_pie->_rtf_keyword("nestcell");
			}
		}
	}
//
// Now output vertically merged cell markers between the last right position and this cell's left.
//
	else
	{
		if(!m_bNewTable)
		{
			if(m_Table.getNestDepth() < 2)
			{
				for(i = m_iRight; i < m_Table.getLeft(); i++)
//
// We don't output these cell's if they're horiztonally merged too.
//
				{
					UT_sint32 iRight = getRightOfCell(m_Table.getCurRow(),i);
					if(iRight == (i +1))
					{
						m_pie->_rtf_keyword("cell");
					}
				}
			}
			else
			{
				for(i = m_iRight; i < m_Table.getLeft(); i++)
				{
					xxx_UT_DEBUGMSG(("Writing nestcell in wrong spot 2 \n"));
					UT_sint32 iRight = getRightOfCell(m_Table.getCurRow(),i);
					if(iRight == (i +1))
					{
						m_pie->_rtf_keyword("nestcell");
					}
				}
			}
		}
	}
	m_bNewTable = false;
	m_iLeft = m_Table.getLeft();
	m_iRight = m_Table.getRight();
	m_iTop = m_Table.getTop();
	m_iBot = m_Table.getBot();
}

/*!
 * This returns the right-attach of the cell to the right of the cell at (row,
 * col)
 */
UT_sint32  s_RTF_ListenerWriteDoc::getRightOfCell(UT_sint32 row,UT_sint32 col)
{
	PL_StruxDocHandle sdhCell = m_pDocument->getCellSDHFromRowCol(m_Table.getTableSDH(),true,PD_MAX_REVISION,row,col);
	if(sdhCell == NULL)
	{
		return -1;
	}
	const char * szRight;
	m_pDocument->getPropertyFromSDH(sdhCell,true,PD_MAX_REVISION,"right-attach",&szRight);
	UT_sint32 iRight = atoi(szRight);
	return iRight;
}
	
void s_RTF_ListenerWriteDoc::_newRow(void)
{
	UT_sint32 i;
	m_Table.incCurRow();
	m_pie->_rtf_nl();
	if(m_Table.getNestDepth() > 1)
	{
		m_pie->_rtf_keyword("itap",m_Table.getNestDepth());
		m_pie->_rtf_open_brace();
		m_pie->_rtf_keyword("*");
		m_pie->_rtf_keyword("nesttableprops");
	} 
	m_pie->_rtf_keyword("trowd");
	m_pie->write(" ");
	m_pie->_rtf_keyword("itap",m_Table.getNestDepth());

//
// Set spacing between cells
//
	const char * szColSpace = m_Table.getTableProp("table-col-spacing");
	if(szColSpace && *szColSpace)
	{
		double dspace = UT_convertToInches(szColSpace) * 360.0;
		UT_sint32 iSpace =0;
		iSpace = static_cast<UT_sint32>(dspace);
		m_pie->_rtf_keyword("trgaph",iSpace);
	}
	else
	{
		m_pie->_rtf_keyword("trgaph",36);
		szColSpace = "0.05in";
	}
	double dColSpace = UT_convertToInches(szColSpace);
//
// Hardwire left-justification (for now)
//
	m_pie->_rtf_keyword("trql");
//
// Height of row. Hardwired to zero (take maximum cell height for row) for now.
//
	m_pie->_rtf_keyword("trrh",0);
//
// Lookup column positions.
//
	const char * szColumnProps = NULL;
	const char * szColumnLeftPos = NULL;
	szColumnProps = m_Table.getTableProp("table-column-props");
	szColumnLeftPos = m_Table.getTableProp("table-column-leftpos");
	double cellLeftPos = 0;
	if(szColumnLeftPos && *szColumnLeftPos)
	{
		cellLeftPos = UT_convertToInches(szColumnLeftPos);
	}
	UT_sint32 iLeftTwips = 0;
	iLeftTwips =  (UT_sint32) (cellLeftPos*1440.0);
	m_pie->_rtf_keyword("trleft",iLeftTwips);
	UT_GenericVector<fl_ColProps *> vecColProps;
	vecColProps.clear();
	if(szColumnProps && *szColumnProps)
	{
		UT_String sProps = szColumnProps;
		UT_sint32 sizes = sProps.size();
		i =0 ;
		UT_sint32 j =0;
		while(i < sizes)
		{
			for (j=i; (j<sizes) && (sProps[j] != '/') ; j++) {}
			if((j+1)>i && sProps[j] == '/')
			{
				UT_String sSub = sProps.substr(i,(j-i));
				double colWidth = UT_convertToInches(sSub.c_str())* 10000.0;
				i = j + 1;
				fl_ColProps * pColP = new fl_ColProps;
				pColP->m_iColWidth = static_cast<UT_sint32>(colWidth);
				vecColProps.addItem(pColP);
			}
		}
	}
	else
	{
//
// Autofit (or not) the row. Look up col widths
//
		m_pie->_rtf_keyword("trautofit",1);
	}
//
// Handle table line types.
//
	const char * szLineThick = m_Table.getTableProp("table-line-thickness");
	UT_sint32 iThick = -1;
	if(szLineThick && *szLineThick)
	{
		iThick = atoi(szLineThick);
		if(iThick > 0)
		{
			_outputTableBorders(iThick);
		}
	}
	else
	{
		_outputTableBorders(1);
	}
//
// OK now output all the cell properties, including merged cell controls.
//
	UT_sint32 row = m_Table.getCurRow();
	UT_sint32 col = m_Table.getLeft();
	double cellpos = cellLeftPos + dColSpace*0.5;
	double colwidth = 0.0;
	double dcells = static_cast<double>(m_Table.getNumCols());
#if 0
//
// fixme. Write this function to determine the width of a nested cell
//
	if(m_Table.getNestDepth() < 2)
	{
		colwidth = (_getColumnWidthInches() - dColSpace*0.5)/dcells;
	}
	else
	{
		colwidth = m_Table.findThisColWidth();
	}
#endif
	colwidth = (_getColumnWidthInches() - dColSpace*0.5)/dcells;

	UT_sint32 iNext = 1;
	UT_String sTableProps;
	PT_AttrPropIndex tableAPI = m_Table.getTableAPI();
	_fillTableProps(tableAPI,sTableProps);
	
	for(i=0; i < m_Table.getNumCols(); i = iNext)
	{
		m_Table.setCellRowCol(row,i);
		xxx_UT_DEBUGMSG(("SEVIOR: set to row %d i %d left %d right %d \n",row,i,m_Table.getLeft(),m_Table.getRight()));
		if(m_Table.getRight() <= i)
		{
			PL_StruxDocHandle cellSDH = m_pDocument->getCellSDHFromRowCol(m_Table.getTableSDH(),true,PD_MAX_REVISION,
																		  row,i);
			UT_ASSERT_HARMLESS(cellSDH);
			if(cellSDH)
			{
				m_pDocument->miniDump(cellSDH,8);
			}
			UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
			iNext = i+1;
		}
		else
		{
			iNext = m_Table.getRight();
		}
//
// Export all the properties of the cell
//
		PT_AttrPropIndex cellAPI = m_Table.getCellAPI();
		_exportCellProps(cellAPI,sTableProps);
//
// Look if we have a vertically merged cell at this (row,i)
//
		bool vMerge = false;
		if(m_Table.getTop() < row)
		{
			m_pie->_rtf_keyword("clvmrg");
			vMerge = true;
		}
//
// Look to see if this is the first cell of a set of vertically merged cells
//		
		if((m_Table.getBot() > row +1) && (m_Table.getTop() == row))
		{
			m_pie->_rtf_keyword("clvmgf");
		}
#if 0
//
// Look to see if we have a horizontally merged cell.
//
		if(m_bNewTable && (m_Table.getLeft() < i))
		{
			m_pie->_rtf_keyword("clmrg");
		}
//
// Look to see if this is the first of a group of horizonatally merged cells.
//
		if(m_bNewTable && (m_Table.getRight() > i +1))
		{
			m_pie->_rtf_keyword("clmrgf");
		}
#endif
//
// output cellx for each cell
//
		double thisX = 0.0;
		UT_sint32 j =0;
		if(vecColProps.getItemCount() > 0)
		{
			for(j= 0; (j< m_Table.getRight()) && (j < static_cast<UT_sint32>(vecColProps.getItemCount())); j++)
			{
				fl_ColProps * pColP = vecColProps.getNthItem(j);
				double bigWidth = static_cast<double>(pColP->m_iColWidth);
				thisX += bigWidth/10000.0;
			}
		}
		else
		{
			for(j= 0; j< m_Table.getRight(); j++)
			{
				thisX += colwidth;
			}
		}
		thisX += cellpos;
		UT_sint32 iCellTwips = 0;
		iCellTwips = (UT_sint32) (thisX*1440.0);
		m_pie->_rtf_keyword("cellx",iCellTwips);
	}
	if(vecColProps.getItemCount() > 0)
	{
		UT_VECTOR_PURGEALL(fl_ColProps *,vecColProps);
	}
	m_Table.setCellRowCol(row,col);
}

void s_RTF_ListenerWriteDoc::_outputTableBorders(UT_sint32 iThick)
{
	m_pie->_rtf_keyword("trbrdrt"); // top border
	m_pie->_rtf_keyword("brdrs"); // plain border
	m_pie->_rtf_keyword("brdrw",10*iThick); //border thickness
	m_pie->write(" ");											
	m_pie->_rtf_keyword("trbrdrl"); // left border
	m_pie->_rtf_keyword("brdrs");
	m_pie->_rtf_keyword("brdrw",10*iThick); // border thickness
	m_pie->write(" ");											
	m_pie->_rtf_keyword("trbrdrb"); // bottom border
	m_pie->_rtf_keyword("brdrs");
	m_pie->_rtf_keyword("brdrw",10*iThick); // border thickness
	m_pie->write(" ");											
	m_pie->_rtf_keyword("trbrdrr"); // right border
	m_pie->_rtf_keyword("brdrs");
	m_pie->_rtf_keyword("brdrw",10*iThick); // border thickness
	m_pie->write(" ");											
}

void s_RTF_ListenerWriteDoc::_outputCellBorders(UT_sint32 iThick)
{
	m_pie->_rtf_keyword("clbrdrt"); // cell top border
	m_pie->_rtf_keyword("brdrs"); // plain border
	m_pie->_rtf_keyword("brdrw",10*iThick); //border thickness
	m_pie->write(" ");											
	m_pie->_rtf_keyword("clbrdrl"); // cell left border
	m_pie->_rtf_keyword("brdrs");
	m_pie->_rtf_keyword("brdrw",10*iThick); // border thickness
	m_pie->write(" ");											
	m_pie->_rtf_keyword("clbrdrb"); // cell bottom border
	m_pie->_rtf_keyword("brdrs");
	m_pie->_rtf_keyword("brdrw",10*iThick); // border thickness
	m_pie->write(" ");											
	m_pie->_rtf_keyword("clbrdrr"); // cell right border
	m_pie->_rtf_keyword("brdrs");
	m_pie->_rtf_keyword("brdrw",10*iThick); // border thickness
	m_pie->write(" ");											
}

double s_RTF_ListenerWriteDoc::_getColumnWidthInches(void)
{
	double pageWidth = m_pDocument->m_docPageSize.Width(DIM_IN);

	const PP_AttrProp * pSpanAP = NULL;
	const PP_AttrProp * pBlockAP = NULL;
	const PP_AttrProp * pSectionAP = NULL;
	m_pDocument->getAttrProp(m_apiThisSection,&pSectionAP);
	const XML_Char * szColumns = PP_evalProperty("columns",
												 pSpanAP,pBlockAP,pSectionAP,
												 m_pDocument,true);
	const XML_Char * szColumnGap = PP_evalProperty("column-gap",
												   pSpanAP,pBlockAP,pSectionAP,
												   m_pDocument,true);
	const XML_Char * szMarginLeft = PP_evalProperty("page-margin-left",
													pSpanAP,pBlockAP,pSectionAP,
													m_pDocument,true);
	const XML_Char * szMarginRight = PP_evalProperty("page-margin-right",
													 pSpanAP,pBlockAP,pSectionAP,
													 m_pDocument,true);
	UT_sint32 iNumCols = 1;
	if(szColumns && *szColumns)
	{
		iNumCols = atoi(szColumns);
	}
	double dNumCols = static_cast<double>(iNumCols);
	double lMarg = UT_convertToInches(szMarginLeft);
	double rMarg = UT_convertToInches(szMarginRight);
	double dGap = UT_convertToInches(szColumnGap);
	double colWidth = pageWidth - lMarg - rMarg - dGap*(dNumCols - 1.0);
	colWidth = colWidth/dNumCols;
	return colWidth;
}

/*!
 * This exports all the properties in atable strux by extend rtf with
 * a \*\abitableproperties keyword.
 * Code mostly copied for fl_TableLayout::_lookupProperties. This code should
 * should be updated when new properties are defined.
 */
void s_RTF_ListenerWriteDoc::_export_AbiWord_Table_props(PT_AttrPropIndex api)
{
//
// Export abiword table properties as an extension
// Use these for cutting and pasting within abiword.
//
	m_pie->_rtf_open_brace();
	m_pie->_rtf_keyword("*");
	UT_String sTableProps;
	sTableProps.clear();
	_fillTableProps(api,sTableProps);
	xxx_UT_DEBUGMSG(("Table props are %s \n",sTableProps.c_str()));
	m_pie->_rtf_keyword("abitableprops ",sTableProps.c_str());
	m_pie->_rtf_close_brace();
}

/*!
 * This method fills the suppiled string with all the properties of the 
 * table given by api
 */
void s_RTF_ListenerWriteDoc::_fillTableProps(PT_AttrPropIndex api, UT_String & sTableProps)
{
	const PP_AttrProp* pSectionAP = NULL;
	m_pDocument->getAttrProp(api, &pSectionAP);
	const XML_Char* pszHomogeneous = NULL;
	pSectionAP->getProperty("homogeneous", pszHomogeneous);
	UT_String sPropVal;
	UT_String sProp;
	if (pszHomogeneous && pszHomogeneous[0])
	{
		if(atoi(pszHomogeneous) == 1)
		{
			sProp = "homogeneous";
			sPropVal= "1";
			UT_String_setProperty(sTableProps,sProp,sPropVal);
		}
	}
	const XML_Char* pszLeftOffset = NULL;
	const XML_Char* pszTopOffset = NULL;
	const XML_Char* pszRightOffset = NULL;
	const XML_Char* pszBottomOffset = NULL;
	pSectionAP->getProperty("table-margin-left", pszLeftOffset);
	pSectionAP->getProperty("table-margin-top", pszTopOffset);
	pSectionAP->getProperty("table-margin-right", pszRightOffset);
	pSectionAP->getProperty("table-margin-bottom", pszBottomOffset);

	if(pszLeftOffset && pszLeftOffset[0])
	{
		sProp = "table-margin-left";
		sPropVal= pszLeftOffset;
		UT_String_setProperty(sTableProps,sProp,sPropVal);
	}

	if(pszTopOffset && pszTopOffset[0])
	{
		sProp = "table-margin-top";
		sPropVal= pszTopOffset;
		UT_String_setProperty(sTableProps,sProp,sPropVal);
	}

	if(pszRightOffset && pszRightOffset[0])
	{
		sProp = "table-margin-right";
		sPropVal= pszRightOffset;
		UT_String_setProperty(sTableProps,sProp,sPropVal);
	}

	if(pszBottomOffset && pszBottomOffset[0])
	{
		sProp = "table-margin-bottom";
		sPropVal= pszBottomOffset;
		UT_String_setProperty(sTableProps,sProp,sPropVal);
	}

	const XML_Char * pszLineThick = NULL;
	pSectionAP->getProperty("table-line-thickness", pszLineThick);
	if(pszLineThick && *pszLineThick)
	{
		sProp = "table-line-thickness";
		sPropVal= pszLineThick;
		UT_String_setProperty(sTableProps,sProp,sPropVal);
	}

	const XML_Char * pszTableColSpacing = NULL;
	const XML_Char * pszTableRowSpacing = NULL;
	pSectionAP->getProperty("table-col-spacing", pszTableColSpacing);
	pSectionAP->getProperty("table-row-spacing", pszTableRowSpacing);
	if(pszTableColSpacing && *pszTableColSpacing)
	{
		sProp = "table-col-spacing";
		sPropVal= pszTableColSpacing;
		UT_String_setProperty(sTableProps,sProp,sPropVal);
	}
	if(pszTableRowSpacing && *pszTableRowSpacing)
	{
		sProp = "table-row-spacing";
		sPropVal= pszTableRowSpacing;
		UT_String_setProperty(sTableProps,sProp,sPropVal);
	}
	const XML_Char * pszLeftColPos = NULL;
	const XML_Char * pszColumnProps = NULL;
	pSectionAP->getProperty("table-column-leftpos", pszLeftColPos);
	pSectionAP->getProperty("table-column-props", pszColumnProps);
	if(pszLeftColPos && *pszLeftColPos)
	{
		sProp = "table-column-leftpos";
		sPropVal= pszLeftColPos;
		UT_String_setProperty(sTableProps,sProp,sPropVal);
	}
	if(pszColumnProps && *pszColumnProps)
	{
		sProp = "table-column-props";
		sPropVal= pszColumnProps;
		UT_String_setProperty(sTableProps,sProp,sPropVal);
	}
//
// global row height type
//
	const XML_Char * pszRowHeightType = NULL;
	const XML_Char * pszRowHeight = NULL;
	pSectionAP->getProperty("table-row-height-type", pszRowHeightType);
	if(pszRowHeightType && *pszRowHeightType)
	{
		sProp = "table-row-height-type";
		sPropVal= pszRowHeightType;
		UT_String_setProperty(sTableProps,sProp,sPropVal);
	}
	pSectionAP->getProperty("table-row-height", pszRowHeight);
	if(pszRowHeight && *pszRowHeight)
	{
		sProp = "table-row-height";
		sPropVal= pszRowHeight;
		UT_String_setProperty(sTableProps,sProp,sPropVal);
	}
//
// Positioned row controls
//
	const XML_Char * pszRowHeights = NULL;
	pSectionAP->getProperty("table-row-heights", pszRowHeights);
	if(pszRowHeights && *pszRowHeights)
	{
		sProp = "table-row-heights";
		sPropVal= pszRowHeights;
		UT_String_setProperty(sTableProps,sProp,sPropVal);
	}

	/* table-border properties:
	 */
	const XML_Char * pszColor = NULL;
	pSectionAP->getProperty ("color", pszColor);
	if (pszColor)
	{
		sProp = "color";
		sPropVal= pszColor;
		UT_String_setProperty(sTableProps,sProp,sPropVal);
	}
	const XML_Char * pszBorderColor = NULL;
	const XML_Char * pszBorderStyle = NULL;
	const XML_Char * pszBorderWidth = NULL;

	pSectionAP->getProperty ("bot-color",       pszBorderColor);
	if (pszBorderColor && *pszBorderColor)
	{
		sProp = "bot-color";
		sPropVal= pszBorderColor;
		UT_String_setProperty(sTableProps,sProp,sPropVal);
	}
	pSectionAP->getProperty ("bot-style",       pszBorderStyle);
	if (pszBorderStyle && *pszBorderStyle)
	{
		sProp = "bot-style";
		sPropVal= pszBorderStyle;
		UT_String_setProperty(sTableProps,sProp,sPropVal);
	}

	pSectionAP->getProperty ("bot-thickness",   pszBorderWidth);
	if (pszBorderWidth && *pszBorderWidth)
	{
		sProp = "bot-thickness";
		sPropVal= pszBorderStyle;
		UT_String_setProperty(sTableProps,sProp,sPropVal);
	}
	pszBorderColor = NULL;
	pszBorderStyle = NULL;
	pszBorderWidth = NULL;

	pSectionAP->getProperty ("left-color",      pszBorderColor);
	pSectionAP->getProperty ("left-style",      pszBorderStyle);
	pSectionAP->getProperty ("left-thickness",  pszBorderWidth);

	if (pszBorderColor && *pszBorderColor)
	{
		sProp = "left-color";
		sPropVal= pszBorderColor;
		UT_String_setProperty(sTableProps,sProp,sPropVal);
	}
	if (pszBorderStyle && *pszBorderStyle)
	{
		sProp = "left-style";
		sPropVal= pszBorderStyle;
		UT_String_setProperty(sTableProps,sProp,sPropVal);
	}
	if (pszBorderWidth && *pszBorderWidth)
	{
		sProp = "left-thickness";
		sPropVal= pszBorderStyle;
		UT_String_setProperty(sTableProps,sProp,sPropVal);
	}

	pszBorderColor = NULL;
	pszBorderStyle = NULL;
	pszBorderWidth = NULL;

	pSectionAP->getProperty ("right-color",     pszBorderColor);
	pSectionAP->getProperty ("right-style",     pszBorderStyle);
	pSectionAP->getProperty ("right-thickness", pszBorderWidth);

	if (pszBorderColor && *pszBorderColor)
	{
		sProp = "right-color";
		sPropVal= pszBorderColor;
		UT_String_setProperty(sTableProps,sProp,sPropVal);
	}
	if (pszBorderStyle && *pszBorderStyle)
	{
		sProp = "right-style";
		sPropVal= pszBorderStyle;
		UT_String_setProperty(sTableProps,sProp,sPropVal);
	}
	if (pszBorderWidth && *pszBorderWidth)
	{
		sProp = "right-thickness";
		sPropVal= pszBorderStyle;
		UT_String_setProperty(sTableProps,sProp,sPropVal);
	}

	pszBorderColor = NULL;
	pszBorderStyle = NULL;
	pszBorderWidth = NULL;

	pSectionAP->getProperty ("top-color",       pszBorderColor);
	pSectionAP->getProperty ("top-style",       pszBorderStyle);
	pSectionAP->getProperty ("top-thickness",   pszBorderWidth);
	if (pszBorderColor && *pszBorderColor)
	{
		sProp = "top-color";
		sPropVal= pszBorderColor;
		UT_String_setProperty(sTableProps,sProp,sPropVal);
	}
	if (pszBorderStyle && *pszBorderStyle)
	{
		sProp = "top-style";
		sPropVal= pszBorderStyle;
		UT_String_setProperty(sTableProps,sProp,sPropVal);
	}
	if (pszBorderWidth && *pszBorderWidth)
	{
		sProp = "top-thickness";
		sPropVal= pszBorderStyle;
		UT_String_setProperty(sTableProps,sProp,sPropVal);
	}

	/* table fill
	 */
	const XML_Char * pszBgStyle = NULL;
	const XML_Char * pszBgColor = NULL;
	const XML_Char * pszBackgroundColor = NULL;

	pSectionAP->getProperty ("bg-style",         pszBgStyle);
	if (pszBgStyle && *pszBgStyle)
	{
		sProp = "bg-style";
		sPropVal= pszBgStyle;
		UT_String_setProperty(sTableProps,sProp,sPropVal);
	}
	pSectionAP->getProperty ("bgcolor",          pszBgColor);
	if (pszBgColor && *pszBgColor)
	{
		sProp = "bgcolor";
		sPropVal= pszBgColor;
		UT_String_setProperty(sTableProps,sProp,sPropVal);
	}
	pSectionAP->getProperty ("background-color", pszBackgroundColor);
	if (pszBackgroundColor && *pszBackgroundColor)
	{
		sProp = "background-color";
		sPropVal= pszBackgroundColor;
		UT_String_setProperty(sTableProps,sProp,sPropVal);
	}
	sProp = "table-sdh";
	UT_String_sprintf(sPropVal,"%x",m_Table.getTableSDH());
	UT_String_setProperty(sTableProps,sProp,sPropVal);
	if(sTableProps.size() == 0)
	{
		sTableProps += " ";
	}
}

void s_RTF_ListenerWriteDoc::_open_table(PT_AttrPropIndex api,bool bIsCell)
{
	PL_StruxDocHandle sdhTable = NULL;
	if(bIsCell)
	{
		PT_DocPosition posCell = m_pDocument->getStruxPosition(m_sdh);
		bool b = m_pDocument->getStruxOfTypeFromPosition(posCell,PTX_SectionTable,&sdhTable);
		UT_return_if_fail(b);
		api = m_pDocument->getAPIFromSDH(sdhTable);
		m_Table.OpenTable(sdhTable,api);
	}
	else
	{
		m_Table.OpenTable(m_sdh,api);
	}
	m_bNewTable = true;
	m_iLeft = -1;
	m_iRight = -1;
	m_iTop = -1;
	m_iBot = -1;
	m_iFirstTop = 0;
//
// Export the AbiWord table Properties as RTF extension
//
	_export_AbiWord_Table_props(api);
#if 1 //#TF
	m_pie->_rtf_keyword("par");
#endif
	
	if(m_Table.getNestDepth() > 1)
	{
		m_pie->_rtf_open_brace();
	}
}

void s_RTF_ListenerWriteDoc::_close_cell(void)
{
	if(m_Table.getNestDepth() < 1)
	{
		return;
	}
	if(m_Table.getNestDepth() < 2)
	{
		m_pie->_rtf_keyword("cell");
	}
	else
	{
		m_pie->_rtf_keyword("nestcell");
	}
	m_pie->_rtf_open_brace();
	m_pie->_rtf_keyword("*");
	m_pie->_rtf_keyword("abiendcell");
	m_pie->_rtf_close_brace();

	m_Table.CloseCell();
}

void s_RTF_ListenerWriteDoc::_close_table(void)
{
//
// First output the cells we need to cover any vertically merged cells
//
	UT_sint32 count = m_Table.getNumCols() - m_Table.getRight();
	UT_sint32 i = 0;
	for(i=0; i< count; i++)
	{
		m_pie->_rtf_keyword("cell");
	}
//
// Close off the last row
//
	if(m_Table.getNestDepth() < 2)
	{
		m_pie->_rtf_keyword("row");
	}
	else
	{				
		_newRow();
		m_pie->_rtf_keyword("nestrow");
	}
	m_pie->_rtf_close_brace();
	if(m_Table.getNestDepth() > 1)
	{
		m_pie->_rtf_close_brace();
	}
	m_Table.CloseTable();
	if(m_Table.getNestDepth() < 1)
	{
		m_iCurRow = -1;
		m_iLeft = -1;
		m_iRight = -1;
		m_iTop = -1;
		m_iBot = -1;
	}
	else
	{
		m_iCurRow = m_Table.getTop();
		m_iLeft = m_Table.getLeft();
		m_iRight = m_Table.getRight();
		m_iTop = m_Table.getTop();
		m_iBot = m_Table.getBot();
	}
	m_pie->_rtf_open_brace();
	m_pie->_rtf_keyword("*");
	m_pie->_rtf_keyword("abiendtable");
	m_pie->_rtf_close_brace();
}


bool s_RTF_ListenerWriteDoc::populateStrux(PL_StruxDocHandle sdh,
										   const PX_ChangeRecord * pcr,
										   PL_StruxFmtHandle * psfh)
{
	UT_return_val_if_fail(pcr->getType() == PX_ChangeRecord::PXT_InsertStrux, false);
	const PX_ChangeRecord_Strux * pcrx = static_cast<const PX_ChangeRecord_Strux *> (pcr);
	*psfh = 0;							// we don't need it.

	m_posDoc = pcrx->getPosition();
	switch (pcrx->getStruxType())
	{
	case PTX_Section:
		{

			// begin a new section.  in RTF this is expressed as
			//
			// <section> := <secfmt>* <hdrftr>? <para>+ (\sect <section>)?
			//
			// here we deal with everything except for the <para>+
//
// OK first we have so see if there is a header/footer associated with this section
//
			PT_AttrPropIndex indexAP = pcr->getIndexAP();
			const PP_AttrProp* pAP = NULL;
			m_pDocument->getAttrProp(indexAP, &pAP);
			const XML_Char* pszHeaderID = NULL;
			bool bHeader = false;
			const XML_Char* pszFooterID = NULL;
			bool bFooter = false;
			const XML_Char* pszHeaderEvenID = NULL;
			bool bHeaderEven = false;
			const XML_Char* pszFooterEvenID = NULL;
			bool bFooterEven = false;
			const XML_Char* pszHeaderFirstID = NULL;
			bool bHeaderFirst = false;
			const XML_Char* pszFooterFirstID = NULL;
			bool bFooterFirst = false;
			const XML_Char* pszHeaderLastID = NULL;
			bool bHeaderLast = false;
			const XML_Char* pszFooterLastID = NULL;
			bool bFooterLast = false;
			
// header,headerl (odd) ,headerr(even) ,headerf(first) ,footer,footerl,footerr,footerf

			pAP->getAttribute("header", pszHeaderID);
			if(pszHeaderID != NULL)
			{
				bHeader = true;
			}
			pAP->getAttribute("footer", pszFooterID);
			if(pszFooterID != NULL)
			{
				bFooter = true;
			}
			pAP->getAttribute("header-even", pszHeaderEvenID);
			if(pszHeaderEvenID != NULL)
			{
				bHeaderEven = true;
			}

			pAP->getAttribute("footer-even", pszFooterEvenID);
			if(pszFooterEvenID != NULL)
			{
				bFooterEven = true;
			}
			pAP->getAttribute("header-first", pszHeaderFirstID);
			if(pszHeaderFirstID != NULL)
			{
				bHeaderFirst = true;
			}

			pAP->getAttribute("footer-first", pszFooterFirstID);
			if(pszFooterFirstID != NULL)
			{
				bFooterFirst = true;
			}
			pAP->getAttribute("header-last", pszHeaderLastID);
			if(pszHeaderLastID != NULL)
			{
				bHeaderLast = true;
			}

			pAP->getAttribute("footer-last", pszFooterLastID);
			if(pszFooterLastID != NULL)
			{
				bFooterLast = true;
			}
			if(bHeader && !bHeaderEven)
			{
			        m_bInBlock = false;
				m_pie->exportHdrFtr("header",pszHeaderID,"header");
			}
			else if(bHeader)
			{
				m_bInBlock = false;
				m_pie->exportHdrFtr("header",pszHeaderID,"headerl");
			}
			if(bHeaderEven)
			{
				m_bInBlock = false;
				m_pie->exportHdrFtr("header-even",pszHeaderEvenID,"headerr");
			}
			if(bHeaderFirst)
			{
				m_bInBlock = false;
				m_pie->exportHdrFtr("header-first",pszHeaderFirstID,"headerf");
			}
			if(bFooter && !bFooterEven)
			{
			        m_bInBlock = false;
				m_pie->exportHdrFtr("footer",pszFooterID,"footer");
			}
			else if(bFooter)
			{
				m_bInBlock = false;
				m_pie->exportHdrFtr("footer",pszFooterID,"footerl");
			}
			if(bFooterEven)
			{
				m_bInBlock = false;
				m_pie->exportHdrFtr("footer-even",pszFooterEvenID,"footerr");
			}
			if(bFooterFirst)
			{
				m_bInBlock = false;
				m_pie->exportHdrFtr("footer-first",pszFooterFirstID,"footerf");
			}
			_closeSpan();
#if 0 // #TF
			_closeBlock();
#endif
			_closeSection();
			_setTabEaten(false);

			m_sdh = sdh;
			_rtf_open_section(pcr->getIndexAP());
			m_bInBlock = false;
			m_bBlankLine = true;
			return true;
		}

	case PTX_SectionHdrFtr:
		{
			_closeSpan();
#if 0 //#TF
			_closeBlock();
#endif
			_closeSection();
			_setTabEaten(false);
			return false;
		}
	case PTX_SectionFootnote:
	    {
			_closeSpan();
			m_bOpennedFootnote = true;

			// we set m_bInBlock to false to prevent issue of \par keyword; the block
			// which gets inserted into the footnote resets this into the normal state, so
			// that when we exit the footnote section, we will be again in block and the
			// block that contains the footnote will get closed as normal
			m_bInBlock = false;
			m_apiSavedBlock = m_apiThisBlock;
			m_sdhSavedBlock = m_sdh;
			_setTabEaten(false);
			m_sdh = sdh;
			m_pie->_rtf_open_brace();
			m_pie->_rtf_keyword("footnote");
			xxx_UT_DEBUGMSG(("_rtf_listenerWriteDoc: Openned Footnote \n"));
			return true;
		}
	case PTX_EndFootnote:
	    {
			_closeSpan();
			_setTabEaten(false);
			m_sdh = m_sdhSavedBlock;
			m_apiThisBlock = m_apiSavedBlock;
			m_pie->_rtf_close_brace();
			xxx_UT_DEBUGMSG(("_rtf_listenerWriteDoc: Closed Footnote \n"));
			return true;
		}
	case PTX_SectionFrame:
	    {
			_closeSpan();
			// see comments under case PTX_SectionFootnote:
			m_bInBlock = false;
			_setTabEaten(false);
			m_sdh = NULL;
			_openFrame(pcr->getIndexAP());
			UT_DEBUGMSG(("_rtf_listenerWriteDoc: openned Frame \n"));
			return true;
		}
	case PTX_EndFrame:
	    {

			_closeSpan();
			_setTabEaten(false);
			m_sdh = sdh;
			_closeFrame();
			return true;
		}
	case PTX_SectionTOC:
	    {
			_closeSpan();

			// see comments under case PTX_SectionFootnote:
			m_bInBlock = false;
			_setTabEaten(pcr->getIndexAP());
			m_sdh = sdh;
			UT_DEBUGMSG(("_rtf_listenerWriteDoc: Found TOC \n"));
			_writeTOC(pcr->getIndexAP());
			return true;
		}
	case PTX_EndTOC:
	    {

			_closeSpan();
			_setTabEaten(false);
			m_sdh = NULL;
			return true;
		}
	case PTX_SectionEndnote:
	    {
			_closeSpan();
			m_bOpennedFootnote = true;

			// see comments under case PTX_SectionFootnote:
			m_bInBlock = false;
			m_apiSavedBlock = m_apiThisBlock;
			m_sdhSavedBlock = m_sdh;
			_setTabEaten(false);
			m_sdh = sdh;
			m_pie->_rtf_open_brace();
			m_pie->_rtf_keyword("footnote");
			m_pie->_rtf_keyword("ftnalt");
			xxx_UT_DEBUGMSG(("_rtf_listenerWriteDoc: Openned Endnote \n"));
			return true;
		}
	case PTX_EndEndnote:
	    {
			_closeSpan();

			_setTabEaten(false);
			m_sdh = m_sdhSavedBlock;
			m_apiThisBlock = m_apiSavedBlock;
			m_pie->_rtf_close_brace();
			xxx_UT_DEBUGMSG(("_rtf_listenerWriteDoc: Closed Endnote \n"));
			return true;
		}
	case PTX_SectionTable:
	    {
			_closeSpan();
			_setTabEaten(false);
			m_sdh = sdh;
			_open_table(pcr->getIndexAP());
			xxx_UT_DEBUGMSG(("_rtf_listenerWriteDoc: openned table \n"));
			return true;
		}
	case PTX_SectionCell:
	    {
			_closeSpan();
			// in rtf cell is a block, while in AW cell contains a block
			// in order to avoid a superfluous paragraph marker we will pretend that we
			// are not in a block
			// see comments under case PTX_SectionFootnote:
			m_bInBlock = false;
			_setTabEaten(false);
			m_sdh = sdh;
			m_bBlankLine = true; // Need this as well!
			_open_cell(pcr->getIndexAP());
			return true;
		}
	case PTX_EndTable:
	    {
			_closeSpan();
			m_bInBlock = false;
			_setTabEaten(false);
			m_sdh = sdh;
			_close_table();
			return true;
		}
	case PTX_EndCell:
	    {

			_closeSpan();
			m_bInBlock = false;
			_setTabEaten(false);
			m_sdh = sdh;
			_close_cell();
			return true;
		}
	case PTX_Block:
		{
			xxx_UT_DEBUGMSG(("_rtf_listenerWriteDoc: Populate block \n"));
			_closeSpan();
			if(!m_bBlankLine && !m_bOpennedFootnote)
			{
				m_bInBlock = true;
			}
			_closeBlock(pcr->getIndexAP());
			_setListBlock(false);
			_setTabEaten(false);
			m_sdh = sdh;
			_rtf_open_block(pcr->getIndexAP());
			m_bBlankLine = true;	
			m_bInBlock = true;
		return true;
		}

	default:
		UT_ASSERT_NOT_REACHED();
		return false;
	}
}

bool s_RTF_ListenerWriteDoc::change(PL_StruxFmtHandle /*sfh*/,
									const PX_ChangeRecord * /*pcr*/)
{
	UT_ASSERT_NOT_REACHED();	// this function is not used.
	return false;
}

bool s_RTF_ListenerWriteDoc::insertStrux(PL_StruxFmtHandle /*sfh*/,
										  const PX_ChangeRecord * /*pcr*/,
										  PL_StruxDocHandle /*sdh*/,
										  PL_ListenerId /* lid */,
										  void (* /*pfnBindHandles*/)(PL_StruxDocHandle /* sdhNew */,
																	  PL_ListenerId /* lid */,
																	  PL_StruxFmtHandle /* sfhNew */))
{
	UT_ASSERT_NOT_REACHED();	// this function is not used.
	return false;
}

bool s_RTF_ListenerWriteDoc::signal(UT_uint32 /* iSignal */)
{
	UT_ASSERT_NOT_REACHED();	// this function is not used.
	return false;
}

//////////////////////////////////////////////////////////////////

/*
  {info

  {\title     #PCDATA}
  {\author    #PCDATA}
  {\manager   #PCDATA}
  {\company   #PCDATA}
  {\category  #PCDATA}
  {\keywords  #PCDATA}
  {\comment   #PCDATA}
  {\doccomm   #PCDATA}

  TODO:
  \userprops
    \propname
    \proptype
    \staticval

  }
 */
void s_RTF_ListenerWriteDoc::_rtf_info(void)
{
	if (!m_pie->isCopying ()) {
		UT_UTF8String propVal ;
		
		m_pie->_rtf_open_brace () ;
		m_pie->_rtf_keyword("info");
		
		if(m_pDocument->getMetaDataProp (PD_META_KEY_TITLE, propVal) && propVal.size())
		{ m_pie->_rtf_open_brace () ; m_pie->_rtf_keyword("title ",propVal.utf8_str()); m_pie->_rtf_close_brace(); }
		if(m_pDocument->getMetaDataProp (PD_META_KEY_CREATOR, propVal) && propVal.size())
		{ m_pie->_rtf_open_brace () ; m_pie->_rtf_keyword("author ",propVal.utf8_str()); m_pie->_rtf_close_brace(); }
		if(m_pDocument->getMetaDataProp (PD_META_KEY_CONTRIBUTOR, propVal) && propVal.size())
		{ m_pie->_rtf_open_brace () ; m_pie->_rtf_keyword("manager ",propVal.utf8_str()); m_pie->_rtf_close_brace(); }
		if(m_pDocument->getMetaDataProp (PD_META_KEY_PUBLISHER, propVal) && propVal.size())
		{ m_pie->_rtf_open_brace () ; m_pie->_rtf_keyword("company ",propVal.utf8_str()); m_pie->_rtf_close_brace(); }
		if(m_pDocument->getMetaDataProp (PD_META_KEY_SUBJECT, propVal) && propVal.size())
		{ m_pie->_rtf_open_brace () ; m_pie->_rtf_keyword("category ",propVal.utf8_str()); m_pie->_rtf_close_brace(); }
		if(m_pDocument->getMetaDataProp (PD_META_KEY_KEYWORDS, propVal) && propVal.size())
		{ m_pie->_rtf_open_brace () ; m_pie->_rtf_keyword("keywords ",propVal.utf8_str()); m_pie->_rtf_close_brace(); }
		if(m_pDocument->getMetaDataProp (PD_META_KEY_DESCRIPTION, propVal) && propVal.size())
		{ m_pie->_rtf_open_brace () ; m_pie->_rtf_keyword("comment ",propVal.utf8_str()); m_pie->_rtf_close_brace(); }
		if(m_pDocument->getMetaDataProp (PD_META_KEY_DESCRIPTION, propVal) && propVal.size())
		{ m_pie->_rtf_open_brace () ; m_pie->_rtf_keyword("doccomm ",propVal.utf8_str()); m_pie->_rtf_close_brace(); }
		
		m_pie->_rtf_close_brace();
	}
}

void s_RTF_ListenerWriteDoc::_rtf_docfmt(void)
{
	// emit everything necessary for <docfmt>* portion of the document

	const PP_AttrProp * pSpanAP = NULL;
	const PP_AttrProp * pBlockAP = NULL;
	const PP_AttrProp * pSectionAP = NULL;

	// <docfmt>

	const XML_Char * szDefaultTabs = PP_evalProperty("default-tab-interval",
													 pSpanAP,pBlockAP,pSectionAP,
													 m_pDocument,true);
	m_pie->_rtf_keyword_ifnotdefault_twips("deftab",static_cast<const char*>(szDefaultTabs),1440);

	// <docfmt> -- document views and zoom level

	m_pie->_rtf_keyword("viewkind",1);	/* PageLayout */

	// TODO <docfmt> -- footnotes and endnotes

	// <docfmt> -- page information

	UT_String szPaperWidth;
	UT_String szPaperHeight;

	bool landscape = !m_pDocument->m_docPageSize.isPortrait();

	{
		UT_LocaleTransactor(LC_NUMERIC, "C");
		
		double width = m_pDocument->m_docPageSize.Width(DIM_IN);
		double height = m_pDocument->m_docPageSize.Height(DIM_IN);
			
		UT_String_sprintf(szPaperWidth, "%fin", width);
		UT_String_sprintf(szPaperHeight, "%fin", height);		
	}

	m_pie->_rtf_keyword_ifnotdefault_twips("paperw",szPaperWidth.c_str(),0);
	m_pie->_rtf_keyword_ifnotdefault_twips("paperh",szPaperHeight.c_str(),0);

	const XML_Char * szLeftMargin = PP_evalProperty("page-margin-left",
													 pSpanAP,pBlockAP,pSectionAP,
													 m_pDocument,true);
	m_pie->_rtf_keyword_ifnotdefault_twips("margl",static_cast<const char*>(szLeftMargin),1800);
	const XML_Char * szRightMargin = PP_evalProperty("page-margin-right",
													 pSpanAP,pBlockAP,pSectionAP,
													 m_pDocument,true);
	m_pie->_rtf_keyword_ifnotdefault_twips("margr",static_cast<const char*>(szRightMargin),1800);
	const XML_Char * szTopMargin = PP_evalProperty("page-margin-top",
													 pSpanAP,pBlockAP,pSectionAP,
													 m_pDocument,true);
	m_pie->_rtf_keyword_ifnotdefault_twips("margt",static_cast<const char*>(szTopMargin),1440);
	const XML_Char * szBottomMargin = PP_evalProperty("page-margin-bottom",
													 pSpanAP,pBlockAP,pSectionAP,
													 m_pDocument,true);
	m_pie->_rtf_keyword_ifnotdefault_twips("margb",static_cast<const char*>(szBottomMargin),1440);

	if (landscape)
		m_pie->_rtf_keyword("landscape");
	m_pie->_rtf_keyword("widowctl");	// enable widow and orphan control

	// TODO <docfmt> -- linked styles
	// TODO <docfmt> -- compatibility options
	// TODO <docfmt> -- forms
	// TODO <docfmt> -- revision marks
	// TODO <docfmt> -- comments (annotations)
	// TODO <docfmt> -- bidirectional controls
	// TODO <docfmt> -- page borders
}

//////////////////////////////////////////////////////////////////

void s_RTF_ListenerWriteDoc::_rtf_open_section(PT_AttrPropIndex api)
{
	m_apiThisSection = api;

	const PP_AttrProp * pSpanAP = NULL;
	const PP_AttrProp * pBlockAP = NULL;
	const PP_AttrProp * pSectionAP = NULL;

	m_pDocument->getAttrProp(m_apiThisSection,&pSectionAP);

	const XML_Char * szColumns = PP_evalProperty("columns",
												 pSpanAP,pBlockAP,pSectionAP,
												 m_pDocument,true);
	const XML_Char * szColumnGap = PP_evalProperty("column-gap",
												   pSpanAP,pBlockAP,pSectionAP,
												   m_pDocument,true);

	const XML_Char * szColumnLine = PP_evalProperty("column-line",
													pSpanAP,pBlockAP,pSectionAP,
													m_pDocument,true);


	const XML_Char * szMarginLeft = PP_evalProperty("page-margin-left",
												 pSpanAP,pBlockAP,pSectionAP,
												 m_pDocument,true);

	const XML_Char * szMarginTop = PP_evalProperty("page-margin-top",
												 pSpanAP,pBlockAP,pSectionAP,
												 m_pDocument,true);

	const XML_Char * szMarginRight = PP_evalProperty("page-margin-right",
												 pSpanAP,pBlockAP,pSectionAP,
												 m_pDocument,true);

	const XML_Char * szMarginBottom = PP_evalProperty("page-margin-bottom",
												 pSpanAP,pBlockAP,pSectionAP,
												 m_pDocument,true);

	const XML_Char * szHeaderY = PP_evalProperty("page-margin-header",
												 pSpanAP,pBlockAP,pSectionAP,
												 m_pDocument,true);

	const XML_Char * szFooterY = PP_evalProperty("page-margin-footer",
												 pSpanAP,pBlockAP,pSectionAP,
												 m_pDocument,true);

// 	const XML_Char * szSpaceAfter = PP_evalProperty("section-space-after",
// 												 pSpanAP,pBlockAP,pSectionAP,
// 												 m_pDocument,true);

	const XML_Char * szRestartNumbering = PP_evalProperty("section-restart",
												 pSpanAP,pBlockAP,pSectionAP,
												 m_pDocument,true);

	const XML_Char * szRestartAt = PP_evalProperty("section-restart-value",
												 pSpanAP,pBlockAP,pSectionAP,
												 m_pDocument,true);
	const XML_Char * szHeaderExists = NULL;
	pSectionAP->getAttribute("header", szHeaderExists);
	const XML_Char * szFooterExists = NULL;
	pSectionAP->getAttribute("footer", szFooterExists);
	const XML_Char * szDomDir = PP_evalProperty("dom-dir",
												 pSpanAP,pBlockAP,pSectionAP,
												 m_pDocument,true);

	bool bSectRTL = UT_strcmp (szDomDir,"rtl") == 0;

	bool bColLine = false;
	if (szColumnLine && !UT_strcmp (szColumnLine, "on"))
		bColLine = true;

	// TODO add other properties here

	m_pie->_rtf_nl();
	_closeSpan();                   // In case it's open.
	if(m_bStartedList)
	{
		m_pie->_rtf_close_brace();
		m_bStartedList = false;
	}
	if (m_bJustStartingDoc)			// 'sect' is a delimiter, rather than a plain start
	{

		m_bJustStartingDoc = false;
	}
	else
		m_pie->_rtf_keyword("sect");							// begin a new section
	m_bJustStartingSection = true;

	m_pie->_rtf_keyword("sectd");								// restore all defaults for this section
	m_pie->_rtf_keyword("sbknone");								// no page break implied
	m_pie->_rtf_keyword_ifnotdefault("cols",static_cast<const char*>(szColumns),1);
	m_pie->_rtf_keyword_ifnotdefault_twips("colsx",static_cast<const char*>(szColumnGap),720);

	{
		UT_LocaleTransactor(LC_NUMERIC, "C");
		
		if (bColLine)
		{
			m_pie->_rtf_keyword ("linebetcol");
		}
		if(szHeaderY)
		{
			double hMarg = UT_convertToInches(szHeaderY);
			UT_String sHeaderY;
			
			UT_String_sprintf(sHeaderY,"%fin",hMarg);
			m_pie->_rtf_keyword_ifnotdefault_twips("headery", static_cast<const char*>(sHeaderY.c_str()), 720);			
		}
		if(szFooterY)
		{
			double hMarg = UT_convertToInches(szFooterY);
			UT_String sFooterY;
			
			UT_String_sprintf(sFooterY,"%fin",hMarg);
			m_pie->_rtf_keyword_ifnotdefault_twips("footery", static_cast<const char*>(sFooterY.c_str()), 720);			
		}
		if(szMarginTop)
		{
			double tMarg = UT_convertToInches(szMarginTop);
			UT_String sRtfTop;
			
			
			UT_String_sprintf(sRtfTop,"%fin",tMarg);
			m_pie->_rtf_keyword_ifnotdefault_twips("margtsxn", static_cast<const char*>(sRtfTop.c_str()), 1440);
		}

		if(szMarginBottom)
		{
			double bMarg = UT_convertToInches(szMarginBottom);
			UT_String sRtfBot;
			UT_String_sprintf(sRtfBot,"%fin",bMarg);
			m_pie->_rtf_keyword_ifnotdefault_twips("margbsxn", static_cast<const char*>(sRtfBot.c_str()), 1440);
		}
	}

	if(szMarginLeft)
	{
		m_pie->_rtf_keyword_ifnotdefault_twips("marglsxn", static_cast<const char*>(szMarginLeft), 1440);
	}
	if(szMarginRight)
	{
		m_pie->_rtf_keyword_ifnotdefault_twips("margrsxn", static_cast<const char*>(szMarginRight), 1440);
	}

	if(szRestartNumbering && UT_strcmp(szRestartNumbering,"1") == 0)
	{
		m_pie->_rtf_keyword("pgnrestart");
		if(szRestartAt)
		{
			UT_sint32 num = atoi(szRestartAt);
			m_pie->_rtf_keyword("pgnx",num);
		}
	}
	else
	{
		m_pie->_rtf_keyword("pgncont");
	}

	if (bSectRTL)
		m_pie->_rtf_keyword("rtlsect");
	else
		m_pie->_rtf_keyword("ltrsect");
}

//////////////////////////////////////////////////////////////////

void s_RTF_ListenerWriteDoc::_rtf_open_block(PT_AttrPropIndex api)
{
	m_apiThisBlock = api;

	const PP_AttrProp * pSpanAP = NULL;
	const PP_AttrProp * pBlockAP = NULL;
	const PP_AttrProp * pSectionAP = NULL;

	m_pDocument->getAttrProp(m_apiThisSection,&pSectionAP);
	m_pDocument->getAttrProp(m_apiThisBlock,&pBlockAP);

	const XML_Char * szTextAlign = PP_evalProperty("text-align",pSpanAP,pBlockAP,pSectionAP,m_pDocument,true);
	const XML_Char * szFirstLineIndent = PP_evalProperty("text-indent",pSpanAP,pBlockAP,pSectionAP,m_pDocument,true);
	const XML_Char * szLeftIndent = PP_evalProperty("margin-left",pSpanAP,pBlockAP,pSectionAP,m_pDocument,true);
	const XML_Char * szRightIndent = PP_evalProperty("margin-right",pSpanAP,pBlockAP,pSectionAP,m_pDocument,true);
	const XML_Char * szTopMargin = PP_evalProperty("margin-top",pSpanAP,pBlockAP,pSectionAP,m_pDocument,true);
	const XML_Char * szBottomMargin = PP_evalProperty("margin-bottom",pSpanAP,pBlockAP,pSectionAP,m_pDocument,true);
	const XML_Char * szLineHeight = PP_evalProperty("line-height",pSpanAP,pBlockAP,pSectionAP,m_pDocument,true);
	const XML_Char * szKeepTogether = PP_evalProperty("keep-together",pSpanAP,pBlockAP,pSectionAP,m_pDocument,true);
	const XML_Char * szKeepWithNext = PP_evalProperty("keep-with-next",pSpanAP,pBlockAP,pSectionAP,m_pDocument,true);
	const XML_Char * szTabStops = PP_evalProperty("tabstops",pSpanAP,pBlockAP,pSectionAP,m_pDocument,true);

	// TODO add other properties here

	// Do abi specific list information.

	const XML_Char * szListid=NULL;
	const XML_Char * szParentid=NULL;
	const XML_Char * szListStyle=NULL;

	if (!pBlockAP || !pBlockAP->getAttribute(static_cast<const XML_Char*>("listid"), szListid))		szListid = NULL;
	if (!pBlockAP || !pBlockAP->getAttribute(static_cast<const XML_Char*>("parentid"), szParentid))
		szParentid = NULL;
	UT_uint32 listid = 0;
	const XML_Char * szAbiListDelim = NULL;
	const XML_Char * szAbiListDecimal = NULL;
	static UT_String szAbiStartValue;
	static UT_String szLevel;
	if(szListid!=NULL)
	{
		listid = atoi(szListid);
		if(listid != 0)
		{
			fl_AutoNum * pAuto = m_pDocument->getListByID(listid);
			if(pAuto)
			{
				szAbiListDelim = pAuto->getDelim();
				szAbiListDecimal = pAuto->getDecimal();
				UT_String_sprintf(szAbiStartValue,"%i",pAuto->getStartValue32());
				UT_String_sprintf(szLevel,"%i",pAuto->getLevel());
			}
		}
	}
	szListStyle = PP_evalProperty("list-style",pSpanAP,pBlockAP,pSectionAP,m_pDocument,true);
	const XML_Char * szAbiFieldFont = PP_evalProperty("field-font",pSpanAP,pBlockAP,pSectionAP,m_pDocument,true);

	m_pie->_rtf_nl();

	// 'par' is a delimiter, rather than a plain start.
	// NO! par is \r and closes a paragraph, not opens it
	// also, block-level character properties are applied to \par so it really matters
	// that gets associated with the correct paragraph
	 
	m_bJustStartingSection = false;
	m_bOpennedFootnote = false;
	
	if(m_Table.getNestDepth() > 0 && m_Table.isCellJustOpenned())
	{
		m_Table.setCellJustOpenned(false);
	}
	
	if(m_bStartedList && !m_bInFrame)
	{
		m_pie->_rtf_close_brace();
	}
	m_bStartedList = false;
		
	m_bJustOpennedFrame = false;
	UT_uint32 id = 0;
	if(szListid != NULL)
		id = atoi(szListid);
	if(id == 0)
	{
//
// If span was openned in a previous closeBlock because of a blank line
// close it now.
//
		_closeSpan();
		m_pie->_rtf_keyword("pard");		// begin a new paragraph
		m_pie->_rtf_keyword("plain");		// begin a new paragraph
	}
	else
	{
		m_bStartedList = true;
	}
	///
	/// Output fallback numbered/bulleted label for rtf readers that don't
	/// know /*/pn
	///
	if(id != 0 )
	{
		m_pie->_rtf_open_brace();
		m_pie->_rtf_keyword("listtext");
		// if string is "left" use "ql", but that is the default, so we don't need to write it out.
		m_pie->_rtf_keyword("pard");		// restore all defaults for this paragraph
		if (UT_strcmp(szTextAlign,"right")==0)		// output one of q{lrcj} depending upon paragraph alignment
			m_pie->_rtf_keyword("qr");
		else if (UT_strcmp(szTextAlign,"center")==0)
			m_pie->_rtf_keyword("qc");
		else if (UT_strcmp(szTextAlign,"justify")==0)
			m_pie->_rtf_keyword("qj");
		m_pie->_rtf_keyword_ifnotdefault_twips("fi",static_cast<const char*>(szFirstLineIndent),0);
		m_pie->_rtf_keyword_ifnotdefault_twips("li",static_cast<const char*>(szLeftIndent),0);
		m_pie->_rtf_keyword_ifnotdefault_twips("ri",static_cast<const char*>(szRightIndent),0);
		m_pie->_rtf_keyword_ifnotdefault_twips("sb",static_cast<const char*>(szTopMargin),0);
		m_pie->_rtf_keyword_ifnotdefault_twips("sa",static_cast<const char*>(szBottomMargin),0);

		fl_AutoNum * pAuto = m_pDocument->getListByID(id);
		UT_return_if_fail(pAuto);
		if(pAuto->getType()==BULLETED_LIST)
		{
			m_pie->_rtf_keyword("bullet");
		}
		else
		{
			const UT_UCSChar * lab = pAuto->getLabel(m_sdh);
            //
			// TODO Handle all our interesting symbols properly
            // In the meantime convert to char *
			static char tmp[100];
			if(lab != NULL)
			{
				UT_uint32 len = UT_MIN(UT_UCS4_strlen(lab),100);
				UT_uint32 i;
				for(i=0; i<=len; i++)
					tmp[i] = (char ) (unsigned char)  *lab++;
				m_pie->_rtf_chardata(tmp,len);
			}
			else
			{
			    UT_DEBUGMSG(("SEVIOR: We should not be here! id = %d \n",id));
				m_pie->_rtf_chardata(" ",1);
			}
		}
		//
        // Put in Tab for braindead RTF importers (like Ted) that can't
        // do numbering.
		//
		char tab = static_cast<char>(9);
		m_pie->_rtf_chardata(&tab,1);
		m_pie->_rtf_close_brace();
//
// Span was openned in a previous closeBlock because of a List item
// close it now.
//
		_closeSpan();
		m_pie->_rtf_keyword("pard");		// restore all defaults for this paragraph
		m_pie->_rtf_keyword("plain");		// restore all defaults for this paragraph
	}
	if(m_bStartedList)
	{
		m_pie->_rtf_open_brace();
	}

	// it is essential that the \rtlpar and \ltrpar tokens are issued
	// before any formatting, otherwise it can cause difficulties
	const XML_Char * szBidiDir = PP_evalProperty("dom-dir",
												 pSpanAP,
												 pBlockAP,
												 pSectionAP,
												 m_pDocument,
												 true);

	xxx_UT_DEBUGMSG(("bidi paragraph: pSectionAp 0x%x, pBlockAP 0x%x, dom-dir\"%s\"\n",pSectionAP,pBlockAP,szBidiDir));
	if (szBidiDir)
	{
		if (!UT_strcmp (szBidiDir, "ltr"))
			m_pie->_rtf_keyword ("ltrpar");
		else
			m_pie->_rtf_keyword ("rtlpar");
	}

	// if string is "left" use "ql", but that is the default, so we
	// don't need to write it out.
	// Except that if the alignment overrides one prescribed by a
	// style, we probably need to issue this (Tomas, Apr 12, 2003)

	// output q{lrcj depending upon paragraph alignment
	if (UT_strcmp(szTextAlign,"left")==0)	
		m_pie->_rtf_keyword("ql");
	else if (UT_strcmp(szTextAlign,"justify")==0)
		m_pie->_rtf_keyword("qj");
	else if (UT_strcmp(szTextAlign,"right")==0)	
		m_pie->_rtf_keyword("qr");
	else if (UT_strcmp(szTextAlign,"center")==0)
		m_pie->_rtf_keyword("qc");



	m_pie->_rtf_keyword_ifnotdefault_twips("fi",static_cast<const char*>(szFirstLineIndent),0);
	m_pie->_rtf_keyword_ifnotdefault_twips("li",static_cast<const char*>(szLeftIndent),0);
	m_pie->_rtf_keyword_ifnotdefault_twips("ri",static_cast<const char*>(szRightIndent),0);
	m_pie->_rtf_keyword_ifnotdefault_twips("sb",static_cast<const char*>(szTopMargin),0);
	m_pie->_rtf_keyword_ifnotdefault_twips("sa",static_cast<const char*>(szBottomMargin),0);

	const XML_Char * szStyle = NULL;
	if (pBlockAP->getAttribute("style", szStyle))
	{
	    m_pie->_rtf_keyword("s", m_pie->_getStyleNumber(szStyle));
	}
///
/// OK we need to output the char props if there is a list here
	// no, we must not output character properties here, because the properties that are
	// output here will apply to everyting that will follow in this paragraph: see bug 5693
	// -- I am really not sure what the rationale for the char props output here was, so
	// if commenting this out creates some other problem, please let me know. Tomas, Sep
	// 2, 2004
#if 0 //#TF
	if(id != 0)
	{
		const PP_AttrProp * pSpanAP = NULL;
		const PP_AttrProp * pBlockAP = NULL;
		const PP_AttrProp * pSectionAP = NULL;

		m_pDocument->getAttrProp(m_apiThisSection,&pSectionAP);
		m_pDocument->getAttrProp(m_apiThisBlock,&pBlockAP);
		m_pie->_write_charfmt(s_RTF_AttrPropAdapter_AP(pSpanAP, pBlockAP, pSectionAP, m_pDocument));
	}
#endif
	///
	/// OK if there is list info in this paragraph we encase it inside
	/// the {\*\abilist..} extension
	///
	if(id != 0 )
	{
		_setListBlock( true);

		m_pie->_rtf_open_brace();
		m_pie->_rtf_keyword("*");
		m_pie->_rtf_keyword("abilist");
		m_pie->_rtf_keyword_ifnotdefault("abilistid",static_cast<const char *>(szListid),-1);
		m_pie->_rtf_keyword_ifnotdefault("abilistparentid",static_cast<const char *>(szParentid),-1);
		m_pie->_rtf_keyword_ifnotdefault("abilistlevel",szLevel.c_str(),-1);
		m_pie->_rtf_keyword_ifnotdefault("abistartat",szAbiStartValue.c_str(),-1);
		/// field font

		m_pie->_rtf_open_brace();
		m_pie->_rtf_keyword("abifieldfont");
		m_pie->_rtf_chardata( static_cast<const char *>(szAbiFieldFont) ,strlen(szAbiFieldFont));
		m_pie->_rtf_close_brace();

		/// list decimal

		m_pie->_rtf_open_brace();
		m_pie->_rtf_keyword("abilistdecimal");
		m_pie->_rtf_chardata(static_cast<const char *>(szAbiListDecimal) ,strlen(szAbiListDecimal));
		m_pie->_rtf_close_brace();

		/// list delim

		m_pie->_rtf_open_brace();
		m_pie->_rtf_keyword("abilistdelim");
		m_pie->_rtf_chardata(static_cast<const char *>(szAbiListDelim) ,strlen( szAbiListDelim));
		m_pie->_rtf_close_brace();

		/// list style

		m_pie->_rtf_open_brace();
		m_pie->_rtf_keyword("abiliststyle");
		m_pie->_rtf_chardata(static_cast<const char *>(szListStyle) ,strlen( szListStyle));
		m_pie->_rtf_close_brace();

		/// Finished!

		m_pie->_rtf_close_brace();

	}

	///
	/// OK Now output word-95 style lists
	///

	if(id != 0 )
	{
		m_pie->_rtf_open_brace();
		m_pie->_rtf_keyword("*");
		m_pie->_rtf_keyword("pn");
		fl_AutoNum * pAuto = m_pDocument->getListByID(id);
		UT_return_if_fail(pAuto);
		m_pie->_rtf_keyword("pnql");
		m_pie->_rtf_keyword("pnstart",pAuto->getStartValue32());
		FL_ListType lType = pAuto->getType();

		///
		/// extract text before and after numbering symbol
		///
		static XML_Char p[80],leftDelim[80],rightDelim[80];
		sprintf(p, "%s",pAuto->getDelim());
		UT_uint32 rTmp;

		UT_uint32 i = 0;

		while (p[i] && p[i] != '%' && p[i+1] != 'L')
		{
			leftDelim[i] = p[i];
			i++;
		}
		leftDelim[i] = '\0';
		i += 2;
		rTmp = i;
		while (p[i] || p[i] != '\0')
		{
			rightDelim[i - rTmp] = p[i];
			i++;
		}
		rightDelim[i - rTmp] = '\0';

		fl_AutoNum * pParent = pAuto->getParent();
		if(pParent == NULL && (lType < BULLETED_LIST))
		{
			m_pie->_rtf_keyword("pnlvlbody");
		}
		else if(lType >= BULLETED_LIST && (lType != NOT_A_LIST))
		{
			m_pie->_rtf_keyword("pnlvlblt");
		}
		else
		{
			m_pie->_rtf_keyword("pnprev");
			m_pie->_rtf_keyword("pnlvl",9);
		}
		if(lType == NUMBERED_LIST)
		{
			m_pie->_rtf_keyword("pndec");
		}
		else if(lType == LOWERCASE_LIST)
		{
			m_pie->_rtf_keyword("pnlcltr");
		}
		else if(lType == UPPERCASE_LIST)
		{
			m_pie->_rtf_keyword("pnucltr");
		}
		else if(lType == LOWERROMAN_LIST)
		{
			m_pie->_rtf_keyword("pnlcrm");
		}
		else if(lType == UPPERROMAN_LIST)
		{
			m_pie->_rtf_keyword("pnucrm");
		}
		else if(lType == NOT_A_LIST)
		{
			m_pie->_rtf_keyword("pnucrm");
		}
		if(lType < BULLETED_LIST)
		{
			m_pie->_rtf_open_brace();
			m_pie->_rtf_keyword("pntxtb");
			m_pie->_rtf_chardata(static_cast<const char *>(leftDelim),strlen(static_cast<const char *>(leftDelim)));
			m_pie->_rtf_close_brace();
			m_pie->_rtf_open_brace();
			m_pie->_rtf_keyword("pntxta");
			m_pie->_rtf_chardata(static_cast<const char *>(rightDelim),strlen(static_cast<const char *>(rightDelim)));
			m_pie->_rtf_close_brace();
		}
		else if(lType == BULLETED_LIST)
		{
			m_pie->_rtf_open_brace();
			m_pie->_rtf_keyword("pntxtb");
			m_pie->_rtf_keyword("bullet");
			m_pie->_rtf_close_brace();
		}
		else if(lType > BULLETED_LIST)
		{
			m_pie->_rtf_open_brace();
			m_pie->_rtf_keyword("pntxtb");
			const UT_UCSChar * tmp = pAuto->getLabel(m_sdh);
			UT_ASSERT_HARMLESS(tmp);

			if(!tmp) // Should not happen, if it does attempt to recover
			{
				m_pie->_rtf_chardata(" ",1);
			}
			else
			{
				UT_uint32 i,len;
				len = UT_UCS4_strlen(tmp);
				for(i=0;i<=len;i++)
					p[i] = (char) (unsigned char) *tmp++;
				m_pie->_rtf_chardata(p,len);
			}
			m_pie->_rtf_close_brace();
		}
		m_pie->_rtf_close_brace();
	}



	///
	/// OK Now output word-97 style lists. First detect if we've moved to
    /// a new list list structure. We need m_currID to track the previous list
    /// we were in.
	///
	xxx_UT_DEBUGMSG(("SEVIOR: Doing output of list structure id = %d\n",id));
	if(id != 0 )
	{
		UT_uint32 iOver = m_pie->getMatchingOverideNum(id);
		UT_uint32 iLevel = 0;
		fl_AutoNum * pAuto = m_pDocument->getListByID(id);

//		if(id != m_currID)
		{
			UT_return_if_fail(iOver);
			fl_AutoNum * pAuto = m_pDocument->getListByID(id);
			UT_return_if_fail(pAuto);
			while(pAuto->getParent() != NULL)
			{
				pAuto = pAuto->getParent();
				iLevel++;
			}
			if(iLevel > 8)
			{
				UT_ASSERT_HARMLESS(0);
				iLevel = 8;
			}
			m_currID = id;
		}
		/* This is changed so that Word97 can see the numbers in
		   numbered lists */
		if(pAuto->getType() < BULLETED_LIST)
		{
	        	m_pie->_rtf_keyword_ifnotdefault_twips("fn",static_cast<const char*>(szFirstLineIndent),0);
	        	m_pie->_rtf_keyword_ifnotdefault_twips("li",static_cast<const char*>(szLeftIndent),0);
		}
		m_pie->_rtf_keyword("ls",iOver);
		m_pie->_rtf_keyword("ilvl",iLevel);
	}

	if (strcmp(szLineHeight,"1.0") != 0)
	{
		double f = UT_convertDimensionless(szLineHeight);


		if (f > 0.000001) 
		{                                   // we get zero on bogus strings....
		        const char * pPlusFound = strrchr(szLineHeight, '+');
		        if (pPlusFound && *(pPlusFound + 1) == 0)             //  "+" means "at least" line spacing
			{
				UT_sint32 dSpacing = (UT_sint32)(f * 20.0);
				m_pie->_rtf_keyword("sl",dSpacing);
				m_pie->_rtf_keyword("slmult",0);
			}
			else if (UT_hasDimensionComponent(szLineHeight)) //  use exact line spacing
			{
			        UT_sint32 dSpacing = (UT_sint32)(f * 20.0);
			        m_pie->_rtf_keyword("sl",-dSpacing);
			        m_pie->_rtf_keyword("slmult",0);
		        }
			else // multiple line spacing
			{
			        UT_sint32 dSpacing = (UT_sint32)(f * 240.0);
			        m_pie->_rtf_keyword("sl",dSpacing);
			        m_pie->_rtf_keyword("slmult",1);
		        }
		}
	}
//
// Output Paragraph Cell nesting level.
//
	if(m_Table.getNestDepth() > 0)
	{
		m_pie->_rtf_keyword("intbl");
	}
	m_pie->_rtf_keyword("itap",m_Table.getNestDepth());

	if (UT_strcmp(szKeepTogether,"yes")==0)
		m_pie->_rtf_keyword("keep");
	if (UT_strcmp(szKeepWithNext,"yes")==0)
		m_pie->_rtf_keyword("keepn");

	m_pie->_write_tabdef(szTabStops);

	m_pie->_output_revision(s_RTF_AttrPropAdapter_AP(pSpanAP, pBlockAP, pSectionAP, m_pDocument),true);
}

//////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////
void s_RTF_ListenerWriteDoc::_writeBookmark(const PX_ChangeRecord_Object * pcro)
{
	PT_AttrPropIndex api = pcro->getIndexAP();
	const PP_AttrProp * pBookmarkAP = NULL;
	m_pDocument->getAttrProp(api,&pBookmarkAP);

	const XML_Char * szType = NULL;
	bool bFound = pBookmarkAP->getAttribute("type", szType);
	if (!bFound) {
		UT_DEBUGMSG (("RTF_Export: cannot get type for bookmark\n"));
		return;
	}
	const XML_Char * szName = NULL;
	bFound = pBookmarkAP->getAttribute("name", szName);
	if (!bFound) {
		UT_DEBUGMSG (("RTF_Export: cannot get name for bookmark\n"));
		return;
	}
	m_pie->_rtf_open_brace();
	{
		m_pie->_rtf_keyword("*");
		if (UT_strcmp (szType, "start") == 0) {
			m_pie->_rtf_keyword("bkmkstart");
		}
		else if (UT_strcmp (szType, "end") == 0) {
			m_pie->_rtf_keyword("bkmkend");
		}
		m_pie->_rtf_chardata(szName, strlen(szName));
		m_pie->_rtf_close_brace();
	}
}


//////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////
void s_RTF_ListenerWriteDoc::_writeHyperlink(const PX_ChangeRecord_Object * pcro)
{
	PT_AttrPropIndex api = pcro->getIndexAP();
	const PP_AttrProp * pHyperlinkAP = NULL;
	m_pDocument->getAttrProp(api,&pHyperlinkAP);

	const XML_Char * szHyper = NULL;
	bool bFound = pHyperlinkAP->getAttribute("xlink:href", szHyper);
	if (!bFound)
	{
		UT_DEBUGMSG (("RTF_Export: cannot get address for hyperlink\n"));
		return;
	}
	_writeFieldPreamble(pHyperlinkAP);
	m_pie->write("HYPERLINK ");
	m_pie->write("\"");
	m_pie->write(szHyper);
	m_pie->write("\"");
	m_bHyperLinkOpen = true;
	m_pie->_rtf_close_brace();
	m_pie->_rtf_close_brace();
	m_pie->_rtf_open_brace();
	m_pie->_rtf_keyword("*");
	m_pie->_rtf_keyword("fldrslt");
}


void s_RTF_ListenerWriteDoc::_writeImageInRTF(const PX_ChangeRecord_Object * pcro)
{
	PT_AttrPropIndex api = pcro->getIndexAP();
	const PP_AttrProp * pImageAP = NULL;
	m_pDocument->getAttrProp(api,&pImageAP);

	// fetch the "name" of the image and use it to fetch the actual image data.

	const XML_Char * szDataID = NULL;
	bool bFoundDataID = pImageAP->getAttribute("dataid",szDataID);
	if (!bFoundDataID)
	{
		UT_DEBUGMSG(("RTF_Export: cannot get dataid for image\n"));
		return;
	}
	const UT_ByteBuf * pbb = NULL;
	const void * pToken = NULL;
	void * pHandle = NULL;
	bool bFoundDataItem = m_pDocument->getDataItemDataByName(static_cast<const char*>(szDataID),&pbb,&pToken,&pHandle);
	if (!bFoundDataItem)
	{
		UT_DEBUGMSG(("RTF_Export: cannot get dataitem for image\n"));
		return;
	}

	// see if the image has a width/height attribute that should
	// override the actual pixel size of the image.

	const XML_Char * szWidthProp = NULL;
	const XML_Char * szHeightProp = NULL;
	const XML_Char * szCroplProp = NULL;
	const XML_Char * szCroprProp = NULL;
	const XML_Char * szCroptProp = NULL;
	const XML_Char * szCropbProp = NULL;
	bool bFoundWidthProperty = pImageAP->getProperty("width",szWidthProp);
	bool bFoundHeightProperty = pImageAP->getProperty("height",szHeightProp);
	bool bFoundCropl = pImageAP->getProperty ("cropl",szCroplProp);
	bool bFoundCropr = pImageAP->getProperty ("cropr",szCroprProp);
	bool bFoundCropt = pImageAP->getProperty ("cropt",szCroptProp);
	bool bFoundCropb = pImageAP->getProperty ("cropb",szCropbProp);

	// get the width/height of the image from the image itself.

	UT_sint32 iImageWidth, iImageHeight;
	UT_PNG_getDimensions(pbb,iImageWidth,iImageHeight);

	// compute scale factors...

	double dImageWidth = static_cast<double>(iImageWidth);
	double dImageHeight = static_cast<double>(iImageHeight);
	dImageWidth = UT_convertDimToInches(dImageWidth,DIM_PT);
	dImageHeight = UT_convertDimToInches(dImageHeight,DIM_PT);

	// if everything is ok, we need to dump the image data (in hex)
	// to the RTF stream with some screwy keywords...
	//
	// we need to emit:     {\*\shppict{\pict <stuff>}}
	// we do not deal with: {\*\nonshppict...}
	//
	// <stuff> ::= <brdr>? <shading>? <pictype> <pictsize> <metafileinfo>? <data>

	m_pie->_rtf_open_brace();
	{
		m_pie->_rtf_keyword("*");
		m_pie->_rtf_keyword("shppict");
		m_pie->_rtf_open_brace();
		{
			m_pie->_rtf_keyword("pict");
			// TODO deal with <brdr>
			// TODO deal with <shading>

			// <pictype> -- we store everything internall as PNG, so that's all
			//              we output here.  TODO consider listing multiple formats
			//              here -- word97 seems to, but this really bloats the file.

			m_pie->_rtf_keyword("pngblip");

			// <pictsize>

			m_pie->_rtf_keyword("picw",iImageWidth);
			m_pie->_rtf_keyword("pich",iImageHeight);
			if (bFoundWidthProperty)
			{
				m_pie->_rtf_keyword_ifnotdefault_twips("picwgoal",static_cast<const char*>(szWidthProp),0);
				double dWidth = UT_convertToInches(szWidthProp);
				double scalex = 100.0*dWidth/dImageWidth;
				UT_uint32 iscalex = static_cast<UT_uint32>(scalex);
				m_pie->_rtf_keyword("picscalex",iscalex);

			}
			if (bFoundHeightProperty)
			{
				m_pie->_rtf_keyword_ifnotdefault_twips("pichgoal",static_cast<const char*>(szHeightProp),0);
				double dHeight = UT_convertToInches(szHeightProp);
				double scaley = 100.0*dHeight/dImageHeight;
				UT_uint32 iscaley = static_cast<UT_uint32>(scaley);
				m_pie->_rtf_keyword("picscaley",iscaley);
			}
			if (bFoundCropl)
			{
				m_pie->_rtf_keyword_ifnotdefault_twips("piccropl",static_cast<const char*>(szCroplProp),0);
			}
			if (bFoundCropr)
			{
				m_pie->_rtf_keyword_ifnotdefault_twips("piccropr",static_cast<const char*>(szCroprProp),0);
			}
			if (bFoundCropt)
			{
				m_pie->_rtf_keyword_ifnotdefault_twips("piccropt",static_cast<const char*>(szCroptProp),0);
			}
			if (bFoundCropb)
			{
				m_pie->_rtf_keyword_ifnotdefault_twips("piccropb",static_cast<const char*>(szCropbProp),0);
			}


			// TODO deal with <metafileinfo>

			// <data>

			// TODO create meaningful values for bliptag and bliduid...
			// we emit "\bliptag<N>{\*\blipuid <N16>}"
			// where <N> is an integer.
			// where <N16> is a 16-byte integer in hex.

			m_pie->_rtf_nl();
			UT_uint32 tag = UT_newNumber ();
			m_pie->_rtf_keyword("bliptag",tag);
			m_pie->_rtf_open_brace();
			{
				m_pie->_rtf_keyword("*");
				m_pie->_rtf_keyword("blipuid");
				UT_String buf;
				UT_String_sprintf(buf,"%032x",tag);
				m_pie->_rtf_chardata(buf.c_str(),buf.size());
			}
			m_pie->_rtf_close_brace();

			UT_uint32 lenData = pbb->getLength();
			const UT_Byte * pData = pbb->getPointer(0);
			UT_uint32 k;

			for (k=0; k<lenData; k++)
			{
				if (k%32==0)
					m_pie->_rtf_nl();
				UT_String buf;
				UT_String_sprintf(buf,"%02x",pData[k]);
				m_pie->_rtf_chardata(buf.c_str(),2);
			}
		}
		m_pie->_rtf_close_brace();
	}
	m_pie->_rtf_close_brace();
}
