Subject: PATCH: RTF style export
From: David Mandelin (mandelin@cs.wisc.edu)
Date: Wed Sep 12 2001 - 17:06:43 CDT
A diff for abi/src/wp and 2 new files. It's sort of rough right now, but
it works for me. Still left:
1. handle list styles
2. merge more style/content code.
Regarding #2, I based a lot of the style sheet code on the document
content code. For example, I reused the part that generates character
formatting information. In some cases, I modified the code to make it
reusable and then reused it. In others, I copied code. I want to fix
that.
The 2 new files are for an adapter class that I use so that the RTF
export code doesn't have to worry about the 2 different methods of
getting properties.
Index: impexp/xp/Makefile
===================================================================
RCS file: /cvsroot/abi/src/wp/impexp/xp/Makefile,v
retrieving revision 1.62
diff -u -r1.62 Makefile
--- impexp/xp/Makefile	2001/08/29 10:02:38	1.62
+++ impexp/xp/Makefile	2001/09/12 22:00:40
@@ -33,6 +33,7 @@
                         ie_exp_Text.cpp				\
                         ie_exp_HRText.cpp			\
                         ie_exp_RTF.cpp				\
+			ie_exp_RTF_AttrProp.cpp			\
                         ie_exp_RTF_listenerGetProps.cpp		\
                         ie_exp_RTF_listenerWriteDoc.cpp		\
                         ie_exp_LaTeX.cpp			\
Index: impexp/xp/ie_exp_RTF.cpp
===================================================================
RCS file: /cvsroot/abi/src/wp/impexp/xp/ie_exp_RTF.cpp,v
retrieving revision 1.24
diff -u -r1.24 ie_exp_RTF.cpp
--- impexp/xp/ie_exp_RTF.cpp	2001/07/27 20:49:47	1.24
+++ impexp/xp/ie_exp_RTF.cpp	2001/09/12 22:00:40
@@ -23,6 +23,7 @@
 #include "ut_base64.h"
 #include "ut_misc.h"
 #include "ut_units.h"
+#include "ut_vector.h"
 #include "pt_Types.h"
 #include "ie_exp_RTF.h"
 #include "pd_Document.h"
@@ -43,6 +44,7 @@
 /*****************************************************************/
 /*****************************************************************/
 
+#include "ie_exp_RTF_AttrProp.h"
 #include "ie_exp_RTF_listenerWriteDoc.h"
 #include "ie_exp_RTF_listenerGetProps.h"
 
@@ -77,6 +79,7 @@
 {
         UT_VECTOR_FREEALL(char *,m_vecColors);
         UT_VECTOR_PURGEALL(_rtf_font_info *,m_vecFonts);
+	_clearStyles();
 }
 
 /*****************************************************************/
@@ -159,6 +162,10 @@
                 getDoc()->tellListener(static_cast<PL_Listener *>(m_pListenerGetProps));
         DELETEP(m_pListenerGetProps);
 
+	// Important: This must come before the header is written so
+        // every font used in a style is properly entered in the font table.
+	_selectStyles();
+
         // write rtf header
 
         if (!_write_rtf_header())
@@ -187,33 +194,6 @@
 /*****************************************************************/
 /*****************************************************************/
 #if 0
-void s_RTF_Listener::_handleStyles(void)
-{
-	bool bWroteOpenStyleSection = false;
-
-	const char * szName;
-	const PD_Style * pStyle;
-
-	for (UT_uint32 k=0; (m_pDocument->enumStyles(k,&szName,&pStyle)); k++)
-	{
-		if (!pStyle->isUsed())
-			continue;
-
-		if (!bWroteOpenStyleSection)
-		{
-			m_pie->write("<styles>\n");
-			bWroteOpenStyleSection = true;
-		}
-
-		PT_AttrPropIndex api = pStyle->getIndexAP();
-		_openTag("s","/",true,api);
-	}
-
-	if (bWroteOpenStyleSection)
-		m_pie->write("</styles>\n");
-
-	return;
-}
 
 void s_RTF_Listener::_handleDataItems(void)
 {
@@ -513,20 +493,13 @@
                 for (k=0; k<kLimit; k++)
                 {
                         const _rtf_font_info * pk = (const _rtf_font_info *)m_vecFonts.getNthItem(k);
-			const char * szFontName = NULL;
-			const char * szFamily = NULL;
-			int pitch;
-			bool bTrueType;
-			
-			_rtf_compute_font_properties(pk,&szFontName,&szFamily,&pitch,&bTrueType);
-			
                         _rtf_nl();
                         _rtf_open_brace();
-			_rtf_keyword("f",k);								// font index number
-			_rtf_keyword(szFamily);								// {\fnil,\froman,\fswiss,...}
-			_rtf_keyword("fcharset",charsetcode);
-			_rtf_keyword("fprq",pitch);							// {0==default,1==fixed,2==variable}
-			_rtf_keyword((bTrueType) ? "fttruetype" : "ftnil");	// {\fttruetype,\ftnil}
+			_rtf_keyword("f", k);								// font index number
+			_rtf_keyword(pk->szFamily);								// {\fnil,\froman,\fswiss,...}
+			_rtf_keyword("fcharset",pk->nCharset);
+			_rtf_keyword("fprq",pk->nPitch);							// {0==default,1==fixed,2==variable}
+			_rtf_keyword((pk->fTrueType) ? "fttruetype" : "ftnil");	// {\fttruetype,\ftnil}
                         
                         // we do nothing with or use default values for
                         // \falt \panose \fname \fbias \ftnil \fttruetype \fontfile
@@ -535,7 +508,7 @@
                         // the actual font name and a semicolon -- i couldn't see this
                         // described in the specification, but it was in other RTF files
                         // that i saw and really seems to help Word and WordPad....
-			_rtf_fontname(szFontName);
+			_rtf_fontname(pk->szName);
                         
                         _rtf_close_brace();
                 }
@@ -564,7 +537,7 @@
                 _rtf_close_brace();
         }
 
-	// TODO write the "style sheets"...
+	_write_stylesheets();
         // TODO write the "list table"...
         // TODO write the "rev table"...
 
@@ -576,12 +549,441 @@
         return (m_error == 0);
 }
 
+/*!
+ * Write an rtf keyword if the given property isn't the default
+ * value. Use this only with twips-valued properties.
+ *
+ * !param pStyle       A style.
+ * !param szPropName   The property to check.
+ * !param szRTFName    The RTF keyword to use if the property
+ *                     doesn't have the default value.
+ */
+void IE_Exp_RTF::_write_prop_ifnotdefault(const PD_Style * pStyle, 
+					  const XML_Char * szPropName, 
+					  const char * szRTFName)
+{
+    XML_Char * sz;
+    if (pStyle->getProperty(szPropName, sz)) {
+	_rtf_keyword_ifnotdefault_twips(szRTFName, sz, 0);
+    }
+}
+
+/*!
+ * Write an RTF keyword if the given property is "yes".
+ */
+void IE_Exp_RTF::_write_prop_ifyes(const PD_Style * pStyle, 
+				   const XML_Char * szPropName, 
+				   const char * szRTFName)
+{
+    XML_Char * sz;
+    if (pStyle->getProperty(szPropName, sz) && UT_strcmp(sz, "yes") == 0) {
+	    _rtf_keyword(szRTFName);
+    }
+}
+
+/*
+ * Used to hold tab information by _write_tabdef.
+ */
+class _t 
+{
+public:
+	_t(const char * szTL, const char * szTT, const char * szTK, UT_sint32 tp)
+		{
+			m_szTabLeaderKeyword = szTL;
+			m_szTabTypeKeyword = szTT;
+			m_szTabKindKeyword = szTK;
+			m_iTabPosition = tp;
+		}
+	const char *    m_szTabLeaderKeyword;
+	const char *	m_szTabTypeKeyword;
+	const char *	m_szTabKindKeyword;
+	UT_sint32		m_iTabPosition;
+};
+
+static int compare_tabs(const void* p1, const void* p2)
+{
+	_t ** ppTab1 = (_t **) p1;
+	_t ** ppTab2 = (_t **) p2;
+
+	if ((*ppTab1)->m_iTabPosition < (*ppTab2)->m_iTabPosition)
+		return -1;
+	if ((*ppTab1)->m_iTabPosition > (*ppTab2)->m_iTabPosition)
+		return 1;
+	return 0;
+}
+
+/*!
+ * Write out the <tabdef> paragraph formatting.
+ */
+void IE_Exp_RTF::_write_tabdef(const char * szTabStops)
+{
+	if (szTabStops && *szTabStops)
+	{
+		// write tabstops for this paragraph
+		// TODO the following parser was copied from abi/src/text/fmt/xp/fl_BlockLayout.cpp
+		// TODO we should extract both of them and share the code.
+
+		UT_Vector vecTabs;
+		
+		const char* pStart = szTabStops;
+		while (*pStart)
+		{
+			const char * szTT = "tx";	// TabType -- assume text tab (use "tb" for bar tab)
+			const char * szTK = NULL;	// TabKind -- assume left tab
+			const char * szTL = NULL;    // TabLeader
+			const char* pEnd = pStart;
+			while (*pEnd && (*pEnd != ','))
+				pEnd++;
+			const char* p1 = pStart;
+			while ((p1 < pEnd) && (*p1 != '/'))
+				p1++;
+			if ( (p1 == pEnd) || ((p1+1) == pEnd) )
+				;						// left-tab is default
+			else
+			{
+				switch (p1[1])
+				{
+				default:
+				case 'L': 	szTK = NULL; 	break;
+				case 'R':	szTK = "tqr";	break;
+				case 'C':	szTK = "tqc";	break;
+				case 'D':	szTK = "tqdec";	break;
+				case 'B':	szTT = "tb";    szTK= NULL;	break; // TabKind == bar tab
+				}
+				switch (p1[2])
+				{
+				default:
+				case '0': szTL = NULL;      break;
+				case '1': szTL = "tldot";   break;
+				case '2': szTL = "tlhyph";    break;
+				case '3': szTL = "tlul";    break;
+				case '4': szTL = "tleq";    break;
+				}
+			}
+
+			char pszPosition[32];
+			UT_uint32 iPosLen = p1 - pStart;
+			UT_ASSERT(iPosLen < 32);
+			UT_uint32 k;
+			for (k=0; k<iPosLen; k++)
+				pszPosition[k] = pStart[k];
+			pszPosition[k] = 0;
+			// convert position into twips
+			double dbl = UT_convertToPoints(pszPosition);
+			UT_sint32 d = (UT_sint32)(dbl * 20.0);
+			
+			_t * p_t = new _t(szTL,szTT,szTK,d);
+			vecTabs.addItem(p_t);
+
+			pStart = pEnd;
+			if (*pStart)
+			{
+				pStart++;	// skip past delimiter
+				while (*pStart == UCS_SPACE)
+					pStart++;
+			}
+		}
+
+		// write each tab in order:
+		// <tabdef> ::= ( <tab> | <bartab> )+
+		// <tab>    ::= <tabkind>? <tablead>? \tx
+		// <bartab> ::= <tablead>? \tb
+
+		vecTabs.qsort(compare_tabs);
+
+		UT_uint32 k;
+		UT_uint32 kLimit = vecTabs.getItemCount();
+		for (k=0; k<kLimit; k++)
+		{
+			_t * p_t = (_t *)vecTabs.getNthItem(k);
+			// write <tabkind>
+			if (p_t->m_szTabKindKeyword && *p_t->m_szTabKindKeyword)
+				_rtf_keyword(p_t->m_szTabKindKeyword);
+			if (p_t->m_szTabLeaderKeyword && *p_t->m_szTabLeaderKeyword)
+				_rtf_keyword(p_t->m_szTabLeaderKeyword);
+			_rtf_keyword(p_t->m_szTabTypeKeyword,p_t->m_iTabPosition);
+
+			delete p_t;
+		}
+	}
+}
+
+/*!
+ * Write out the <charfmt> paragraph or character formatting. This
+ * does not print opening and closing braces.
+ */
+void IE_Exp_RTF::_write_charfmt(const s_RTF_AttrPropAdapter & apa)
+{
+	const XML_Char * szColor = apa.getProperty("color");
+	UT_sint32 ndxColor = _findColor((char*)szColor);
+	UT_ASSERT(ndxColor != -1);
+
+	if (ndxColor != 0) // black text, the default
+		_rtf_keyword("cf",ndxColor);
+
+	szColor = apa.getProperty("bgcolor");
+
+	if (szColor && UT_stricmp (szColor, "transparent") != 0)
+	{
+		ndxColor = _findColor((char*)szColor);
+		UT_ASSERT(ndxColor != -1);
+		if (ndxColor != 1) // white background, the default
+		{
+			_rtf_keyword("cb",ndxColor);
+		}
+	}
+
+   	_rtf_font_info fi(apa);
+	UT_sint32 ndxFont = _findFont(&fi);
+	UT_ASSERT(ndxFont != -1);
+	_rtf_keyword("f",ndxFont);	// font index in fonttbl
+
+	const XML_Char * szFontSize = apa.getProperty("font-size");
+	double dbl = UT_convertToPoints(szFontSize);
+	UT_sint32 d = (UT_sint32)(dbl*2.0);
+
+	// if (d != 24) - always write this out
+	_rtf_keyword("fs",d);	// font size in half points
+
+	const XML_Char * szFontStyle = apa.getProperty("font-style");
+	if (szFontStyle && *szFontStyle && (UT_strcmp(szFontStyle,"italic")==0))
+		_rtf_keyword("i");
+
+	const XML_Char * szFontWeight = apa.getProperty("font-weight");
+	if (szFontWeight && *szFontWeight && (UT_strcmp(szFontWeight,"bold")==0))
+		_rtf_keyword("b");
+
+	const XML_Char * szFontDecoration = apa.getProperty("text-decoration");
+	if (szFontDecoration && *szFontDecoration)
+	{
+		if (strstr(szFontDecoration,"underline") != 0)
+			_rtf_keyword("ul");
+		if (strstr(szFontDecoration,"overline") != 0)
+			_rtf_keyword("ol");
+		if (strstr(szFontDecoration,"line-through") != 0)
+			_rtf_keyword("strike");
+		if (strstr(szFontDecoration,"topline") != 0)
+		{
+			_rtf_keyword("*");
+			_rtf_keyword("topline");
+		}
+		if (strstr(szFontDecoration,"bottomline") != 0)
+		{
+			_rtf_keyword("*");
+			_rtf_keyword("botline");
+		}
+	}
+
+	const XML_Char * szFontPosition = apa.getProperty("text-position");
+	if (szFontPosition && *szFontPosition)
+	{
+		if (!UT_strcmp(szFontPosition,"superscript"))
+			_rtf_keyword("super");
+		else if (!UT_strcmp(szFontPosition,"subscript"))
+			_rtf_keyword("sub");
+	}
+
+#if 0
+	const XML_Char * szLang = apa.getProperty("lang");
+	// TODO: convert lang to numerical code
+#endif
+
+#ifdef BIDI_ENABLED
+
+	const XML_Char * szDir = apa.getProperty("dir");
+
+	if (szDir)
+	{
+		if (!UT_strcmp (szDir, "ltr"))
+			_rtf_keyword ("ltrch");
+		else
+			_rtf_keyword ("rtlch");
+	}
+
+#endif
+	// TODO do something with our font-stretch and font-variant properties
+	// note: we assume that kerning has been turned off at global scope.
+}
+
+/*!
+ * Write out the formatting group for one style in the RTF header.
+ */
+void IE_Exp_RTF::_write_style_fmt(const PD_Style * pStyle)
+{
+    // brdrdef: not implemented because AbiWord does not have borders
+    // at time of this writing.
+
+    // parfmt
+    _write_prop_ifyes(pStyle, "keep-together", "keep");
+    _write_prop_ifyes(pStyle, "keep-with-next", "keepn");
+
+    XML_Char * sz;
+    if (pStyle->getProperty("text-align", sz)) {
+	if (UT_strcmp(sz, "left") == 0) {
+	    // Default, so no need to print anything
+	} else if (UT_strcmp(sz, "right") == 0) {
+	    _rtf_keyword("qr");
+	} else if (UT_strcmp(sz, "center") == 0) {
+	    _rtf_keyword("qc");
+	} else if (UT_strcmp(sz, "justify") == 0) {
+	    _rtf_keyword("qj");
+	} else {
+	    UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
+	}
+    }
+
+    XML_Char * szLineHeight;
+    if (pStyle->getProperty("line-height", szLineHeight)
+	&& strcmp(szLineHeight,"1.0") != 0) {
+	double f = UT_convertDimensionless(szLineHeight);
+	if (f != 0.0) {
+	    UT_sint32 dSpacing = (UT_sint32)(f * 240.0);
+	    _rtf_keyword("sl",dSpacing);
+	    _rtf_keyword("slmult",1);
+	}
+    }
+
+    _write_prop_ifnotdefault(pStyle, "text-indent", "fi");
+    _write_prop_ifnotdefault(pStyle, "margin-left", "li");
+    _write_prop_ifnotdefault(pStyle, "margin-right", "ri");
+    _write_prop_ifnotdefault(pStyle, "margin-top", "sb");
+    _write_prop_ifnotdefault(pStyle, "margin-bottom", "sa");
+
+    // apoctl
+
+    // tabdef
+    if (pStyle->getProperty("tabstops", sz)) _write_tabdef(sz);
+    
+
+    // shading
+
+    // chrfmt
+    _write_charfmt(s_RTF_AttrPropAdapter_Style(pStyle));
+}
+
+/*!
+ * This is just a pair: style and style number. It is used for
+ * two-way association of PD_Style and RTF style number.
+ */
+struct NumberedStyle 
+{
+    const PD_Style * pStyle;
+    UT_uint32 n;
+
+    NumberedStyle(const PD_Style * pStyle, UT_uint32 n) : 
+	pStyle(pStyle), n(n) {}
+};
+
+/*!
+ * Clear the style hash.
+ */
+void IE_Exp_RTF::_clearStyles()
+{
+    UT_HASH_PURGEDATA(NumberedStyle *, &m_hashStyles, delete);
+}
+
+/*!
+ * Select styles for export. This inserts all styles to be exported
+ * into the style hash. Also, it makes sure that all fonts used in
+ * styles are present in the font table.
+ */
+void IE_Exp_RTF::_selectStyles()
+{
+    _clearStyles();
+
+    UT_uint32 i;
+    UT_uint32 nStyleNumber = 0;
+    const char * szName;
+    const PD_Style * pStyle;
+    for (i = 0; getDoc()->enumStyles(i, &szName, &pStyle); ++i) {
+	if (pStyle->isUsed() || pStyle->isUserDefined()) {
+	    m_hashStyles.insert(szName, new NumberedStyle(pStyle, ++nStyleNumber));
+	    _rtf_font_info fi(static_cast<s_RTF_AttrPropAdapter_Style>(pStyle));
+	    if (_findFont(&fi) == -1)
+		_addFont(&fi);
+	}
+    }
+}
+
+/*!
+ * Return the style number that was assigned to the given style.
+ * The style must be present in the style hash.
+ */
+UT_uint32 IE_Exp_RTF::_getStyleNumber(const PD_Style * pStyle)
+{
+    return _getStyleNumber(pStyle->getName());
+}
+
+/*!
+ * Return the style number that was assigned to the named style.
+ * The style must be present in the style hash.
+ */
+UT_uint32 IE_Exp_RTF::_getStyleNumber(const XML_Char * szStyle)
+{
+    const NumberedStyle * pns = reinterpret_cast<const NumberedStyle *>(m_hashStyles.pick(szStyle));
+    UT_ASSERT(pns != NULL);
+    return pns->n;
+}
+
+/*!
+ * Write the stylesheets group of the RTF header. Only styles that
+ * are used by the document are written.
+ */
+void IE_Exp_RTF::_write_stylesheets(void)
+{
+    if (getDoc()->getStyleCount() == 0) return;
+
+    _rtf_nl();
+    _rtf_open_brace();
+    _rtf_keyword("stylesheet");
+
+    UT_StringPtrMap::UT_Cursor hc(&m_hashStyles);
+    const NumberedStyle * pns;
+    for (pns = reinterpret_cast<const NumberedStyle *>(hc.first()); 
+	 hc.is_valid(); 
+	 pns = reinterpret_cast<const NumberedStyle *>(hc.next())) {
+	const PD_Style * pStyle = pns->pStyle;
+	_rtf_nl();
+	_rtf_open_brace();
+
+	if (pStyle->isCharStyle()) {
+	    _rtf_keyword("*");
+	    _rtf_keyword("cs", pns->n);
+	} else {
+	    _rtf_keyword("s", pns->n);
+	}
+
+	_write_style_fmt(pStyle);
+
+	const PD_Style * pStyleBasedOn = pStyle->getBasedOn();
+	// TODO: Can this really return NULL?
+	if (pStyleBasedOn != NULL) {
+	    _rtf_keyword("sbasedon", _getStyleNumber(pStyleBasedOn));
+	}
+
+	const PD_Style * pStyleNext = pStyle->getFollowedBy();
+	// TODO: Can this really return NULL?
+	if (pStyleNext != NULL) {
+	    _rtf_keyword("snext", _getStyleNumber(pStyleNext));
+	}
+	
+	_rtf_chardata(pStyle->getName(), strlen(pStyle->getName()));
+	_rtf_close_brace();
+    }
+
+    _rtf_close_brace();
+}
+
 bool IE_Exp_RTF::_write_rtf_trailer(void)
 {
         _rtf_close_brace();
         return (m_error == 0);
 }
 
+/*!
+ * Find a font in the font table. Return the index of the font. If
+ * it is not found, return -1.
+ */
 UT_sint32 IE_Exp_RTF::_findFont(const _rtf_font_info * pfi) const
 {
         UT_ASSERT(pfi);
@@ -592,60 +994,56 @@
         for (k=0; k<kLimit; k++)
         {
                 const _rtf_font_info * pk = (const _rtf_font_info *)m_vecFonts.getNthItem(k);
-		if (pk->_is_same(pfi))
+		if (pk->_is_same(*pfi))
                         return k;
         }
 
         return -1;
 }
 
+/*!
+ * Add a font to the font table. The font must not be present
+ * in the font table at the start of the call.
+ */
 void IE_Exp_RTF::_addFont(const _rtf_font_info * pfi)
 {
         UT_ASSERT(pfi && (_findFont(pfi)==-1));
-
-	// note: this does not guarantee uniqueness of actual fonts,
-	// note: since the three AP's may have other stuff besides
-	// note: just font info -- two identical fonts with different
-	// note: colors, for example -- will appear as two distinct
-	// note: entries -- we don't care.
-	
         _rtf_font_info * pNew = new _rtf_font_info(*pfi);
         if (pNew)
                 m_vecFonts.addItem(pNew);
 
         return;
 }
-
-void IE_Exp_RTF::_rtf_compute_font_properties(const _rtf_font_info * pfi,
-											  const char ** p_sz_font_name,
-											  const char ** p_sz_rtf_family,
-											  int * p_rtf_pitch,
-											  bool * p_rtf_bTrueType) const
-{
-	static const char * t_ff[] = { "fnil", "froman", "fswiss", "fmodern", "fscript", "fdecor", "ftech", "fbidi" };
-
-	const XML_Char * szFontFamily = PP_evalProperty("font-family",
-													pfi->m_pSpanAP,pfi->m_pBlockAP,pfi->m_pSectionAP,
-													getDoc(),true);
-
-	GR_Font::FontFamilyEnum ff;
-	GR_Font::FontPitchEnum fp;
-	bool tt;
-	
-	GR_Font::s_getGenericFontProperties((char*)szFontFamily, &ff, &fp, &tt);
 
-	// TODO there is a general confusion in this program between fontname and fontfamily.
-	// TODO one is "Courier New" and the other is "Modern".  it seems that we interchange
-	// TODO these in a few places....
-	
-	*p_sz_font_name = szFontFamily;
-	
-	if ((ff >= 0) && (ff < (int)NrElements(t_ff)))
-		*p_sz_rtf_family = t_ff[ff];
-	else
-		*p_sz_rtf_family = t_ff[GR_Font::FF_Unknown];
-
-	*p_rtf_pitch = fp;
-
-	*p_rtf_bTrueType = tt;
+_rtf_font_info::_rtf_font_info(const s_RTF_AttrPropAdapter & apa)
+{
+    // Not a typo. The AbiWord "font-family" property is what RTF
+    // calls font name. It has values like "Courier New".
+    szName = apa.getProperty("font-family");
+    
+    static const char * t_ff[] = { "fnil", "froman", "fswiss", "fmodern", "fscript", "fdecor", "ftech", "fbidi" };
+    GR_Font::FontFamilyEnum ff;
+    GR_Font::FontPitchEnum fp;
+    bool tt;
+    GR_Font::s_getGenericFontProperties((char*)szName, &ff, &fp, &tt);
+
+    if ((ff >= 0) && (ff < (int)NrElements(t_ff)))
+	szFamily = t_ff[ff];
+    else
+	szFamily = t_ff[GR_Font::FF_Unknown];
+    nCharset = XAP_EncodingManager::get_instance()->getWinCharsetCode();
+    nPitch = fp;
+    fTrueType = tt;
+}
+
+/*!
+ * True if the two objects represent the same RTF font.
+ */
+bool _rtf_font_info::_is_same(const _rtf_font_info & fi) const
+{
+    return UT_strcmp(szFamily, fi.szFamily) == 0
+	&& nCharset == fi.nCharset
+	&& nPitch == fi.nPitch
+	&& UT_strcmp(szName, fi.szName) == 0
+	&& fTrueType == fi.fTrueType;
 }
Index: impexp/xp/ie_exp_RTF.h
===================================================================
RCS file: /cvsroot/abi/src/wp/impexp/xp/ie_exp_RTF.h,v
retrieving revision 1.13
diff -u -r1.13 ie_exp_RTF.h
--- impexp/xp/ie_exp_RTF.h	2001/08/09 20:57:44	1.13
+++ impexp/xp/ie_exp_RTF.h	2001/09/12 22:00:41
@@ -23,12 +23,15 @@
 
 #include "ie_exp.h"
 #include "ut_vector.h"
+#include "ut_hash.h"
 #include "ut_misc.h"
 #include "pl_Listener.h"
 class PD_Document;
+class PD_Style;
 class PP_AttrProp;
 class s_RTF_ListenerWriteDoc;
 class s_RTF_ListenerGetProps;
+class s_RTF_AttrPropAdapter;
 struct _rtf_font_info;
 
 // The exporter/writer for RTF file format (based upon spec version 1.5).
@@ -99,13 +102,21 @@
         void				_rtf_nl(void);
         bool				_write_rtf_header(void);
         bool				_write_rtf_trailer(void);
+
+	void                            _clearStyles();
+	void                            _selectStyles();
+	UT_uint32                       _getStyleNumber(const PD_Style * pStyle);
+	UT_uint32                       _getStyleNumber(const XML_Char * szStyleName);
+
+	void                            _write_prop_ifnotdefault(const PD_Style * pStyle, const XML_Char * szPropName, const char * szRTFName);
+	void                            _write_prop_ifyes(const PD_Style * pStyle, const XML_Char * szPropName, const char * szRTFName);
+	void                            _write_tabdef(const char * szTabStops);
+        void                            _write_charfmt(const s_RTF_AttrPropAdapter &);
+        void                            _write_style_fmt(const PD_Style *);
+	void                            _write_stylesheets(void);
+
         UT_sint32			_findFont(const _rtf_font_info * pfi) const;
         void				_addFont(const _rtf_font_info * pfi);
-	void				_rtf_compute_font_properties(const _rtf_font_info * pfi,
-													 const char ** p_sz_font_name,
-													 const char ** p_sz_rtf_family,
-													 int * p_rtf_pitch,
-													 bool * pbTrueType) const;
 
  private:	
         s_RTF_ListenerWriteDoc *	m_pListenerWriteDoc;
@@ -116,39 +127,28 @@
         UT_sint32					m_braceLevel;			/* nesting depth of {} braces */
         bool						m_bLastWasKeyword;		/* just wrote a keyword, so need space before text data */
         bool						m_atticFormat; 		/* whether to use unicode for all characters >0xff or convert to native windows encoding*/
+	UT_StringPtrMap                                 m_hashStyles;
+	/* Hash containing styles to be exported. The key is the
+	   AbiWord style name. The value is a NumberedStyle object
+	   (see the cpp file). */
 };
 
 /*****************************************************************/
 /*****************************************************************/
 
+/* This struct contains the RTF font info as needed for the 
+   font table. */
 struct _rtf_font_info
 {
-	_rtf_font_info(const PP_AttrProp * pSpanAP,
-				   const PP_AttrProp * pBlockAP,
-				   const PP_AttrProp * pSectionAP)
-       : m_pSpanAP(pSpanAP), m_pBlockAP(pBlockAP), m_pSectionAP(pSectionAP)
-		{
-		}
-
-	~_rtf_font_info(void)
-		{
-		}
-
-	bool _is_same(const PP_AttrProp * pSpanAP,
-					 const PP_AttrProp * pBlockAP,
-					 const PP_AttrProp * pSectionAP) const
-		{
-			return ((pSpanAP==m_pSpanAP) && (pBlockAP==m_pBlockAP) && (pSectionAP==m_pSectionAP));
-		}
-
-	bool _is_same(const _rtf_font_info * pfi) const
-		{
-			return _is_same(pfi->m_pSpanAP,pfi->m_pBlockAP,pfi->m_pSectionAP);
-		}
-	
-	const PP_AttrProp * m_pSpanAP;
-	const PP_AttrProp * m_pBlockAP;
-	const PP_AttrProp * m_pSectionAP;
+    _rtf_font_info(const s_RTF_AttrPropAdapter & apa);
+    bool _is_same(const _rtf_font_info & fi) const;
+    
+    /* Neither of the char variables should be freed. */
+    const XML_Char * szFamily;
+    int nCharset;
+    int nPitch;
+    const XML_Char * szName;
+    bool fTrueType;
 };
 
 #endif /* IE_EXP_RTF_H */
Index: impexp/xp/ie_exp_RTF_listenerGetProps.cpp
===================================================================
RCS file: /cvsroot/abi/src/wp/impexp/xp/ie_exp_RTF_listenerGetProps.cpp,v
retrieving revision 1.7
diff -u -r1.7 ie_exp_RTF_listenerGetProps.cpp
--- impexp/xp/ie_exp_RTF_listenerGetProps.cpp	2001/05/03 22:08:39	1.7
+++ impexp/xp/ie_exp_RTF_listenerGetProps.cpp	2001/09/12 22:00:41
@@ -26,6 +26,7 @@
 
 #include "ut_string.h"
 #include "ie_exp_RTF_listenerGetProps.h"
+#include "ie_exp_RTF_AttrProp.h"
 #include "pd_Document.h"
 #include "pp_AttrProp.h"
 #include "pp_Property.h"
@@ -274,7 +275,7 @@
         // write it out now.  so, we build a vector of the stuff we want
         // to write (and make sure it's unique).
 
-	_rtf_font_info fi(pSpanAP,pBlockAP,pSectionAP);
+	_rtf_font_info fi(s_RTF_AttrPropAdapter_AP(pSpanAP,pBlockAP,pSectionAP,m_pDocument));
         UT_sint32 ndxFont = m_pie->_findFont(&fi);
         if (ndxFont == -1)
                 m_pie->_addFont(&fi);
Index: impexp/xp/ie_exp_RTF_listenerWriteDoc.cpp
===================================================================
RCS file: /cvsroot/abi/src/wp/impexp/xp/ie_exp_RTF_listenerWriteDoc.cpp,v
retrieving revision 1.48
diff -u -r1.48 ie_exp_RTF_listenerWriteDoc.cpp
--- impexp/xp/ie_exp_RTF_listenerWriteDoc.cpp	2001/07/19 15:56:35	1.48
+++ impexp/xp/ie_exp_RTF_listenerWriteDoc.cpp	2001/09/12 22:00:41
@@ -34,6 +34,7 @@
 #include "ut_png.h"
 #include "ut_bytebuf.h"
 #include "ie_exp_RTF_listenerWriteDoc.h"
+#include "ie_exp_RTF_AttrProp.h"
 #include "pd_Document.h"
 #include "pp_AttrProp.h"
 #include "pp_Property.h"
@@ -90,100 +91,10 @@
         m_pDocument->getAttrProp(m_apiThisBlock,&pBlockAP);
         m_pDocument->getAttrProp(apiSpan,&pSpanAP);
 
-	const XML_Char * szColor = PP_evalProperty("color",pSpanAP,pBlockAP,pSectionAP,m_pDocument,true);
-	UT_sint32 ndxColor = m_pie->_findColor((char*)szColor);
-	UT_ASSERT(ndxColor != -1);
+	m_pie->_write_charfmt(s_RTF_AttrPropAdapter_AP(pSpanAP, pBlockAP, pSectionAP, m_pDocument));
 
-	if (ndxColor != 0) // black text, the default
-		m_pie->_rtf_keyword("cf",ndxColor);
-
-	szColor = PP_evalProperty("bgcolor",pSpanAP,pBlockAP,pSectionAP,m_pDocument,true);
-
-	if (UT_stricmp (szColor, "transparent") != 0)
-	{
-		ndxColor = m_pie->_findColor((char*)szColor);
-		UT_ASSERT(ndxColor != -1);
-		if (ndxColor != 1) // white background, the default
-		{
-			m_pie->_rtf_keyword("cb",ndxColor);
-		}
-	}
-
-   	_rtf_font_info fi(pSpanAP,pBlockAP,pSectionAP);
-	UT_sint32 ndxFont = m_pie->_findFont(&fi);
-	UT_ASSERT(ndxFont != -1);
-	m_pie->_rtf_keyword("f",ndxFont);	// font index in fonttbl
-
-	const XML_Char * szFontSize = PP_evalProperty("font-size",pSpanAP,pBlockAP,pSectionAP,m_pDocument,true);
-	double dbl = UT_convertToPoints(szFontSize);
-	UT_sint32 d = (UT_sint32)(dbl*2.0);
-
-	// if (d != 24) - always write this out
-	m_pie->_rtf_keyword("fs",d);	// font size in half points
-
-	const XML_Char * szFontStyle = PP_evalProperty("font-style",pSpanAP,pBlockAP,pSectionAP,m_pDocument,true);
-	if (szFontStyle && *szFontStyle && (UT_strcmp(szFontStyle,"italic")==0))
-		m_pie->_rtf_keyword("i");
-
-	const XML_Char * szFontWeight = PP_evalProperty("font-weight",pSpanAP,pBlockAP,pSectionAP,m_pDocument,true);
-	if (szFontWeight && *szFontWeight && (UT_strcmp(szFontWeight,"bold")==0))
-		m_pie->_rtf_keyword("b");
-
-	const XML_Char * szFontDecoration = PP_evalProperty("text-decoration",pSpanAP,pBlockAP,pSectionAP,m_pDocument,true);
-	if (szFontDecoration && *szFontDecoration)
-	{
-		if (strstr(szFontDecoration,"underline") != 0)
-			m_pie->_rtf_keyword("ul");
-		if (strstr(szFontDecoration,"overline") != 0)
-			m_pie->_rtf_keyword("ol");
-		if (strstr(szFontDecoration,"line-through") != 0)
-			m_pie->_rtf_keyword("strike");
-		if (strstr(szFontDecoration,"topline") != 0)
-		{
-			m_pie->_rtf_keyword("*");
-			m_pie->_rtf_keyword("topline");
-		}
-		if (strstr(szFontDecoration,"bottomline") != 0)
-		{
-			m_pie->_rtf_keyword("*");
-			m_pie->_rtf_keyword("botline");
-		}
-	}
-
-	const XML_Char * szFontPosition = PP_evalProperty("text-position",pSpanAP,pBlockAP,pSectionAP,m_pDocument,true);
-	if (szFontPosition && *szFontPosition)
-	{
-		if (!UT_strcmp(szFontPosition,"superscript"))
-			m_pie->_rtf_keyword("super");
-		else if (!UT_strcmp(szFontPosition,"subscript"))
-			m_pie->_rtf_keyword("sub");
-	}
-
-#if 0
-	const XML_Char * szLang = PP_evalProperty("lang",pSpanAP,pBlockAP,pSectionAP,m_pDocument,true);
-	// TODO: convert lang to numerical code
-#endif
-
-#ifdef BIDI_ENABLED
-
-	const XML_Char * szDir = PP_evalProperty("dir",pSpanAP,pBlockAP,pSectionAP,m_pDocument,true);
-
-	if (szDir)
-	{
-		if (!UT_strcmp (szDir, "ltr"))
-			m_pie->_rtf_keyword ("ltrch");
-		else
-			m_pie->_rtf_keyword ("rtlch");
-	}
-
-#endif
-
-	// TODO do something with our font-stretch and font-variant properties
-	// note: we assume that kerning has been turned off at global scope.
-	
         m_bInSpan = true;
         m_apiLastSpan = apiSpan;
-	return;
 }
 
 void s_RTF_ListenerWriteDoc::_outputData(const UT_UCSChar * data, UT_uint32 length)
@@ -696,34 +607,6 @@
 
 //////////////////////////////////////////////////////////////////
 
-class _t 
-{
-public:
-	_t(const char * szTL, const char * szTT, const char * szTK, UT_sint32 tp)
-		{
-			m_szTabLeaderKeyword = szTL;
-			m_szTabTypeKeyword = szTT;
-			m_szTabKindKeyword = szTK;
-			m_iTabPosition = tp;
-		}
-	const char *    m_szTabLeaderKeyword;
-	const char *	m_szTabTypeKeyword;
-	const char *	m_szTabKindKeyword;
-	UT_sint32		m_iTabPosition;
-};
-
-static int compare_tabs(const void* p1, const void* p2)
-{
-	_t ** ppTab1 = (_t **) p1;
-	_t ** ppTab2 = (_t **) p2;
-
-	if ((*ppTab1)->m_iTabPosition < (*ppTab2)->m_iTabPosition)
-		return -1;
-	if ((*ppTab1)->m_iTabPosition > (*ppTab2)->m_iTabPosition)
-		return 1;
-	return 0;
-}
-
 void s_RTF_ListenerWriteDoc::_rtf_open_block(PT_AttrPropIndex api)
 {
         m_apiThisBlock = api;
@@ -786,6 +669,10 @@
         
         m_pie->_rtf_keyword("pard");		// restore all defaults for this paragraph
 
+	XML_Char * szStyle;
+	if (pBlockAP->getAttribute("style", szStyle)) {
+	    m_pie->_rtf_keyword("s", m_pie->_getStyleNumber(szStyle));
+	}
 
         ///
         /// OK if there is list info in this paragraph we encase it inside
@@ -1058,96 +945,8 @@
                 m_pie->_rtf_keyword("keep");
         if (UT_strcmp(szKeepWithNext,"yes")==0)
                 m_pie->_rtf_keyword("keepn");
-
-	if (szTabStops && *szTabStops)
-	{
-		// write tabstops for this paragraph
-		// TODO the following parser was copied from abi/src/text/fmt/xp/fl_BlockLayout.cpp
-		// TODO we should extract both of them and share the code.
-
-		UT_Vector vecTabs;
-		
-		const char* pStart = szTabStops;
-		while (*pStart)
-		{
-			const char * szTT = "tx";	// TabType -- assume text tab (use "tb" for bar tab)
-			const char * szTK = NULL;	// TabKind -- assume left tab
-			const char * szTL = NULL;    // TabLeader
-			const char* pEnd = pStart;
-			while (*pEnd && (*pEnd != ','))
-				pEnd++;
-			const char* p1 = pStart;
-			while ((p1 < pEnd) && (*p1 != '/'))
-				p1++;
-			if ( (p1 == pEnd) || ((p1+1) == pEnd) )
-				;						// left-tab is default
-			else
-			{
-				switch (p1[1])
-				{
-				default:
-				case 'L': 	szTK = NULL; 	break;
-				case 'R':	szTK = "tqr";	break;
-				case 'C':	szTK = "tqc";	break;
-				case 'D':	szTK = "tqdec";	break;
-				case 'B':	szTT = "tb";    szTK= NULL;	break; // TabKind == bar tab
-				}
-				switch (p1[2])
-				{
-				default:
-				case '0': szTL = NULL;      break;
-				case '1': szTL = "tldot";   break;
-				case '2': szTL = "tlhyph";    break;
-				case '3': szTL = "tlul";    break;
-				case '4': szTL = "tleq";    break;
-				}
-			}
-
-			char pszPosition[32];
-			UT_uint32 iPosLen = p1 - pStart;
-			UT_ASSERT(iPosLen < 32);
-			UT_uint32 k;
-			for (k=0; k<iPosLen; k++)
-				pszPosition[k] = pStart[k];
-			pszPosition[k] = 0;
-			// convert position into twips
-			double dbl = UT_convertToPoints(pszPosition);
-			UT_sint32 d = (UT_sint32)(dbl * 20.0);
-			
-			_t * p_t = new _t(szTL,szTT,szTK,d);
-			vecTabs.addItem(p_t);
 
-			pStart = pEnd;
-			if (*pStart)
-			{
-				pStart++;	// skip past delimiter
-				while (*pStart == UCS_SPACE)
-					pStart++;
-			}
-		}
-
-		// write each tab in order:
-		// <tabdef> ::= ( <tab> | <bartab> )+
-		// <tab>    ::= <tabkind>? <tablead>? \tx
-		// <bartab> ::= <tablead>? \tb
-
-		vecTabs.qsort(compare_tabs);
-
-		UT_uint32 k;
-		UT_uint32 kLimit = vecTabs.getItemCount();
-		for (k=0; k<kLimit; k++)
-		{
-			_t * p_t = (_t *)vecTabs.getNthItem(k);
-			// write <tabkind>
-			if (p_t->m_szTabKindKeyword && *p_t->m_szTabKindKeyword)
-				m_pie->_rtf_keyword(p_t->m_szTabKindKeyword);
-			if (p_t->m_szTabLeaderKeyword && *p_t->m_szTabLeaderKeyword)
-				m_pie->_rtf_keyword(p_t->m_szTabLeaderKeyword);
-			m_pie->_rtf_keyword(p_t->m_szTabTypeKeyword,p_t->m_iTabPosition);
-
-			delete p_t;
-		}
-	}
+	m_pie->_write_tabdef(szTabStops);
 }
 
 //////////////////////////////////////////////////////////////////
This archive was generated by hypermail 2b25 : Wed Sep 12 2001 - 17:06:53 CDT