1 /* -*- mode: C++; tab-width: 4; c-basic-offset: 4; -*- */
2 
3 /* AbiWord
4  * Copyright (C) 1998 AbiSource, Inc.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19  * 02110-1301 USA.
20  */
21 
22 #include <stdlib.h>
23 #include <string>
24 #include "ut_string.h"
25 #include "ut_bytebuf.h"
26 #include "ut_base64.h"
27 #include "ut_misc.h"
28 #include "ut_units.h"
29 #include "ut_vector.h"
30 #include "ut_endian.h"
31 #include "pt_Types.h"
32 #include "ie_impexp_RTF.h"
33 #include "ie_exp_RTF.h"
34 #include "pd_Document.h"
35 #include "pd_DocumentRDF.h"
36 #include "pd_RDFSupport.h"
37 #include "pp_AttrProp.h"
38 #include "pp_Property.h"
39 #include "pp_Revision.h"
40 #include "px_ChangeRecord.h"
41 #include "px_CR_Object.h"
42 #include "px_CR_Span.h"
43 #include "px_CR_Strux.h"
44 #include "pd_Style.h"
45 #include "gr_Graphics.h"
46 #include "ut_rand.h"
47 #include "ut_std_string.h"
48 #include "pl_ListenerCoupleCloser.h"
49 
50 #include "wv.h" //for wvLIDToCodePageConverter
51 #include "xap_EncodingManager.h"
52 #include "ut_debugmsg.h"
53 
54 #include <sstream>
55 
56 /*****************************************************************/
57 /*****************************************************************/
58 
59 #include "ie_exp_RTF_AttrProp.h"
60 #include "ie_exp_RTF_listenerWriteDoc.h"
61 #include "ie_exp_RTF_listenerGetProps.h"
62 
63 /*****************************************************************/
64 /*****************************************************************/
65 
IE_Exp_RTF(PD_Document * pDocument)66 IE_Exp_RTF::IE_Exp_RTF(PD_Document * pDocument)
67 	: IE_Exp(pDocument)
68 {
69 	m_error = 0;
70 	m_pListenerWriteDoc = NULL;
71 	m_bNeedUnicodeText = false;
72 	m_braceLevel = 0;
73 	m_bLastWasKeyword = false;
74 	m_atticFormat = false;
75 	m_CharRTL = UT_BIDI_UNSET;
76 	m_conv = UT_iconv_open("UCS-4", "utf-8");
77 }
78 
IE_Exp_RTF(PD_Document * pDocument,bool atticFormat)79 IE_Exp_RTF::IE_Exp_RTF(PD_Document * pDocument,bool atticFormat)
80 	: IE_Exp(pDocument)
81 {
82 	m_error = 0;
83 	m_pListenerWriteDoc = NULL;
84 	m_bNeedUnicodeText = false;
85 	m_braceLevel = 0;
86 	m_bLastWasKeyword = false;
87 	m_atticFormat = atticFormat;
88 	m_CharRTL = UT_BIDI_UNSET;
89 	m_conv = UT_iconv_open("UCS-4", "utf-8");
90 }
91 
~IE_Exp_RTF()92 IE_Exp_RTF::~IE_Exp_RTF()
93 {
94 	UT_VECTOR_FREEALL(char *,m_vecColors);
95 	UT_VECTOR_PURGEALL(_rtf_font_info *,m_vecFonts);
96 	_clearStyles();
97 	if (UT_iconv_isValid(m_conv))
98 	{
99 		UT_iconv_close(m_conv);
100 	}
101 }
102 
103 /*****************************************************************/
104 /*****************************************************************/
105 
IE_Exp_RTF_Sniffer()106 IE_Exp_RTF_Sniffer::IE_Exp_RTF_Sniffer ()
107 	: IE_ExpSniffer(IE_IMPEXPNAME_RTF, true)
108 {
109 	//
110 }
111 
supportsMIME(const char * szMIME)112 UT_Confidence_t IE_Exp_RTF_Sniffer::supportsMIME (const char * szMIME)
113 {
114 	if (strcmp (szMIME, IE_MIMETYPE_RTF) == 0)
115 		{
116 			return UT_CONFIDENCE_PERFECT;
117 		}
118 	return UT_CONFIDENCE_ZILCH;
119 }
120 
recognizeSuffix(const char * szSuffix)121 bool IE_Exp_RTF_Sniffer::recognizeSuffix(const char * szSuffix)
122 {
123 	return (!g_ascii_strcasecmp(szSuffix,".rtf"));
124 }
125 
constructExporter(PD_Document * pDocument,IE_Exp ** ppie)126 UT_Error IE_Exp_RTF_Sniffer::constructExporter(PD_Document * pDocument,
127 											   IE_Exp ** ppie)
128 {
129 	IE_Exp_RTF * p = new IE_Exp_RTF(pDocument);
130 	*ppie = p;
131 	return UT_OK;
132 }
133 
getDlgLabels(const char ** pszDesc,const char ** pszSuffixList,IEFileType * ft)134 bool IE_Exp_RTF_Sniffer::getDlgLabels(const char ** pszDesc,
135 									  const char ** pszSuffixList,
136 									  IEFileType * ft)
137 {
138 	*pszDesc = "Rich Text Format (.rtf)";
139 	*pszSuffixList = "*.rtf";
140 	*ft = getFileType();
141 	return true;
142 }
143 
144 /*for attic*/
145 
IE_Exp_RTF_attic_Sniffer()146 IE_Exp_RTF_attic_Sniffer::IE_Exp_RTF_attic_Sniffer ()
147 	: IE_ExpSniffer(IE_IMPEXPNAME_RTFATTIC, false)
148 {
149 	//
150 }
151 
recognizeSuffix(const char * szSuffix)152 bool IE_Exp_RTF_attic_Sniffer::recognizeSuffix(const char * szSuffix)
153 {
154 	return (!g_ascii_strcasecmp(szSuffix,".rtf"));
155 }
156 
constructExporter(PD_Document * pDocument,IE_Exp ** ppie)157 UT_Error IE_Exp_RTF_attic_Sniffer::constructExporter(PD_Document * pDocument,
158 											   IE_Exp ** ppie)
159 {
160 	IE_Exp_RTF * p = new IE_Exp_RTF(pDocument,1);
161 	*ppie = p;
162 	return UT_OK;
163 }
164 
getDlgLabels(const char ** pszDesc,const char ** pszSuffixList,IEFileType * ft)165 bool IE_Exp_RTF_attic_Sniffer::getDlgLabels(const char ** pszDesc,
166 									  const char ** pszSuffixList,
167 									  IEFileType * ft)
168 {
169 	*pszDesc = "Rich Text Format for old apps (.rtf)";
170 	*pszSuffixList = "*.rtf";
171 	*ft = getFileType();
172 	return true;
173 }
174 
IE_Exp_MsWord_Hack_Sniffer()175 IE_Exp_MsWord_Hack_Sniffer::IE_Exp_MsWord_Hack_Sniffer ()
176 	: IE_ExpSniffer(IE_IMPEXPNAME_RTFMSDOC)
177 {
178 	//
179 }
180 
recognizeSuffix(const char * szSuffix)181 bool IE_Exp_MsWord_Hack_Sniffer::recognizeSuffix(const char * szSuffix)
182 {
183 	return (!g_ascii_strcasecmp(szSuffix,".doc"));
184 }
185 
constructExporter(PD_Document * pDocument,IE_Exp ** ppie)186 UT_Error IE_Exp_MsWord_Hack_Sniffer::constructExporter(PD_Document * pDocument,
187 											   IE_Exp ** ppie)
188 {
189 	IE_Exp_RTF * p = new IE_Exp_RTF(pDocument);
190 	*ppie = p;
191 	return UT_OK;
192 }
193 
getDlgLabels(const char ** pszDesc,const char ** pszSuffixList,IEFileType * ft)194 bool IE_Exp_MsWord_Hack_Sniffer::getDlgLabels(const char ** pszDesc,
195 									  const char ** pszSuffixList,
196 									  IEFileType * ft)
197 {
198 	*pszDesc = "Microsoft Word (.doc)";
199 	*pszSuffixList = "*.doc";
200 	*ft = getFileType();
201 	return true;
202 }
203 
204 /*****************************************************************/
205 /*****************************************************************/
_writeDocument(void)206 UT_Error IE_Exp_RTF::_writeDocument(void)
207 {
208 	return _writeDocumentLocal(false);
209 }
210 
_writeDocumentLocal(bool bSkipHeader)211 UT_Error IE_Exp_RTF::_writeDocumentLocal(bool bSkipHeader)
212 {
213 	// The overall syntax for an RTF file is:
214 	//
215 	// <file> := '{' <header> <document> '}'
216 	//
217 	// We are responsible for everything except for <document>;
218 	// We let _ListenerWriteDoc() deal with that.
219 	//
220 	// create and install a listener to sniff over the
221 	// document and find things that we need to write
222 	// into the rtf header.  this includes the font table
223 	// and the color table.
224 
225 	_addColor("000000");				// load black as color 0.
226 	_addColor("ffffff");                // load white as color 1.
227 
228 	s_RTF_ListenerGetProps * listenerGetProps = new s_RTF_ListenerGetProps(getDoc(),this);
229 	if (!listenerGetProps)
230 		return UT_IE_NOMEMORY;
231 	if (getDocRange() && !bSkipHeader)
232 		getDoc()->tellListenerSubset(listenerGetProps,getDocRange());
233 	else
234 		getDoc()->tellListener(listenerGetProps);
235 
236 	// if the bit to be pasted contains a new block anywhere within it,
237 	// then we also want the block props for the first block in the
238 	// clipboard.  mark this info down here, use it in the listener.
239 	bool hasBlock = listenerGetProps->hasBlock();
240 
241 	DELETEP(listenerGetProps);
242 
243 	// Important: This must come before the header is written so
244         // every font used in a style is properly entered in the font table.
245 	_selectStyles();
246 
247 	// write rtf header
248 	if(!bSkipHeader)
249 	{
250 		if (!_write_rtf_header())
251 			return UT_IE_COULDNOTWRITE;
252 	}
253 	// create and install a listener to receive the document
254 	// and write its content in rtf.
255 
256 	m_pListenerWriteDoc = new s_RTF_ListenerWriteDoc(getDoc(),this, (getDocRange()!=NULL), hasBlock);
257 	if (!m_pListenerWriteDoc)
258 		return UT_IE_NOMEMORY;
259     PL_ListenerCoupleCloser* pCloser = new PL_ListenerCoupleCloser();
260 	if (getDocRange())
261 		getDoc()->tellListenerSubset(m_pListenerWriteDoc,getDocRange(),pCloser);
262 	else
263 		getDoc()->tellListener(m_pListenerWriteDoc);
264     DELETEP(pCloser);
265 	DELETEP(m_pListenerWriteDoc);
266 
267 	// write any rtf trailer matter
268 
269 	if(!bSkipHeader)
270 	{
271 			if (!_write_rtf_trailer())
272 				return UT_IE_COULDNOTWRITE;
273 	}
274 	return ((m_error) ? UT_IE_COULDNOTWRITE : UT_OK);
275 }
276 
277 
278 
279 /*!
280  * This method search for the requested header/footer section within
281  * PD_DOCument and writes into the stream at the current write point.
282  \param pszHdrFtr constchar * string describing the type of header/footer to
283                                 export.
284  \param pszHdrFtrID const char * identification string for the header/footer
285  */
exportHdrFtr(const char * pszHdrFtr,const char * pszHdrFtrID,const char * pszKeyWord)286 void IE_Exp_RTF::exportHdrFtr(const char * pszHdrFtr , const char * pszHdrFtrID, const char * pszKeyWord)
287 {
288 
289 // First find the header/footer section and id in the document.
290 	m_pListenerWriteDoc->_closeSpan();
291 #if 0 //#TF
292 	m_pListenerWriteDoc->_closeBlock();
293 	m_pListenerWriteDoc->_closeSpan();
294 	m_pListenerWriteDoc->_closeSection();
295 #endif
296 	m_pListenerWriteDoc->_setTabEaten(false);
297 
298 	pf_Frag_Strux* hdrSDH = getDoc()->findHdrFtrStrux((const gchar *) pszHdrFtr,(const gchar * ) pszHdrFtrID);
299 
300 	if(hdrSDH == NULL)
301 	{
302 	  UT_ASSERT_NOT_REACHED();
303 		return;
304 	}
305 	PT_DocPosition posStart = getDoc()->getStruxPosition(hdrSDH);
306 	PT_DocPosition posEnd = 0;
307 	pf_Frag_Strux* nextSDH = NULL;
308 	bool found = getDoc()->getNextStruxOfType(hdrSDH,PTX_SectionHdrFtr ,&nextSDH);
309 
310 	if(!found || (nextSDH == NULL ))
311 	{
312 		getDoc()->getBounds(true, posEnd);
313 	}
314 	else
315 	{
316 		posEnd =  getDoc()->getStruxPosition(nextSDH);
317 	}
318 	posStart++;
319 	PD_DocumentRange * pExportHdrFtr = new PD_DocumentRange(getDoc(),posStart,posEnd);
320 //
321 // Got everything. Now write out an openning brace and HdrFtr type.
322 //
323 	if(m_pListenerWriteDoc->m_bStartedList)
324 	{
325 		_rtf_close_brace();
326 	}
327 	_rtf_nl();
328 	_rtf_open_brace();
329 	_rtf_keyword(pszKeyWord);
330 	_rtf_keyword("pard");
331 	_rtf_keyword("plain");
332 	m_pListenerWriteDoc->m_bBlankLine = true;
333 	m_pListenerWriteDoc->m_bStartedList = false;
334 	UT_DEBUGMSG(("SEVIOR: Doing header \n"));
335 //
336 // Now pump out the contents of the HdrFtr
337 //
338 	getDoc()->tellListenerSubset(static_cast<PL_Listener *>(m_pListenerWriteDoc),pExportHdrFtr);
339 #if 0 //#TF
340 	_rtf_keyword("par");
341 #endif
342 	delete pExportHdrFtr;
343 	_rtf_close_brace();
344 }
345 
346 
347 /*****************************************************************/
348 /*****************************************************************/
349 #if 0
350 
351 void s_RTF_Listener::_handleDataItems(void)
352 {
353 	bool bWroteOpenDataSection = false;
354 
355 	const char * szName;
356 	const UT_ByteBuf * pByteBuf;
357 
358 	UT_ByteBuf bb64(1024);
359 
360 	for (UT_uint32 k=0; (m_pDocument->enumDataItems(k,NULL,&szName,&pByteBuf,NULL)); k++)
361 	{
362 		if (!bWroteOpenDataSection)
363 		{
364 			m_pie->write("<data>\n");
365 			bWroteOpenDataSection = true;
366 		}
367 
368 		if (UT_Base64Encode(&bb64, pByteBuf))
369 		{
370 			m_pie->write("<d name=\"");
371 			m_pie->write(szName);
372 			m_pie->write("\">\n");
373 
374 			// break up the Base64 blob as a series lines
375 			// like MIME does.
376 
377 			UT_uint32 jLimit = bb64.getLength();
378 			UT_uint32 jSize;
379 			UT_uint32 j;
380 			for (j=0; j<jLimit; j+=72)
381 			{
382 				jSize = UT_MIN(72,(jLimit-j));
383 				m_pie->write((const char *)bb64.getPointer(j),jSize);
384 				m_pie->write("\n");
385 			}
386 			m_pie->write("</d>\n");
387 		}
388 	}
389 
390 	if (bWroteOpenDataSection)
391 		m_pie->write("</data>\n");
392 
393 	return;
394 }
395 #endif
396 /*****************************************************************/
397 /*****************************************************************/
398 
_findColor(const char * szColor) const399 UT_sint32 IE_Exp_RTF::_findColor(const char * szColor) const
400 {
401 	if (!szColor || !*szColor)
402 		return 0;						// black
403 
404 	UT_uint32 k;
405 	UT_uint32 kLimit = m_vecColors.getItemCount();
406 
407 	for (k=0; k<kLimit; k++)
408 	{
409 		const char * sz = (const char *)m_vecColors.getNthItem(k);
410 		if (g_ascii_strcasecmp(sz,szColor) == 0)
411 			return k;
412 	}
413 
414 	return -1;
415 }
416 
_addColor(const char * szColor)417 void IE_Exp_RTF::_addColor(const char * szColor)
418 {
419 	UT_return_if_fail(szColor && *szColor && (_findColor(szColor)==-1));
420 
421 	char * sz = g_strdup(szColor);
422 	if (sz)
423 		m_vecColors.addItem(sz);
424 	return;
425 }
426 
_findOrAddColor(const char * szColor)427 UT_sint32 IE_Exp_RTF::_findOrAddColor(const char * szColor)
428 {
429 	UT_sint32 ndx = _findColor (szColor);
430 
431 	if (ndx != -1)
432 		return ndx;
433 
434 	_addColor (szColor);
435 	return _findColor (szColor);
436 }
437 
438 /*****************************************************************/
439 /*****************************************************************/
440 
_rtf_open_brace(void)441 void IE_Exp_RTF::_rtf_open_brace(void)
442 {
443 	m_braceLevel++;
444 	write("{");
445 	m_bLastWasKeyword = false;
446 }
447 
_rtf_close_brace(void)448 void IE_Exp_RTF::_rtf_close_brace(void)
449 {
450 	m_braceLevel--;
451 	write("}");
452 	m_bLastWasKeyword = false;
453 
454 	UT_return_if_fail(m_braceLevel >= 0);
455 }
456 
_rtf_keyword(const char * szKey)457 void IE_Exp_RTF::_rtf_keyword(const char * szKey)
458 {
459 	write("\\");
460 	write(szKey);
461 	m_bLastWasKeyword = true;
462 }
463 
464 /* output a non-ascii char in the RTF stream. */
_rtf_nonascii_hex2(UT_sint32 d)465 void IE_Exp_RTF::_rtf_nonascii_hex2 (UT_sint32 d)
466 {
467 	write("\\'");
468 	write(UT_String_sprintf("%02x",d));
469 	m_bLastWasKeyword = false;
470 }
471 
472 /* write a non-ascii char into a string class pointer */
_rtf_nonascii_hex2(UT_sint32 d,UT_String & pStr)473 void IE_Exp_RTF::_rtf_nonascii_hex2 (UT_sint32 d, UT_String & pStr)
474 {
475 	pStr = "\\'";
476 	pStr += UT_String_sprintf("%02x",d);
477 }
478 
479 
_rtf_keyword(const char * szKey,UT_sint32 d)480 void IE_Exp_RTF::_rtf_keyword(const char * szKey, UT_sint32 d)
481 {
482 	write("\\");
483 	write(szKey);
484 	write(UT_String_sprintf("%d",d));
485 	m_bLastWasKeyword = true;
486 }
487 
488 
_rtf_keyword_space(const char * szKey,UT_sint32 d)489 void IE_Exp_RTF::_rtf_keyword_space(const char * szKey, UT_sint32 d)
490 {
491 	write("\\");
492 	write(szKey);
493 	write(UT_String_sprintf(" %d",d));
494 	m_bLastWasKeyword = true;
495 }
496 
_rtf_keyword(const char * szKey,const char * val)497 void IE_Exp_RTF::_rtf_keyword(const char * szKey, const char * val)
498 {
499   write("\\");
500   write(szKey);
501   write(val);
502   m_bLastWasKeyword = true;
503 }
504 
_rtf_keyword_hex2(const char * szKey,UT_sint32 d)505 void IE_Exp_RTF::_rtf_keyword_hex2(const char * szKey, UT_sint32 d)
506 {
507 	write("\\");
508 	write(szKey);
509 	write(UT_String_sprintf("%02x",d));
510 	m_bLastWasKeyword = true;
511 }
512 
_rtf_keyword_ifnotdefault(const char * szKey,const char * szValue,UT_sint32 defaultValue)513 void IE_Exp_RTF::_rtf_keyword_ifnotdefault(const char * szKey, const char * szValue, UT_sint32 defaultValue)
514 {
515 	if (!szValue || !*szValue)
516 		return;
517 
518 	UT_sint32 d = atol(szValue);
519 	if (d == defaultValue)
520 		return;
521 
522 	write("\\");
523 	write(szKey);
524 	write(UT_String_sprintf("%d",d));
525 	m_bLastWasKeyword = true;
526 }
527 
_rtf_keyword_ifnotdefault_twips(const char * szKey,const char * szValue,UT_sint32 defaultValue)528 void IE_Exp_RTF::_rtf_keyword_ifnotdefault_twips(const char * szKey, const char * szValue, UT_sint32 defaultValue)
529 {
530 	if (!szValue || !*szValue)
531 		return;
532 
533 	// convert dimensioned value into twips (twentieths of a point) (aka 720 twips/inch)
534 	double dbl = UT_convertToPoints(szValue);
535 	UT_sint32 d = (UT_sint32)(dbl * 20.0);
536 
537 	if (d == defaultValue)
538 		return;
539 
540 	write("\\");
541 	write(szKey);
542 	write(UT_String_sprintf("%d",d));
543 	m_bLastWasKeyword = true;
544 }
545 
_rtf_semi(void)546 void IE_Exp_RTF::_rtf_semi(void)
547 {
548 	write(";");
549 	m_bLastWasKeyword = false;
550 }
551 
_rtf_fontname(const char * szFontName)552 void IE_Exp_RTF::_rtf_fontname(const char * szFontName)
553 {
554 	/*  map "Helvetic" to "Helvetica", since on Windows
555 	    font "Helvetic" contains only Latin1 chars, while
556 	    "Helvetica" contains all needed chars.
557 	    This is safe to do for both attic and non-attic format
558 	    because we handle "helvetica" in a special way on import.
559 	*/
560 	if (g_ascii_strcasecmp(szFontName,"helvetic")==0)
561 		write("Helvetica");
562 	else
563 	{
564 		_rtf_pcdata(szFontName, true);
565 	}
566 
567 	_rtf_semi();
568 	return;
569 }
570 
571 
_rtf_chardata(const std::string & buf)572 void IE_Exp_RTF::_rtf_chardata(const std::string& buf)
573 {
574     _rtf_chardata( buf.c_str(), buf.length() );
575 }
576 
577 /*
578  * I'm increasingly suspicious of this function. It seems to be
579  * being used to output PCDATA (like stylesheet names). For
580  * such a use it is certainly broken. Does it have any genuine use?
581  *
582  * It's broken because it converts the input data to UCS-4 and
583  * then writes out any characters between 0x80 and 0xff as hex escaped
584  * sequences. However, it takes no account of the rtf files encoding
585  * set by the \ansicpg command in the header
586  * - R.Kay (Aug '05)
587  *
588  */
_rtf_chardata(const char * pbuf,UT_uint32 buflen)589 void IE_Exp_RTF::_rtf_chardata(const char * pbuf, UT_uint32 buflen)
590 {
591 	const char * current = pbuf;
592 	UT_uint32 count = 0;
593 
594 	xxx_UT_DEBUGMSG(("Buffer length = %d \n",buflen));
595 	if(buflen < 400)
596 	{
597 		xxx_UT_DEBUGMSG(("data = %s\n", pbuf));
598 	}
599 	if (m_bLastWasKeyword)
600 	{
601 		write(" ");
602 		m_bLastWasKeyword = false;
603 	}
604 
605 	if(0 == buflen) {
606 		return;
607 	}
608 
609 	UT_return_if_fail (UT_iconv_isValid(m_conv));
610 	while (count < buflen) {
611 		if (*current & 0x80) {  // check for non-ASCII value
612 			UT_UCS4Char wc;
613 			size_t insz, sz;
614 			char * dest = (char*)(&wc);
615 			insz = buflen - count;
616 			sz = sizeof(wc);
617 			UT_iconv(m_conv, &current, &insz, &dest, &sz);
618 			if (wc > 0x00ff) {
619 //				UT_ASSERT_HARMLESS(UT_NOT_IMPLEMENTED);
620 			}
621 			else {
622 				_rtf_nonascii_hex2(wc);
623 			}
624 			if(buflen - insz > 0)
625 			{
626 				count += buflen - insz;
627 			}
628 			else
629 			{
630 				count++;
631 			}
632 		}
633 		else {
634 			write (current, 1);
635 			current++;
636 			count++;
637 		}
638 	}
639 }
640 
641 /*
642  * This writes out UTF8 data by escaping non-ASCII chars.
643  * using \uXXXX.
644  */
_rtf_pcdata(UT_UTF8String & sPCData,bool bSupplyUC,UT_uint32 iAltChars)645 void IE_Exp_RTF::_rtf_pcdata(UT_UTF8String &sPCData, bool bSupplyUC, UT_uint32 iAltChars)
646 {
647 	bool bEscaped;
648 
649 	UT_UTF8String sEscapedData;
650 	UT_UCS4String sUCS4PCData = sPCData.ucs4_str();
651 
652 	// Escape the string.
653 	bEscaped = s_escapeString(sEscapedData, sUCS4PCData, iAltChars);
654 	// If escaping was necessary and we've been asked to do it, supply
655 	// the appropriate \uc command.
656 	if (bEscaped && bSupplyUC)
657 		_rtf_keyword("uc", iAltChars);
658 	if (m_bLastWasKeyword) {
659 		write(" ");
660 		m_bLastWasKeyword = false;
661 	}
662 	// Write the string to the file.
663 	write(sEscapedData.utf8_str());
664 }
665 
666 /*
667  * Access functions for above
668  */
_rtf_pcdata(const std::string & szPCData,bool bSupplyUC,UT_uint32 iAltChars)669 void IE_Exp_RTF::_rtf_pcdata(const std::string & szPCData, bool bSupplyUC, UT_uint32 iAltChars)
670 {
671 	UT_UTF8String str(szPCData);
672 	_rtf_pcdata(str, bSupplyUC, iAltChars);
673 }
674 
675 /*
676  * Access functions for above
677  */
_rtf_pcdata(const char * szPCData,bool bSupplyUC,UT_uint32 iAltChars)678 void IE_Exp_RTF::_rtf_pcdata(const char * szPCData, bool bSupplyUC, UT_uint32 iAltChars)
679 {
680 	UT_UTF8String str(szPCData);
681 	_rtf_pcdata(str, bSupplyUC, iAltChars);
682 }
683 
_rtf_nl(void)684 void IE_Exp_RTF::_rtf_nl(void)
685 {
686 	write("\n");
687 }
688 
_write_rtf_header(void)689 bool IE_Exp_RTF::_write_rtf_header(void)
690 {
691 	UT_uint32 k,kLimit;
692 
693 	UT_uint32 langcode = XAP_EncodingManager::get_instance()->getWinLanguageCode();
694 	// write <rtf-header>
695 	// return false on error
696 	UT_DEBUGMSG(("Belcon:in  IE_Exp_RTF::_write_rtf_header,langcode=%d\n",langcode));
697 
698 	_rtf_open_brace();
699 	_rtf_keyword("rtf",1);				// major version number of spec version 1.5
700 
701 	_rtf_keyword("ansi");
702 	bool wrote_cpg = 0;
703 	if (langcode)
704 	{
705 		const char* cpgname = wvLIDToCodePageConverter(langcode);
706 		xxx_UT_DEBUGMSG(("Belcon,after wvLIDToCodePageConverter(%d),cpgname=%s\n",langcode,cpgname));
707 		if (g_ascii_strncasecmp(cpgname,"cp",2)==0 && UT_UCS4_isdigit(cpgname[2]))
708 		{
709 			int cpg;
710 			if (sscanf(cpgname+2,"%d",&cpg)==1)
711 			{
712 				_rtf_keyword("ansicpg",cpg);
713 				wrote_cpg = 1;
714 			}
715 		}
716 		// NOTE:we will get "GB2312","BIG5" or something else after we call
717 		// NOTE:wvLIDToCodePageConverter,we can deal with them quite fine,
718 		// NOTE:but we need convert them to CPxx.:-(
719 		else
720 		{
721 			const char* codepage=XAP_EncodingManager::get_instance()->CodepageFromCharset(cpgname);
722 			if(g_ascii_strncasecmp(codepage,"cp",2)==0 && UT_UCS4_isdigit(codepage[2]))
723 			{
724 				int cpg;
725 				if (sscanf(codepage+2,"%d",&cpg)==1)
726 				{
727 					_rtf_keyword("ansicpg",cpg);
728 					wrote_cpg = 1;
729 				}
730 			}
731 			xxx_UT_DEBUGMSG(("Belcon:after XAP_EncodingManager::get_instance()->CodepageFromCharset(%s),codepage=%s\n",cpgname,codepage));
732 		}
733 	};
734 	if (!wrote_cpg)
735 	    _rtf_keyword("ansicpg",1252);		// TODO what CodePage do we want here ??
736 
737 	_rtf_keyword("deff",0);				// default font is index 0 aka black
738 	if (m_atticFormat)
739 	{
740 		/* I'm not sure whether this makes any sense - VH */
741 		if (langcode)
742 			_rtf_keyword("deflang",langcode);
743 	}
744 	// Default uc value for unicode characters
745 	_rtf_keyword("uc",1);
746 
747 	// write the "font table"....
748 
749 	kLimit = m_vecFonts.getItemCount();
750 	// don't write a font table group if we don't have any font to write.
751 	// see bug 1383
752 	if (kLimit > 0)
753 	{
754 		_rtf_nl();
755 		_rtf_open_brace();
756 		_rtf_keyword("fonttbl");
757 		/*UT_uint32 charsetcode =*/ XAP_EncodingManager::get_instance()->getWinCharsetCode();
758 		for (k=0; k<kLimit; k++)
759 		{
760 			const _rtf_font_info * pk = (const _rtf_font_info *)m_vecFonts.getNthItem(k);
761 			_rtf_nl();
762 			_rtf_open_brace();
763 			_rtf_keyword("f", k);								// font index number
764 			_rtf_keyword(pk->getFontFamily());								// {\fnil,\froman,\fswiss,...}
765 			_rtf_keyword("fcharset",pk->getFontCharset());
766 			_rtf_keyword("fprq",pk->getFontPitch());							// {0==default,1==fixed,2==variable}
767 			_rtf_keyword((pk->isTrueType()) ? "fttruetype" : "ftnil");	// {\fttruetype,\ftnil}
768 
769 			// we do nothing with or use default values for
770 			// \falt \panose \fname \fbias \ftnil \fttruetype \fontfile
771 
772 			// after we write the various generic font properties, we write
773 			// the actual font name and a semicolon -- i couldn't see this
774 			// described in the specification, but it was in other RTF files
775 			// that i saw and really seems to help Word and WordPad....
776 			_rtf_fontname(pk->getFontName());
777 
778 			_rtf_close_brace();
779 		}
780 		_rtf_close_brace();
781 	}
782 
783 	// TODO write the "file table" if necessary...
784 
785 	kLimit = m_vecColors.getItemCount();
786 	if (kLimit > 0)
787 	{
788 		_rtf_nl();
789 		_rtf_open_brace();
790 		_rtf_keyword("colortbl");
791 		for (k=0; k<kLimit; k++)
792 		{
793 			const char * szColor = (const char *)m_vecColors.getNthItem(k);
794 			UT_RGBColor localColor;
795 			UT_parseColor(szColor,localColor);
796 			_rtf_nl();
797 			_rtf_keyword("red",  localColor.m_red);
798 			_rtf_keyword("green",localColor.m_grn);
799 			_rtf_keyword("blue", localColor.m_blu);
800 			_rtf_semi();
801 		}
802 		_rtf_close_brace();
803 	}
804 
805 	_write_stylesheets();
806 	_write_listtable();
807 
808 	// TODO write the "rev table"...
809 
810 	// write default character properties at global scope...
811 	_rtf_nl();
812 	_rtf_keyword("kerning",0);			// turn off kerning
813 	_rtf_keyword("cf",0);				// set color 0 -- black
814 	_rtf_keyword("ftnbj");				// set footnotes to bottom of page
815 	_rtf_keyword("fet",2);				// Allow both footnotes and endnotes
816 	_rtf_keyword("ftnstart",1);			// First footnote is one - later use
817 	                                    // document properties
818 	const gchar * pszFootnoteType = NULL;
819 	const PP_AttrProp* pDocAP = getDoc()->getAttrProp();
820 	UT_return_val_if_fail (pDocAP, false);
821 	pDocAP->getProperty("document-footnote-type", (const gchar *&)pszFootnoteType);
822 	if (pszFootnoteType == NULL)
823 	{
824 		_rtf_keyword("ftnnar");			// Numeric Footnotes
825 	}
826 	else if(pszFootnoteType[0] == 0)
827 	{
828 		_rtf_keyword("ftnnar");			// Numeric Footnotes
829 	}
830 	else if(strcmp(pszFootnoteType,"numeric") == 0)
831 	{
832 		_rtf_keyword("ftnnar");			// Numeric Footnotes
833 	}
834 	else if(strcmp(pszFootnoteType,"numeric-square-brackets") == 0)
835 	{
836 		_rtf_keyword("ftnnar");			// Numeric Footnotes
837 	}
838 	else if(strcmp(pszFootnoteType,"numeric-paren") == 0)
839 	{
840 		_rtf_keyword("ftnnar");			// Numeric Footnotes
841 	}
842 	else if(strcmp(pszFootnoteType,"numeric-open-paren") == 0)
843 	{
844 		_rtf_keyword("ftnnar");			// Numeric Footnotes
845 	}
846 	else if(strcmp(pszFootnoteType,"upper") == 0)
847 	{
848 		_rtf_keyword("ftnnauc");			// Alphabetic Upper case
849 	}
850 	else if(strcmp(pszFootnoteType,"upper-paren") == 0)
851 	{
852 		_rtf_keyword("ftnnauc");			// Alphabetic Upper case
853 	}
854 	else if(strcmp(pszFootnoteType,"upper-paren-open") == 0)
855 	{
856 		_rtf_keyword("ftnnauc");			// Alphabetic Upper case
857 	}
858 	else if(strcmp(pszFootnoteType,"lower") == 0)
859 	{
860 		_rtf_keyword("ftnnalc");			// Alphabetic Lower case
861 	}
862 	else if(strcmp(pszFootnoteType,"lower-paren") == 0)
863 	{
864 		_rtf_keyword("ftnnalc");			// Alphabetic Lower case
865 	}
866 	else if(strcmp(pszFootnoteType,"lower-paren-open") == 0)
867 	{
868 		_rtf_keyword("ftnnalc");			// Alphabetic Lower case
869 	}
870 	else if(strcmp(pszFootnoteType,"lower-roman") == 0)
871 	{
872 		_rtf_keyword("ftnnrlc");			// Roman Lower case
873 	}
874 	else if(strcmp(pszFootnoteType,"lower-roman-paren") == 0)
875 	{
876 		_rtf_keyword("ftnnrlc");			// Roman Lower case
877 	}
878 	else if(strcmp(pszFootnoteType,"upper-roman") == 0)
879 	{
880 		_rtf_keyword("ftnnruc");			// Roman Upper case
881 	}
882 	else if(strcmp(pszFootnoteType,"upper-roman-paren") == 0)
883 	{
884 		_rtf_keyword("ftnnruc");			// Roman Upper case
885 	}
886 	else
887 	{
888 		_rtf_keyword("ftnnar");			// Numeric Footnotes
889 	}
890 
891 	const gchar * pszEndnoteType = NULL;
892 	pDocAP->getProperty("document-endnote-type", (const gchar *&)pszEndnoteType);
893 	if (pszEndnoteType == NULL)
894 	{
895 		_rtf_keyword("aftnnar");			// Numeric Endnotes
896 	}
897 	else if(pszEndnoteType[0] == 0)
898 	{
899 		_rtf_keyword("aftnnar");			// Numeric Endnotes
900 	}
901 	else if(strcmp(pszEndnoteType,"numeric") == 0)
902 	{
903 		_rtf_keyword("aftnnar");			// Numeric Endnotes
904 	}
905 	else if(strcmp(pszEndnoteType,"numeric-square-brackets") == 0)
906 	{
907 		_rtf_keyword("aftnnar");			// Numeric Endnotes
908 	}
909 	else if(strcmp(pszEndnoteType,"numeric-paren") == 0)
910 	{
911 		_rtf_keyword("aftnnar");			// Numeric Endnotes
912 	}
913 	else if(strcmp(pszEndnoteType,"numeric-open-paren") == 0)
914 	{
915 		_rtf_keyword("aftnnar");			// Numeric Endnotes
916 	}
917 	else if(strcmp(pszEndnoteType,"upper") == 0)
918 	{
919 		_rtf_keyword("aftnnauc");			// Alphabetic Upper Endnotes
920 	}
921 	else if(strcmp(pszEndnoteType,"upper-paren") == 0)
922 	{
923 		_rtf_keyword("aftnnauc");			// Alphabetic Upper Endnotes
924 	}
925 	else if(strcmp(pszEndnoteType,"upper-paren-open") == 0)
926 	{
927 		_rtf_keyword("aftnnauc");			// Alphabetic Upper Endnotes
928 	}
929 	else if(strcmp(pszEndnoteType,"lower") == 0)
930 	{
931 		_rtf_keyword("aftnnalc");			// Alphabetic Lower Endnotes
932 	}
933 	else if(strcmp(pszEndnoteType,"lower-paren") == 0)
934 	{
935 		_rtf_keyword("aftnnalc");			// Alphabetic Lower Endnotes
936 	}
937 	else if(strcmp(pszEndnoteType,"lower-paren-open") == 0)
938 	{
939 		_rtf_keyword("aftnnalc");			// Alphabetic Lower Endnotes
940 	}
941 	else if(strcmp(pszEndnoteType,"lower-roman") == 0)
942 	{
943 		_rtf_keyword("aftnnrlc");			// Roman Lower Endnotes
944 	}
945 	else if(strcmp(pszEndnoteType,"lower-roman-paren") == 0)
946 	{
947 		_rtf_keyword("aftnnrlc");			// Roman Lower Endnotes
948 	}
949 	else if(strcmp(pszEndnoteType,"upper-roman") == 0)
950 	{
951 		_rtf_keyword("aftnnruc");			// Roman Upper Endnotes
952 	}
953 	else if(strcmp(pszEndnoteType,"upper-roman-paren") == 0)
954 	{
955 		_rtf_keyword("aftnnruc");			// Roman Upper Endnotes
956 	}
957 	else
958 	{
959 		_rtf_keyword("aftnnar");			// Numeric Endnotes
960 	}
961 
962 	const gchar * pszTmp = NULL;
963 	pDocAP->getProperty("document-footnote-initial", (const gchar *&)pszTmp);
964 	if(pszTmp && pszTmp[0])
965 	{
966 		_rtf_keyword("ftnstart",atoi(pszTmp));			// First footnote
967 	}
968 	else
969 	{
970 		_rtf_keyword("ftnstart",1);			// First footnote
971 	}
972 
973 	pDocAP->getProperty("document-footnote-restart-section", (const gchar *&)pszTmp);
974 	if(pszTmp && pszTmp[0])
975 	{
976 		if(strcmp(pszTmp,"1") == 0)
977 		{
978 			_rtf_keyword("ftnrestart");			// footnote restarts each section
979 		}
980 	}
981 
982 	pDocAP->getProperty("document-footnote-restart-page", (const gchar *&)pszTmp);
983 	if(pszTmp && pszTmp[0])
984 	{
985 		if(strcmp(pszTmp,"1") == 0)
986 		{
987 			_rtf_keyword("ftnrstpg");			// footnote restarts each page
988 		}
989 	}
990 
991 	pDocAP->getProperty("document-endnote-initial", (const gchar *&)pszTmp);
992 	if(pszTmp && pszTmp[0])
993 	{
994 		_rtf_keyword("aftnstart", atoi(pszTmp)); // initial endnote value
995 	}
996 	pDocAP->getProperty("document-endnote-restart-section", (const gchar *&)pszTmp);
997 	if(pszTmp && pszTmp[0])
998 	{
999 		if(strcmp(pszTmp,"1") == 0)
1000 		{
1001 			_rtf_keyword("aftnrestart"); // restart endnotes each section
1002 		}
1003 	}
1004 
1005 	pDocAP->getProperty("document-endnote-place-endsection", (const gchar *&)pszTmp);
1006 	if(pszTmp && pszTmp[0])
1007 	{
1008 		if(strcmp(pszTmp,"1") == 0)
1009 		{
1010 			_rtf_keyword("aendnotes"); // endnotes at end of section
1011 		}
1012 	}
1013 
1014 	pDocAP->getProperty("document-endnote-place-enddoc", (const gchar *&)pszTmp);
1015 	if(pszTmp && pszTmp[0])
1016 	{
1017 		if(strcmp(pszTmp,"1") == 0)
1018 		{
1019 			_rtf_keyword("aenddoc"); // endnotes at end of document
1020 		}
1021 	}
1022 //
1023 // Write out the facingp and titlepg keywords so that we can export our fancy
1024 // header-footers to RTF
1025 //
1026 	pf_Frag_Strux* sdh = NULL;
1027 	getDoc()->getStruxOfTypeFromPosition(2,PTX_Section,&sdh);
1028 	if(sdh != NULL)
1029 	{
1030 	        PT_AttrPropIndex api = getDoc()->getAPIFromSDH(sdh);
1031 		const PP_AttrProp * pSectionAP = NULL;
1032 		getDoc()->getAttrProp(api,&pSectionAP);
1033 		const char * pszAtt = NULL;
1034 		if(pSectionAP != NULL)
1035 		{
1036 		     if(pSectionAP->getAttribute("header-even",pszAtt))
1037 		     {
1038 		          _rtf_keyword("facingp"); // Allow odd-even headers/footers
1039 		     }
1040 		     else if(pSectionAP->getAttribute("footer-even",pszAtt))
1041 		     {
1042 		          _rtf_keyword("facingp"); // Allow odd-even headers/footers
1043 		     }
1044 		     if(pSectionAP->getAttribute("header-first",pszAtt))
1045 		     {
1046 			 _rtf_keyword("titlepg"); // Allow first page headers/footers
1047 		     }
1048 		     else if(pSectionAP->getAttribute("footer-first",pszAtt))
1049 		     {
1050 			 _rtf_keyword("titlepg"); // Allow first page headers/footers
1051 		     }
1052 		}
1053 
1054 	}
1055 	// revisions stuff
1056 	const UT_GenericVector<AD_Revision*> & Revs = getDoc()->getRevisions();
1057 
1058 	if(Revs.getItemCount())
1059 	{
1060 		_rtf_open_brace();
1061 		_rtf_keyword("*");
1062 		_rtf_keyword("revtbl");
1063 
1064 		UT_UTF8String s;
1065 		UT_UCS4String s4;
1066 
1067 		// MS Word assumes that the table always starts with author Unknown and it will
1068 		// not bother to lookup the 0-th author, so we have to insert that dummy entry
1069 		_rtf_open_brace();
1070 		_rtf_chardata("Unknown", 7);
1071 		_rtf_semi();
1072 		_rtf_close_brace();
1073 
1074 		for(UT_sint32 i = 0; i < Revs.getItemCount(); ++i)
1075 		{
1076 			AD_Revision* pRev = Revs.getNthItem(i);
1077 			UT_continue_if_fail(pRev);
1078 
1079 			s4 = pRev->getDescription();
1080 
1081 			// construct author name from our numerical id and comment
1082 			// (the id guarantees us uniqueness)
1083 			UT_UTF8String_sprintf(s, "rev %d (%s)",pRev->getId(),s4.utf8_str());
1084 			_rtf_open_brace();
1085 			_rtf_chardata(s.utf8_str(),s.byteLength());
1086 			_rtf_semi();
1087 			_rtf_close_brace();
1088 		}
1089 
1090 		_rtf_close_brace();
1091 	}
1092 
1093 	// underline newly inserted text
1094 	UT_uint32 iRevMode = 0;
1095 	if(getDoc()->isShowRevisions())
1096 	{
1097 		iRevMode = 3;
1098 	}
1099 
1100 	_rtf_keyword("revprop", iRevMode);
1101 
1102 	if(getDoc()->isMarkRevisions())
1103 	{
1104 		_rtf_keyword("revisions");
1105 	}
1106 
1107     //
1108     // export the RDF too
1109     //
1110     {
1111         _rtf_open_brace();
1112 		_rtf_keyword("*");
1113 		_rtf_keyword("rdf");
1114         if( getDocRange() )
1115         {
1116             PD_DocumentRDFHandle rdf = getDoc()->getDocumentRDF();
1117             std::set< std::string > xmlids;
1118             rdf->addRelevantIDsForRange( xmlids, getDocRange() );
1119             UT_DEBUGMSG(("MIQ: RTF export creating restricted RDF model xmlids.sz:%lu\n",
1120 						 (unsigned long)xmlids.size()));
1121             PD_RDFModelHandle subm = rdf->createRestrictedModelForXMLIDs( xmlids );
1122             std::string rdfxml = toRDFXML( subm );
1123             _rtf_chardata( s_escapeXMLString(rdfxml) );
1124 
1125 
1126             // std::stringstream countss;
1127             // countss << subm->size();
1128             // _rtf_chardata( countss.str() );
1129 
1130             // PD_RDFModelIterator iter = subm->begin();
1131             // PD_RDFModelIterator    e = subm->end();
1132             // for( ; iter != e; ++iter )
1133             // {
1134             //     const PD_RDFStatement& st = *iter;
1135             //     _rtf_open_brace();
1136             //     _rtf_keyword("*");
1137             //     _rtf_keyword("triple");
1138             //     std::stringstream ss;
1139             //     st.getSubject().write( ss );
1140             //     st.getPredicate().write( ss );
1141             //     st.getObject().write( ss );
1142             //     _rtf_chardata( ss.str() );
1143             //     _rtf_close_brace();
1144             // }
1145 
1146         }
1147 		_rtf_close_brace();
1148     }
1149 
1150 	return (m_error == 0);
1151 }
1152 
1153 /*!
1154  * Write an rtf keyword if the given property isn't the default
1155  * value. Use this only with twips-valued properties.
1156  *
1157  * !param pStyle       A style.
1158  * !param szPropName   The property to check.
1159  * !param szRTFName    The RTF keyword to use if the property
1160  *                     doesn't have the default value.
1161  */
_write_prop_ifnotdefault(const PD_Style * pStyle,const gchar * szPropName,const char * szRTFName)1162 void IE_Exp_RTF::_write_prop_ifnotdefault(const PD_Style * pStyle,
1163 					  const gchar * szPropName,
1164 					  const char * szRTFName)
1165 {
1166 	const gchar * sz = NULL;
1167 	if (pStyle->getProperty((const gchar *)szPropName, sz)) {
1168 		_rtf_keyword_ifnotdefault_twips(szRTFName, sz, 0);
1169 	}
1170 }
1171 
1172 /*!
1173  * Write an RTF keyword if the given property is "yes".
1174  */
_write_prop_ifyes(const PD_Style * pStyle,const gchar * szPropName,const char * szRTFName)1175 void IE_Exp_RTF::_write_prop_ifyes(const PD_Style * pStyle,
1176 				   const gchar * szPropName,
1177 				   const char * szRTFName)
1178 {
1179     const gchar * sz = NULL;
1180     if (pStyle->getProperty((const gchar *)szPropName, sz) && strcmp(sz, "yes") == 0) {
1181 	    _rtf_keyword(szRTFName);
1182     }
1183 }
1184 
1185 /*
1186  * Used to hold tab information by _write_tabdef.
1187  */
1188 class ABI_EXPORT _t
1189 {
1190 public:
_t(const char * szTL,const char * szTT,const char * szTK,UT_sint32 tp)1191 	_t(const char * szTL, const char * szTT, const char * szTK, UT_sint32 tp)
1192 		{
1193 			m_szTabLeaderKeyword = szTL;
1194 			m_szTabTypeKeyword = szTT;
1195 			m_szTabKindKeyword = szTK;
1196 			m_iTabPosition = tp;
1197 		}
1198 	const char *    m_szTabLeaderKeyword;
1199 	const char *	m_szTabTypeKeyword;
1200 	const char *	m_szTabKindKeyword;
1201 	UT_sint32		m_iTabPosition;
1202 };
1203 
compare_tabs(const void * p1,const void * p2)1204 static int compare_tabs(const void* p1, const void* p2)
1205 {
1206 	_t ** ppTab1 = (_t **) p1;
1207 	_t ** ppTab2 = (_t **) p2;
1208 
1209 	if ((*ppTab1)->m_iTabPosition < (*ppTab2)->m_iTabPosition)
1210 		return -1;
1211 	if ((*ppTab1)->m_iTabPosition > (*ppTab2)->m_iTabPosition)
1212 		return 1;
1213 	return 0;
1214 }
1215 
1216 /*!
1217  * Write out the <tabdef> paragraph formatting.
1218  */
_write_tabdef(const char * szTabStops)1219 void IE_Exp_RTF::_write_tabdef(const char * szTabStops)
1220 {
1221 	if (szTabStops && *szTabStops)
1222 	{
1223 		// write tabstops for this paragraph
1224 		// TODO the following parser was copied from abi/src/text/fmt/xp/fl_BlockLayout.cpp
1225 		// TODO we should extract both of them and share the code.
1226 
1227 		UT_Vector vecTabs;
1228 
1229 		const char* pStart = szTabStops;
1230 		while (*pStart)
1231 		{
1232 			const char * szTT = "tx";	// TabType -- assume text tab (use "tb" for bar tab)
1233 			const char * szTK = NULL;	// TabKind -- assume left tab
1234 			const char * szTL = NULL;    // TabLeader
1235 			const char* pEnd = pStart;
1236 			while (*pEnd && (*pEnd != ','))
1237 				pEnd++;
1238 			const char* p1 = pStart;
1239 			while ((p1 < pEnd) && (*p1 != '/'))
1240 				p1++;
1241 			if ( (p1 == pEnd) || ((p1+1) == pEnd) )
1242 				;						// left-tab is default
1243 			else
1244 			{
1245 				switch (p1[1])
1246 				{
1247 				default:
1248 				case 'L': 	szTK = NULL; 	break;
1249 				case 'R':	szTK = "tqr";	break;
1250 				case 'C':	szTK = "tqc";	break;
1251 				case 'D':	szTK = "tqdec";	break;
1252 				case 'B':	szTT = "tb";    szTK= NULL;	break; // TabKind == bar tab
1253 				}
1254 				switch (p1[2])
1255 				{
1256 				default:
1257 				case '0': szTL = NULL;      break;
1258 				case '1': szTL = "tldot";   break;
1259 				case '2': szTL = "tlhyph";    break;
1260 				case '3': szTL = "tlul";    break;
1261 				case '4': szTL = "tleq";    break;
1262 				}
1263 			}
1264 
1265 			char pszPosition[32];
1266 			UT_uint32 iPosLen = p1 - pStart;
1267 			UT_return_if_fail(iPosLen < 32);
1268 			UT_uint32 k;
1269 			for (k=0; k<iPosLen; k++)
1270 				pszPosition[k] = pStart[k];
1271 			pszPosition[k] = 0;
1272 			// convert position into twips
1273 			double dbl = UT_convertToPoints(pszPosition);
1274 			UT_sint32 d = (UT_sint32)(dbl * 20.0);
1275 
1276 			_t * p_t = new _t(szTL,szTT,szTK,d);
1277 			vecTabs.addItem(p_t);
1278 
1279 			pStart = pEnd;
1280 			if (*pStart)
1281 			{
1282 				pStart++;	// skip past delimiter
1283 				while (*pStart == UCS_SPACE)
1284 					pStart++;
1285 			}
1286 		}
1287 
1288 		// write each tab in order:
1289 		// <tabdef> ::= ( <tab> | <bartab> )+
1290 		// <tab>    ::= <tabkind>? <tablead>? \tx
1291 		// <bartab> ::= <tablead>? \tb
1292 
1293 		vecTabs.qsort(compare_tabs);
1294 
1295 		UT_uint32 k;
1296 		UT_uint32 kLimit = vecTabs.getItemCount();
1297 		for (k=0; k<kLimit; k++)
1298 		{
1299 			_t * p_t = (_t *)vecTabs.getNthItem(k);
1300 			// write <tabkind>
1301 			if (p_t->m_szTabKindKeyword && *p_t->m_szTabKindKeyword)
1302 				_rtf_keyword(p_t->m_szTabKindKeyword);
1303 			if (p_t->m_szTabLeaderKeyword && *p_t->m_szTabLeaderKeyword)
1304 				_rtf_keyword(p_t->m_szTabLeaderKeyword);
1305 			_rtf_keyword(p_t->m_szTabTypeKeyword,p_t->m_iTabPosition);
1306 
1307 			delete p_t;
1308 		}
1309 	}
1310 }
1311 
1312 /*!
1313  * Get style
1314  */
_getStyleProp(s_RTF_AttrPropAdapter_Style * pADStyle,const s_RTF_AttrPropAdapter * apa,const char * szProp)1315 const gchar * IE_Exp_RTF::_getStyleProp(
1316 	s_RTF_AttrPropAdapter_Style * pADStyle,
1317 	const s_RTF_AttrPropAdapter * apa,
1318 	const char * szProp)
1319 {
1320 	const gchar *szVal = NULL;
1321 	if(pADStyle != NULL)
1322 	{
1323 		szVal = pADStyle->getProperty(szProp);
1324 		if(szVal == NULL)
1325 		{
1326 			szVal = apa->getProperty(szProp);
1327 		}
1328 		else
1329 		{
1330 			szVal = NULL;
1331 		}
1332 	}
1333 	else
1334 	{
1335 		szVal = apa->getProperty(szProp);
1336 	}
1337 	return szVal;
1338 }
1339 
1340 /*!
1341  * Write out paragraphs-specific fmt. This
1342  * does not print opening and closing braces.
1343  */
_write_parafmt(const PP_AttrProp * pSpanAP,const PP_AttrProp * pBlockAP,const PP_AttrProp * pSectionAP,bool & bStartedList,pf_Frag_Strux * sdh,UT_uint32 & iCurrID,bool & bIsListBlock,UT_sint32 iNestLevel)1344 void IE_Exp_RTF::_write_parafmt(const PP_AttrProp * pSpanAP, const PP_AttrProp * pBlockAP, const PP_AttrProp * pSectionAP,
1345 								bool & bStartedList, pf_Frag_Strux* sdh, UT_uint32 & iCurrID, bool &bIsListBlock,
1346 								UT_sint32 iNestLevel)
1347 {
1348 	const gchar * szTextAlign = PP_evalProperty("text-align",pSpanAP,pBlockAP,pSectionAP,getDoc(),true);
1349 	const gchar * szFirstLineIndent = PP_evalProperty("text-indent",pSpanAP,pBlockAP,pSectionAP,getDoc(),true);
1350 	const gchar * szLeftIndent = PP_evalProperty("margin-left",pSpanAP,pBlockAP,pSectionAP,getDoc(),true);
1351 	const gchar * szRightIndent = PP_evalProperty("margin-right",pSpanAP,pBlockAP,pSectionAP,getDoc(),true);
1352 	const gchar * szTopMargin = PP_evalProperty("margin-top",pSpanAP,pBlockAP,pSectionAP,getDoc(),true);
1353 	const gchar * szBottomMargin = PP_evalProperty("margin-bottom",pSpanAP,pBlockAP,pSectionAP,getDoc(),true);
1354 	const gchar * szLineHeight = PP_evalProperty("line-height",pSpanAP,pBlockAP,pSectionAP,getDoc(),true);
1355 	const gchar * szKeepTogether = PP_evalProperty("keep-together",pSpanAP,pBlockAP,pSectionAP,getDoc(),true);
1356 	const gchar * szKeepWithNext = PP_evalProperty("keep-with-next",pSpanAP,pBlockAP,pSectionAP,getDoc(),true);
1357 	const gchar * szTabStops = PP_evalProperty("tabstops",pSpanAP,pBlockAP,pSectionAP,getDoc(),true);
1358 
1359 	// Borders
1360 
1361 	const gchar * pszCanMergeBorders = PP_evalProperty("border-merge",pSpanAP,pBlockAP,pSectionAP,getDoc(),true);
1362 	const gchar * pszBotBorderColor = PP_evalProperty("bot-color",pSpanAP,pBlockAP,pSectionAP,getDoc(),true);
1363 	const gchar * pszBotBorderStyle = NULL;
1364 	pBlockAP->getProperty ("bot-style",pszBotBorderStyle );
1365 	const gchar * pszBotBorderWidth = PP_evalProperty("bot-thickness",pSpanAP,pBlockAP,pSectionAP,getDoc(),true);
1366 	const gchar * pszBotBorderSpacing = PP_evalProperty("bot-space",pSpanAP,pBlockAP,pSectionAP,getDoc(),true);
1367 
1368 	const gchar * pszLeftBorderColor = PP_evalProperty("left-color",pSpanAP,pBlockAP,pSectionAP,getDoc(),true);
1369 	const gchar * pszLeftBorderStyle = NULL;
1370 	pBlockAP->getProperty ("left-style",pszLeftBorderStyle );
1371 	const gchar * pszLeftBorderWidth = PP_evalProperty("left-thickness",pSpanAP,pBlockAP,pSectionAP,getDoc(),true);
1372 	const gchar * pszLeftBorderSpacing = PP_evalProperty("left-space",pSpanAP,pBlockAP,pSectionAP,getDoc(),true);
1373 
1374 	const gchar * pszRightBorderColor = PP_evalProperty("right-color",pSpanAP,pBlockAP,pSectionAP,getDoc(),true);
1375 	const gchar * pszRightBorderStyle = NULL;
1376 	pBlockAP->getProperty ("right-style",pszRightBorderStyle );
1377 	const gchar * pszRightBorderWidth = PP_evalProperty("right-thickness",pSpanAP,pBlockAP,pSectionAP,getDoc(),true);
1378 	const gchar * pszRightBorderSpacing = PP_evalProperty("right-space",pSpanAP,pBlockAP,pSectionAP,getDoc(),true);
1379 
1380 	const gchar * pszTopBorderColor = PP_evalProperty("top-color",pSpanAP,pBlockAP,pSectionAP,getDoc(),true);
1381 	const gchar * pszTopBorderStyle = NULL;
1382 	pBlockAP->getProperty ("top-style",pszTopBorderStyle );
1383 	const gchar * pszTopBorderWidth = PP_evalProperty("top-thickness",pSpanAP,pBlockAP,pSectionAP,getDoc(),true);
1384 	const gchar * pszTopBorderSpacing = PP_evalProperty("top-space",pSpanAP,pBlockAP,pSectionAP,getDoc(),true);
1385 
1386 	// Shading
1387 
1388 	const gchar * szPattern = PP_evalProperty("shading-pattern",pSpanAP,pBlockAP,pSectionAP,getDoc(),true);
1389 	const gchar * szShadingForeCol =  PP_evalProperty("shading-foreground-color",pSpanAP,pBlockAP,pSectionAP,getDoc(),true);
1390 	//const gchar * szShadingBackCol =  PP_evalProperty("shading-background-color",pSpanAP,pBlockAP,pSectionAP,getDoc(),true);
1391 
1392 
1393 	// TODO add other properties here
1394 
1395 	// Do abi specific list information.
1396 
1397 	const gchar * szListid=NULL;
1398 	const gchar * szParentid=NULL;
1399 	const gchar * szListStyle=NULL;
1400 
1401 	if (!pBlockAP || !pBlockAP->getAttribute(static_cast<const gchar*>("listid"), szListid))		szListid = NULL;
1402 	if (!pBlockAP || !pBlockAP->getAttribute(static_cast<const gchar*>("parentid"), szParentid))
1403 		szParentid = NULL;
1404 	UT_uint32 listid = 0;
1405 	const gchar * szAbiListDelim = NULL;
1406 	const gchar * szAbiListDecimal = NULL;
1407 	static UT_String szAbiStartValue;
1408 	static UT_String szLevel;
1409 	if(szListid!=NULL)
1410 	{
1411 		listid = atoi(szListid);
1412 		if(listid != 0)
1413 		{
1414 			fl_AutoNum * pAuto = getDoc()->getListByID(listid);
1415 			if(pAuto)
1416 			{
1417 				szAbiListDelim = pAuto->getDelim();
1418 				szAbiListDecimal = pAuto->getDecimal();
1419 				UT_String_sprintf(szAbiStartValue,"%i",pAuto->getStartValue32());
1420 				UT_String_sprintf(szLevel,"%i",pAuto->getLevel());
1421 			}
1422 		}
1423 	}
1424 	szListStyle = PP_evalProperty("list-style",pSpanAP,pBlockAP,pSectionAP,getDoc(),true);
1425 	const gchar * szAbiFieldFont = PP_evalProperty("field-font",pSpanAP,pBlockAP,pSectionAP,getDoc(),true);
1426 
1427 
1428 	UT_uint32 id = 0;
1429 	if(szListid != NULL)
1430 		id = atoi(szListid);
1431 	if(id == 0)
1432 	{
1433 		_rtf_keyword("pard");		// begin a new paragraph
1434 		_rtf_keyword("plain");		// begin a new paragraph
1435 	}
1436 	else
1437 	{
1438 		bStartedList = true;
1439 	}
1440 	///
1441 	/// Output fallback numbered/bulleted label for rtf readers that don't
1442 	/// know /*/pn
1443 	///
1444 	if(id != 0 )
1445 	{
1446 		_rtf_open_brace();
1447 		_rtf_keyword("listtext");
1448 		// if string is "left" use "ql", but that is the default, so we don't need to write it out.
1449 		_rtf_keyword("pard");		// restore all defaults for this paragraph
1450 		if (strcmp(szTextAlign,"right")==0)		// output one of q{lrcj} depending upon paragraph alignment
1451 			_rtf_keyword("qr");
1452 		else if (strcmp(szTextAlign,"center")==0)
1453 			_rtf_keyword("qc");
1454 		else if (strcmp(szTextAlign,"justify")==0)
1455 			_rtf_keyword("qj");
1456 		_rtf_keyword_ifnotdefault_twips("fi",static_cast<const char*>(szFirstLineIndent),0);
1457 		_rtf_keyword_ifnotdefault_twips("li",static_cast<const char*>(szLeftIndent),0);
1458 		_rtf_keyword_ifnotdefault_twips("ri",static_cast<const char*>(szRightIndent),0);
1459 		_rtf_keyword_ifnotdefault_twips("sb",static_cast<const char*>(szTopMargin),0);
1460 		_rtf_keyword_ifnotdefault_twips("sa",static_cast<const char*>(szBottomMargin),0);
1461 
1462 		fl_AutoNum * pAuto = getDoc()->getListByID(id);
1463 		UT_return_if_fail(pAuto);
1464 		if(pAuto->getType()==BULLETED_LIST)
1465 		{
1466 			_rtf_keyword("bullet");
1467 		}
1468 		else
1469 		{
1470 			const UT_UCSChar * lab = pAuto->getLabel(sdh);
1471 
1472 			if(lab != NULL)
1473 			{
1474 				UT_UTF8String tmp = lab;
1475 				_rtf_chardata(tmp.utf8_str(),tmp.byteLength());
1476 			}
1477 			else
1478 			{
1479 			    UT_DEBUGMSG(("SEVIOR: We should not be here! id = %d \n",id));
1480 				_rtf_chardata(" ",1);
1481 			}
1482 		}
1483 		//
1484         // Put in Tab for braindead RTF importers (like Ted) that can't
1485         // do numbering.
1486 		//
1487 		char tab = static_cast<char>(9);
1488 		_rtf_chardata(&tab,1);
1489 		_rtf_close_brace();
1490 		_rtf_keyword("pard");		// restore all defaults for this paragraph
1491 		_rtf_keyword("plain");		// restore all defaults for this paragraph
1492 	}
1493 	if(bStartedList)
1494 	{
1495 		_rtf_open_brace();
1496 	}
1497 
1498 	// it is essential that the \rtlpar and \ltrpar tokens are issued
1499 	// before any formatting, otherwise it can cause difficulties
1500 	const gchar * szBidiDir = PP_evalProperty("dom-dir",
1501 												 pSpanAP,
1502 												 pBlockAP,
1503 												 pSectionAP,
1504 												 getDoc(),
1505 												 true);
1506 
1507 	xxx_UT_DEBUGMSG(("bidi paragraph: pSectionAp 0x%x, pBlockAP 0x%x, dom-dir\"%s\"\n",pSectionAP,pBlockAP,szBidiDir));
1508 	if (szBidiDir)
1509 	{
1510 		if (!strcmp (szBidiDir, "ltr"))
1511 			_rtf_keyword ("ltrpar");
1512 		else
1513 			_rtf_keyword ("rtlpar");
1514 	}
1515 
1516 	// if string is "left" use "ql", but that is the default, so we
1517 	// don't need to write it out.
1518 	// Except that if the alignment overrides one prescribed by a
1519 	// style, we probably need to issue this (Tomas, Apr 12, 2003)
1520 
1521 	// output q{lrcj depending upon paragraph alignment
1522 	if (strcmp(szTextAlign,"left")==0)
1523 		_rtf_keyword("ql");
1524 	else if (strcmp(szTextAlign,"justify")==0)
1525 		_rtf_keyword("qj");
1526 	else if (strcmp(szTextAlign,"right")==0)
1527 		_rtf_keyword("qr");
1528 	else if (strcmp(szTextAlign,"center")==0)
1529 		_rtf_keyword("qc");
1530 
1531 
1532 
1533 	_rtf_keyword_ifnotdefault_twips("fi",static_cast<const char*>(szFirstLineIndent),0);
1534 	_rtf_keyword_ifnotdefault_twips("li",static_cast<const char*>(szLeftIndent),0);
1535 	_rtf_keyword_ifnotdefault_twips("ri",static_cast<const char*>(szRightIndent),0);
1536 	_rtf_keyword_ifnotdefault_twips("sb",static_cast<const char*>(szTopMargin),0);
1537 	_rtf_keyword_ifnotdefault_twips("sa",static_cast<const char*>(szBottomMargin),0);
1538 
1539 	const gchar * szStyle = NULL;
1540 	if (pBlockAP->getAttribute("style", szStyle))
1541 	{
1542 	    _rtf_keyword("s", _getStyleNumber(szStyle));
1543 	}
1544 ///
1545 /// OK we need to output the char props if there is a list here
1546 	// no, we must not output character properties here, because the properties that are
1547 	// output here will apply to everyting that will follow in this paragraph: see bug 5693
1548 	// -- I am really not sure what the rationale for the char props output here was, so
1549 	// if commenting this out creates some other problem, please let me know. Tomas, Sep
1550 	// 2, 2004
1551 #if 0 //#TF
1552 	if(id != 0)
1553 	{
1554 		const PP_AttrProp * pSpanAP = NULL;
1555 		const PP_AttrProp * pBlockAP = NULL;
1556 		const PP_AttrProp * pSectionAP = NULL;
1557 
1558 		getDoc()->getAttrProp(m_apiThisSection,&pSectionAP);
1559 		getDoc()->getAttrProp(m_apiThisBlock,&pBlockAP);
1560 		_write_charfmt(s_RTF_AttrPropAdapter_AP(pSpanAP, pBlockAP, pSectionAP, getDoc()));
1561 	}
1562 #endif
1563 	///
1564 	/// OK if there is list info in this paragraph we encase it inside
1565 	/// the {\*\abilist..} extension
1566 	///
1567 	if(id != 0 )
1568 	{
1569 		bIsListBlock = true;
1570 
1571 		_rtf_open_brace();
1572 		_rtf_keyword("*");
1573 		_rtf_keyword("abilist");
1574 		_rtf_keyword_ifnotdefault("abilistid",static_cast<const char *>(szListid),-1);
1575 		_rtf_keyword_ifnotdefault("abilistparentid",static_cast<const char *>(szParentid),-1);
1576 		_rtf_keyword_ifnotdefault("abilistlevel",szLevel.c_str(),-1);
1577 		_rtf_keyword_ifnotdefault("abistartat",szAbiStartValue.c_str(),-1);
1578 		/// field font
1579 
1580 		_rtf_open_brace();
1581 		_rtf_keyword("abifieldfont");
1582 		_rtf_chardata( static_cast<const char *>(szAbiFieldFont) ,strlen(szAbiFieldFont));
1583 		_rtf_close_brace();
1584 
1585 		/// list decimal
1586 
1587 		_rtf_open_brace();
1588 		_rtf_keyword("abilistdecimal");
1589 		_rtf_chardata(static_cast<const char *>(szAbiListDecimal) ,strlen(szAbiListDecimal));
1590 		_rtf_close_brace();
1591 
1592 		/// list delim
1593 
1594 		_rtf_open_brace();
1595 		_rtf_keyword("abilistdelim");
1596 		_rtf_chardata(static_cast<const char *>(szAbiListDelim) ,strlen( szAbiListDelim));
1597 		_rtf_close_brace();
1598 
1599 		/// list style
1600 
1601 		_rtf_open_brace();
1602 		_rtf_keyword("abiliststyle");
1603 		_rtf_chardata(static_cast<const char *>(szListStyle) ,strlen( szListStyle));
1604 		_rtf_close_brace();
1605 
1606 		/// Finished!
1607 
1608 		_rtf_close_brace();
1609 
1610 	}
1611 
1612 	///
1613 	/// OK Now output word-95 style lists
1614 	///
1615 
1616 	if(id != 0 )
1617 	{
1618 		_rtf_open_brace();
1619 		_rtf_keyword("*");
1620 		_rtf_keyword("pn");
1621 		fl_AutoNum * pAuto = getDoc()->getListByID(id);
1622 		UT_return_if_fail(pAuto);
1623 		_rtf_keyword("pnql");
1624 		_rtf_keyword("pnstart",pAuto->getStartValue32());
1625 		FL_ListType lType = pAuto->getType();
1626 
1627 		///
1628 		/// extract text before and after numbering symbol
1629 		///
1630 		static gchar p[80],leftDelim[80],rightDelim[80];
1631 		sprintf(p, "%s",pAuto->getDelim());
1632 		UT_uint32 rTmp;
1633 
1634 		UT_uint32 i = 0;
1635 
1636 		while (p[i] && p[i] != '%' && p[i+1] != 'L')
1637 		{
1638 			leftDelim[i] = p[i];
1639 			i++;
1640 		}
1641 		leftDelim[i] = '\0';
1642 		i += 2;
1643 		rTmp = i;
1644 		while (p[i] || p[i] != '\0')
1645 		{
1646 			rightDelim[i - rTmp] = p[i];
1647 			i++;
1648 		}
1649 		rightDelim[i - rTmp] = '\0';
1650 
1651 		fl_AutoNum * pParent = pAuto->getParent();
1652 		if(pParent == NULL && (lType < BULLETED_LIST))
1653 		{
1654 			_rtf_keyword("pnlvlbody");
1655 		}
1656 		else if(lType >= BULLETED_LIST && (lType != NOT_A_LIST))
1657 		{
1658 			_rtf_keyword("pnlvlblt");
1659 		}
1660 		else
1661 		{
1662 			_rtf_keyword("pnprev");
1663 			_rtf_keyword("pnlvl",9);
1664 		}
1665 		if(lType == NUMBERED_LIST)
1666 		{
1667 			_rtf_keyword("pndec");
1668 		}
1669 		else if(lType == LOWERCASE_LIST)
1670 		{
1671 			_rtf_keyword("pnlcltr");
1672 		}
1673 		else if(lType == UPPERCASE_LIST)
1674 		{
1675 			_rtf_keyword("pnucltr");
1676 		}
1677 		else if(lType == LOWERROMAN_LIST)
1678 		{
1679 			_rtf_keyword("pnlcrm");
1680 		}
1681 		else if(lType == UPPERROMAN_LIST)
1682 		{
1683 			_rtf_keyword("pnucrm");
1684 		}
1685 		else if(lType == NOT_A_LIST)
1686 		{
1687 			_rtf_keyword("pnucrm");
1688 		}
1689 		if(lType < BULLETED_LIST)
1690 		{
1691 			_rtf_open_brace();
1692 			_rtf_keyword("pntxtb");
1693 			_rtf_chardata(static_cast<const char *>(leftDelim),strlen(static_cast<const char *>(leftDelim)));
1694 			_rtf_close_brace();
1695 			_rtf_open_brace();
1696 			_rtf_keyword("pntxta");
1697 			_rtf_chardata(static_cast<const char *>(rightDelim),strlen(static_cast<const char *>(rightDelim)));
1698 			_rtf_close_brace();
1699 		}
1700 		else if(lType >= BULLETED_LIST && lType != NOT_A_LIST)
1701 		{
1702 			_rtf_open_brace();
1703 			_rtf_keyword("pntxtb");
1704 			_rtf_keyword("bullet");
1705 			_rtf_close_brace();
1706 		}
1707 
1708 		_rtf_close_brace();
1709 	}
1710 
1711 
1712 
1713 	///
1714 	/// OK Now output word-97 style lists. First detect if we've moved to
1715     /// a new list list structure. We need m_currID to track the previous list
1716     /// we were in.
1717 	///
1718 	xxx_UT_DEBUGMSG(("SEVIOR: Doing output of list structure id = %d\n",id));
1719 	if(id != 0 )
1720 	{
1721 		UT_uint32 iOver = getMatchingOverideNum(id);
1722 		UT_uint32 iLevel = 0;
1723 		fl_AutoNum * pAuto = getDoc()->getListByID(id);
1724 
1725 //		if(id != m_currID)
1726 		{
1727 			UT_return_if_fail(iOver);
1728 //			fl_AutoNum * pAuto = getDoc()->getListByID(id);
1729 			UT_return_if_fail(pAuto);
1730 			while(pAuto->getParent() != NULL)
1731 			{
1732 				pAuto = pAuto->getParent();
1733 				iLevel++;
1734 			}
1735 			if(iLevel > 8)
1736 			{
1737 				UT_ASSERT_HARMLESS(0);
1738 				iLevel = 8;
1739 			}
1740 			iCurrID = id;
1741 		}
1742 		/* This is changed so that Word97 can see the numbers in
1743 		   numbered lists */
1744 		if(pAuto->getType() < BULLETED_LIST)
1745 		{
1746 	        	_rtf_keyword_ifnotdefault_twips("fn",static_cast<const char*>(szFirstLineIndent),0);
1747 	        	_rtf_keyword_ifnotdefault_twips("li",static_cast<const char*>(szLeftIndent),0);
1748 		}
1749 		_rtf_keyword("ls",iOver);
1750 		_rtf_keyword("ilvl",iLevel);
1751 	}
1752 
1753 	if (strcmp(szLineHeight,"1.0") != 0)
1754 	{
1755 		double f = UT_convertDimensionless(szLineHeight);
1756 
1757 
1758 		if (f > 0.000001)
1759 		{                                   // we get zero on bogus strings....
1760 		        const char * pPlusFound = strrchr(szLineHeight, '+');
1761 		        if (pPlusFound && *(pPlusFound + 1) == 0)             //  "+" means "at least" line spacing
1762 			{
1763 				UT_sint32 dSpacing = (UT_sint32)(f * 20.0);
1764 				_rtf_keyword("sl",dSpacing);
1765 				_rtf_keyword("slmult",0);
1766 			}
1767 			else if (UT_hasDimensionComponent(szLineHeight)) //  use exact line spacing
1768 			{
1769 			        UT_sint32 dSpacing = (UT_sint32)(f * 20.0);
1770 			        _rtf_keyword("sl",-dSpacing);
1771 			        _rtf_keyword("slmult",0);
1772 		        }
1773 			else // multiple line spacing
1774 			{
1775 			        UT_sint32 dSpacing = (UT_sint32)(f * 240.0);
1776 			        _rtf_keyword("sl",dSpacing);
1777 			        _rtf_keyword("slmult",1);
1778 		        }
1779 		}
1780 	}
1781 //
1782 // Output Paragraph Cell nesting level.
1783 //
1784 	if(iNestLevel > 0)
1785 	{
1786 		_rtf_keyword("intbl");
1787 	}
1788 	_rtf_keyword("itap",iNestLevel);
1789 
1790 	if (strcmp(szKeepTogether,"yes")==0)
1791 		_rtf_keyword("keep");
1792 	if (strcmp(szKeepWithNext,"yes")==0)
1793 		_rtf_keyword("keepn");
1794 
1795 	_write_tabdef(szTabStops);
1796 
1797 	// Export Borders
1798 	UT_sint32 ndx_col = 0;
1799 	if(pszCanMergeBorders != NULL && strcmp(pszCanMergeBorders,"0") != 0)
1800 	{
1801 		_rtf_keyword("brdrbtw");
1802 	}
1803 	if(pszBotBorderStyle != NULL && *pszBotBorderStyle && strcmp(pszBotBorderStyle,"0") != 0)
1804 	{
1805 		UT_DEBUGMSG(("pszBotBorderStyle is %s \n",pszBotBorderStyle));
1806 		write(" ");
1807 		_rtf_keyword("brdrb");
1808 		_rtf_keyword("brdrs");
1809 		ndx_col =_findOrAddColor(pszBotBorderColor);
1810 		if(ndx_col < 0)
1811 			ndx_col = 0;
1812 		_rtf_keyword("brdrcf",ndx_col);
1813 		if(pszBotBorderWidth)
1814 		{
1815 			_rtf_keyword_ifnotdefault_twips("brdrw",static_cast<const char*>(pszBotBorderWidth),0);
1816 		}
1817 		if(pszBotBorderSpacing)
1818 		{
1819 			_rtf_keyword_ifnotdefault_twips("brsp",static_cast<const char*>(pszBotBorderSpacing),0);
1820 		}
1821 		write(" ");
1822 	}
1823 	if(pszLeftBorderStyle != NULL && *pszLeftBorderStyle && strcmp(pszLeftBorderStyle,"0") != 0)
1824 	{
1825 		_rtf_keyword("brdrl");
1826 		_rtf_keyword("brdrs");
1827 		ndx_col =_findOrAddColor(pszLeftBorderColor);
1828 		if(ndx_col < 0)
1829 			ndx_col = 0;
1830 		_rtf_keyword("brdrcf",ndx_col);
1831 		if(pszLeftBorderWidth)
1832 		{
1833 			_rtf_keyword_ifnotdefault_twips("brdrw",static_cast<const char*>(pszLeftBorderWidth),0);
1834 		}
1835 		if(pszLeftBorderSpacing)
1836 		{
1837 			_rtf_keyword_ifnotdefault_twips("brsp",static_cast<const char*>(pszLeftBorderSpacing),0);
1838 		}
1839 		write(" ");
1840 	}
1841 	if(pszRightBorderStyle != NULL && *pszRightBorderStyle && strcmp(pszRightBorderStyle,"0") != 0)
1842 	{
1843 		_rtf_keyword("brdrr");
1844 		_rtf_keyword("brdrs");
1845 		ndx_col =_findOrAddColor(pszRightBorderColor);
1846 		if(ndx_col < 0)
1847 			ndx_col = 0;
1848 		_rtf_keyword("brdrcf",ndx_col);
1849 		if(pszRightBorderWidth)
1850 		{
1851 			_rtf_keyword_ifnotdefault_twips("brdrw",static_cast<const char*>(pszRightBorderWidth),0);
1852 		}
1853 		if(pszRightBorderSpacing)
1854 		{
1855 			_rtf_keyword_ifnotdefault_twips("brsp",static_cast<const char*>(pszRightBorderSpacing),0);
1856 		}
1857 		write(" ");
1858 	}
1859 	if(pszTopBorderStyle != NULL && *pszTopBorderStyle && strcmp(pszTopBorderStyle,"0") != 0)
1860 	{
1861 		_rtf_keyword("brdrt");
1862 		_rtf_keyword("brdrs");
1863 		ndx_col =_findOrAddColor(pszTopBorderColor);
1864 		if(ndx_col < 0)
1865 			ndx_col = 0;
1866 		_rtf_keyword("brdrcf",ndx_col);
1867 		if(pszTopBorderWidth)
1868 		{
1869 			_rtf_keyword_ifnotdefault_twips("brdrw",static_cast<const char*>(pszTopBorderWidth),0);
1870 		}
1871 		if(pszTopBorderSpacing)
1872 		{
1873 			_rtf_keyword_ifnotdefault_twips("brsp",static_cast<const char*>(pszTopBorderSpacing),0);
1874 		}
1875 		write(" ");
1876 	}
1877 
1878 	// export shadings
1879 
1880 	if(szPattern != NULL && *szPattern && strcmp(szPattern,"1") == 0)
1881 	{
1882 		// Can only handle solid shadings right now
1883 		ndx_col =_findOrAddColor(szShadingForeCol);
1884 		if(ndx_col < 0)
1885 			ndx_col = 0;
1886 		_rtf_keyword("cbpat",ndx_col);
1887 
1888 	}
1889 
1890 }
1891 
1892 
1893 /*!
1894  * Write out the <charfmt> paragraph or character formatting. This
1895  * does not print opening and closing braces.
1896  */
_write_charfmt(const s_RTF_AttrPropAdapter & apa)1897 void IE_Exp_RTF::_write_charfmt(const s_RTF_AttrPropAdapter & apa)
1898 {
1899 	//const gchar * szStyle = apa.getAttribute(PT_STYLE_ATTRIBUTE_NAME);
1900 	//UT_sint32 iStyle = -1;
1901 	s_RTF_AttrPropAdapter_Style * pADStyle = NULL;
1902 #if 0
1903 	if(szStyle != NULL)
1904 	{
1905 		PD_Style * pStyle = NULL;
1906 		iStyle = static_cast<UT_sint32>(_getStyleNumber(szStyle));
1907 		getDoc()->getStyle(szStyle,&pStyle);
1908 		pADStyle = new s_RTF_AttrPropAdapter_Style(pStyle);
1909 //
1910 // OK now we have to make sure all these character props aren't in the style
1911 //
1912 	}
1913 #endif
1914 	const gchar * szColor = _getStyleProp(pADStyle,&apa,"color");
1915 
1916 	UT_sint32 ndxColor = -1;
1917 	if(szColor)
1918 	{
1919 		ndxColor = _findColor((char*)szColor);
1920 		if( ndxColor == -1)
1921 		{
1922 			return;
1923 		}
1924 		UT_return_if_fail (ndxColor != -1);
1925 
1926 		if (ndxColor != 0) // black text, the default
1927 			_rtf_keyword("cf",ndxColor);
1928 	}
1929 
1930 	szColor = _getStyleProp(pADStyle,&apa,"bgcolor");
1931 
1932 	if (szColor && g_ascii_strcasecmp (szColor, "transparent") != 0)
1933 	{
1934 		ndxColor = _findColor((char*)szColor);
1935 		UT_ASSERT_HARMLESS(ndxColor != -1);
1936 		if (ndxColor != 1) // white background, the default
1937 		{
1938 			_rtf_keyword("cb",ndxColor);
1939 			_rtf_keyword("highlight",ndxColor);
1940 		}
1941 	}
1942 	const gchar * szFont = NULL;
1943 	if(pADStyle != NULL)
1944 	{
1945 		szFont = pADStyle->getProperty("font-family");
1946 	}
1947 	if(szFont == NULL)
1948 	{
1949 		UT_sint32 ndxFont = _findFont(&apa);
1950 		if(ndxFont != -1)
1951 			_rtf_keyword("f",ndxFont);	// font index in fonttbl
1952 	}
1953 
1954 	const gchar * szFontSize = _getStyleProp(pADStyle,&apa,"font-size");
1955 	double dbl = UT_convertToPoints(szFontSize);
1956 	UT_sint32 d = (UT_sint32)(dbl*2.0);
1957 
1958 	// if (d != 24) - always write this out
1959 	if(szFontSize != NULL)
1960 	{
1961 		if(d == 0)
1962 			d = 24;
1963 		_rtf_keyword("fs",d);	// font size in half points
1964 	}
1965 	const gchar * szFontStyle = _getStyleProp(pADStyle,&apa,"font-style");
1966 	if (szFontStyle && *szFontStyle && (strcmp(szFontStyle,"italic")==0))
1967 		_rtf_keyword("i");
1968 
1969 	const gchar * szFontWeight = _getStyleProp(pADStyle,&apa,"font-weight");
1970 	if (szFontWeight && *szFontWeight && (strcmp(szFontWeight,"bold")==0))
1971 		_rtf_keyword("b");
1972 
1973 	const gchar * szFontDecoration = _getStyleProp(pADStyle,&apa,"text-decoration");
1974 	if (szFontDecoration && *szFontDecoration)
1975 	{
1976 		if (strstr(szFontDecoration,"underline") != 0)
1977 			_rtf_keyword("ul");
1978 		if (strstr(szFontDecoration,"overline") != 0)
1979 			_rtf_keyword("ol");
1980 		if (strstr(szFontDecoration,"line-through") != 0)
1981 			_rtf_keyword("strike");
1982 		if (strstr(szFontDecoration,"topline") != 0)
1983 		{
1984 			_rtf_keyword("abitopline"); // abiword extension
1985 		}
1986 		if (strstr(szFontDecoration,"bottomline") != 0)
1987 		{
1988 			_rtf_keyword("abibotline"); // abiword extension
1989 		}
1990 	}
1991 
1992 	const gchar * szFontPosition = _getStyleProp(pADStyle,&apa,"text-position");
1993 	if (szFontPosition && *szFontPosition)
1994 	{
1995 		if (!strcmp(szFontPosition,"superscript"))
1996 			_rtf_keyword("super");
1997 		else if (!strcmp(szFontPosition,"subscript"))
1998 			_rtf_keyword("sub");
1999 	}
2000 
2001 	// export the language of the run of text
2002 	const gchar * szLang = _getStyleProp(pADStyle,&apa,"lang");
2003 	if ( szLang )
2004 	  {
2005 	    xxx_UT_DEBUGMSG(("DOM: lang,lid = %s,%d\n", szLang, wvLangToLIDConverter(szLang)));
2006 	    _rtf_keyword("lang", wvLangToLIDConverter(szLang));
2007 	  }
2008 
2009 	//###TF const gchar * szDir = apa.getProperty("dir");
2010 	const gchar * szDirOvrr = _getStyleProp(pADStyle,&apa,"dir-override");
2011 
2012 	//bool bProceed = true;
2013 	if (szDirOvrr)
2014 	{
2015 		if (!strcmp (szDirOvrr, "ltr"))
2016 		{
2017 			_rtf_keyword ("ltrch");
2018 			_rtf_keyword ("abiltr");
2019 			m_CharRTL = UT_BIDI_LTR;
2020 			//bProceed = false;
2021 		}
2022 		else if (!strcmp (szDirOvrr, "rtl"))
2023 		{
2024 			_rtf_keyword ("rtlch");
2025 			_rtf_keyword ("abirtl");
2026 			m_CharRTL = UT_BIDI_RTL;
2027 			//bProceed  = false;
2028 		}
2029 	}
2030 	/*
2031 	if (bProceed || szDir)
2032 	{
2033 		if (!strcmp (szDir, "ltr"))
2034 			_rtf_keyword ("ltrch");
2035 		else if (!strcmp (szDir, "rtl"))
2036 			_rtf_keyword ("rtlch");
2037 	}  */
2038 
2039 	const gchar * szHidden = _getStyleProp(pADStyle,&apa,"display");
2040 	if(szHidden && *szHidden && !strcmp(szHidden, "none"))
2041 	{
2042 		_rtf_keyword ("v");
2043 	}
2044 
2045 
2046 	const gchar * szListTag = apa.getProperty("list-tag");
2047 	if (szListTag && *szListTag)
2048 	{
2049 		_rtf_open_brace();
2050 		_rtf_keyword("*");
2051 		UT_uint32 id = atoi(szListTag);
2052 		_rtf_keyword("listtag",id);
2053 		_rtf_close_brace();
2054 	}
2055 	DELETEP(pADStyle);
2056 
2057 	// TODO do something with our font-stretch and font-variant properties
2058 	// note: we assume that kerning has been turned off at global scope.
2059 
2060 	// MUST BE LAST after all other props have been processed
2061 	bool b1,b2; UT_uint32 u1; // these are only used if the bPara parameter is true
2062 	_output_revision(apa, false, NULL, 0, b1, b2, u1);
2063 }
2064 
_output_revision(const s_RTF_AttrPropAdapter & apa,bool bPara,pf_Frag_Strux * sdh,UT_sint32 iNestLevel,bool & bStartedList,bool & bIsListBlock,UT_uint32 & iCurrID)2065 void IE_Exp_RTF::_output_revision(const s_RTF_AttrPropAdapter & apa, bool bPara,
2066 								  pf_Frag_Strux* sdh, UT_sint32 iNestLevel,
2067 								  bool & bStartedList,  bool &bIsListBlock, UT_uint32 &iCurrID)
2068 {
2069 	const gchar *szRevisions = apa.getAttribute("revision");
2070 	if (szRevisions && *szRevisions)
2071 	{
2072 		PP_RevisionAttr RA(szRevisions);
2073 
2074 		if(!RA.getRevisionsCount())
2075 		{
2076 			UT_return_if_fail( UT_SHOULD_NOT_HAPPEN );
2077 		}
2078 
2079 		// we dump the revision attribute directly using our extended keyword
2080 		// 'abirevision' (I do this only reluctantly)
2081 
2082 		_rtf_open_brace();
2083 		_rtf_keyword("*");
2084 		_rtf_keyword("abirevision");
2085 
2086 		UT_UTF8String s;
2087 		const char * p = szRevisions;
2088 
2089 		// have to escape \, {, }
2090 		while(p && *p)
2091 		{
2092 			if(*p == '\\' || *p == '{' || *p == '}')
2093 				s += '\\';
2094 
2095 			s += *p++;
2096 		}
2097 
2098 
2099 		_rtf_chardata(s.utf8_str(), s.byteLength());
2100 		_rtf_close_brace();
2101 
2102 
2103 		// for now, we will just dump the lot; later we need to figure out how to deal
2104 		// with revision conflicts ...
2105 		for(UT_uint32 i = 0; i < RA.getRevisionsCount(); ++i)
2106 		{
2107 			const PP_Revision * pRev = RA.getNthRevision(i); // a revision found in our attribute
2108 			UT_continue_if_fail(pRev);
2109 
2110 			UT_uint32 iId = pRev->getId();
2111 
2112 			// now we translated the revision id to and index into the revision table of
2113 			// the document
2114 			UT_sint32 iIndx = getDoc()->getRevisionIndxFromId(iId);
2115 			const UT_GenericVector<AD_Revision*> & RevTbl = getDoc()->getRevisions();
2116 			if(iIndx < 0 || RevTbl.getItemCount() == 0)
2117 			{
2118 				UT_ASSERT_HARMLESS( UT_SHOULD_NOT_HAPPEN );
2119 				continue;
2120 			}
2121 
2122 			AD_Revision * pRevTblItem = RevTbl.getNthItem(iIndx);
2123 			UT_continue_if_fail(pRevTblItem);
2124 
2125 			// the revisions in rtf are marked by a peculiar timestamp, which we now need
2126 			// to construct
2127 			time_t t = pRevTblItem->getStartTime();
2128 			struct tm * pT = gmtime(&t);
2129 
2130 			// NB: gmtime counts months 0-11, while dttm 1-12
2131 
2132 			UT_uint32 iDttm = pT->tm_min | (pT->tm_hour << 6) | (pT->tm_mday << 11) | ((pT->tm_mon + 1) << 16)
2133 				| (pT->tm_year << 20) | (pT->tm_wday << 29);
2134 
2135 #if 0
2136 			// according to the docs, we are supposed to emit each of the four bytes of
2137 			// the dttm int as an ascii char, and if > 127 convert to hex; however, Word
2138 			// emits this as a normal int and so will we -- the code below is disabled
2139 			//
2140 			// Now that we have the int, we need to convert the 4 bytes to ascii string.
2141 			// We need to output this in little endian order, I think, since win32 is
2142 			// inherently LE
2143 			char Dttm[4];
2144 			const char * pDttm = (const char *) & iDttm;
2145 
2146 #ifdef UT_LITTLE_ENDIAN
2147 			Dttm[0] = *pDttm;
2148 			Dttm[1] = *(pDttm + 1);
2149 			Dttm[2] = *(pDttm + 2);
2150 			Dttm[3] = *(pDttm + 3);
2151 #else
2152 			Dttm[3] = *pDttm;
2153 			Dttm[2] = *(pDttm + 1);
2154 			Dttm[1] = *(pDttm + 2);
2155 			Dttm[0] = *(pDttm + 3);
2156 #endif
2157 			UT_UTF8String s;
2158 
2159 			for(UT_uint32 j = 0; j < 4; j++)
2160 			{
2161 				if(Dttm[i] <= 127)
2162 				{
2163 					s += Dttm[i];
2164 				}
2165 				else
2166 				{
2167 					UT_String s2;
2168 					_rtf_nonascii_hex2((UT_sint32)Dttm[i], s2);
2169 					s += s2.c_str();
2170 				}
2171 			}
2172 #endif
2173 			if(iIndx < 0)
2174 			{
2175 				UT_ASSERT_HARMLESS( UT_SHOULD_NOT_HAPPEN );
2176 				continue;
2177 			}
2178 
2179 			bool bRevisedProps = false;
2180 
2181 			const char * pAD     = bPara ? "pnrnot": "revised";
2182 			const char * pADauth = bPara ? "pnrauth" : "revauth";
2183 			const char * pADdttm = bPara ? "pnrdate" : "revdttm";
2184 
2185 			const char pDEL[]     = "deleted";
2186 			const char pDELauth[] = "revauthdel";
2187 			const char pDELdttm[] = "revdttmdel";
2188 
2189 			// it seems that block props cannot be changed in rev mode
2190 			const char * pCHauth = bPara ? NULL : "crauth";
2191 			const char * pCHdttm = bPara ? NULL : "crdate";
2192 
2193 			switch(pRev->getType())
2194 			{
2195 				case PP_REVISION_ADDITION_AND_FMT:
2196 					bRevisedProps = true;
2197 					// fall through
2198 				case PP_REVISION_ADDITION:
2199 					_rtf_keyword(pAD);
2200 					_rtf_keyword(pADauth, iIndx+1);
2201 					_rtf_keyword(pADdttm, iDttm);
2202 					break;
2203 
2204 				case PP_REVISION_DELETION:
2205 					_rtf_keyword(pDEL);
2206 					_rtf_keyword(pDELauth, iIndx+1);
2207 					_rtf_keyword(pDELdttm, iDttm);
2208 					break;
2209 
2210 				case PP_REVISION_FMT_CHANGE:
2211 					bRevisedProps = true;
2212 
2213 					if(bPara)
2214 					{
2215 						break;
2216 					}
2217 
2218 					_rtf_keyword(pCHauth, iIndx+1);
2219 					_rtf_keyword(pCHdttm, iDttm);
2220 					break;
2221 
2222 				default:
2223 					UT_ASSERT_HARMLESS( UT_SHOULD_NOT_HAPPEN );
2224 			}
2225 
2226 			if(bRevisedProps)
2227 			{
2228 				// need to dump the props that belong to our revision ...
2229 				// NB: this might be a recursive call, since _output_revision()
2230 				// gets (among others) called by _write_charfmt()
2231 				const PP_AttrProp * pSpanAP = pRev;
2232 				const PP_AttrProp * pBlockAP = NULL;
2233 				const PP_AttrProp * pSectionAP = NULL;
2234 
2235 				_write_charfmt(s_RTF_AttrPropAdapter_AP(pSpanAP, pBlockAP, pSectionAP, getDoc()));
2236 
2237 				if(bPara)
2238 				{
2239 					UT_continue_if_fail(sdh);
2240 
2241 					_write_parafmt(NULL, pRev, NULL,
2242 								   bStartedList, sdh, iCurrID, bIsListBlock, iNestLevel);
2243 				}
2244 
2245 			}
2246 
2247 		} // for
2248 	} // if(pRevisions)
2249 }
2250 
2251 
2252 /*!
2253  * Write out the formatting group for one style in the RTF header.
2254  */
_write_style_fmt(const PD_Style * pStyle)2255 void IE_Exp_RTF::_write_style_fmt(const PD_Style * pStyle)
2256 {
2257     // brdrdef: not implemented because AbiWord does not have borders
2258     // at time of this writing.
2259 
2260     // parfmt
2261     _write_prop_ifyes(pStyle, "keep-together", "keep");
2262     _write_prop_ifyes(pStyle, "keep-with-next", "keepn");
2263 
2264     const gchar * sz = NULL;
2265     if (pStyle->getProperty((const gchar *)"text-align", sz))
2266 	{
2267 		if (strcmp(sz, "left") == 0)
2268 		{
2269 			// Default, so no need to print anything
2270 		}
2271 		else if (strcmp(sz, "right") == 0)
2272 		{
2273 			_rtf_keyword("qr");
2274 		}
2275 		else if (strcmp(sz, "center") == 0)
2276 		{
2277 			_rtf_keyword("qc");
2278 		}
2279 		else if (strcmp(sz, "justify") == 0)
2280 		{
2281 			_rtf_keyword("qj");
2282 		}
2283 		else
2284 		{
2285 		  UT_ASSERT_NOT_REACHED();
2286 		}
2287     }
2288 
2289     const gchar * szLineHeight = NULL;
2290     if (pStyle->getProperty((const gchar *) "line-height", szLineHeight)
2291 		&& strcmp(szLineHeight,"1.0") != 0)
2292 	{
2293 		double f = UT_convertDimensionless(szLineHeight);
2294 		if (f != 0.0)
2295 		{
2296 			UT_sint32 dSpacing = (UT_sint32)(f * 240.0);
2297 			_rtf_keyword("sl",dSpacing);
2298 			_rtf_keyword("slmult",1);
2299 		}
2300     }
2301 
2302     _write_prop_ifnotdefault(pStyle, "text-indent", "fi");
2303     _write_prop_ifnotdefault(pStyle, "margin-left", "li");
2304     _write_prop_ifnotdefault(pStyle, "margin-right", "ri");
2305     _write_prop_ifnotdefault(pStyle, "margin-top", "sb");
2306     _write_prop_ifnotdefault(pStyle, "margin-bottom", "sa");
2307 
2308     // apoctl
2309 
2310     // tabdef
2311     if (pStyle->getProperty((const gchar *) "tabstops", sz)) _write_tabdef(sz);
2312 
2313 
2314     // shading
2315 
2316     // chrfmt
2317     _write_charfmt(s_RTF_AttrPropAdapter_Style(pStyle));
2318 }
2319 
2320 /*!
2321  * This is just a pair: style and style number. It is used for
2322  * two-way association of PD_Style and RTF style number.
2323  */
2324 struct NumberedStyle
2325 {
2326     const PD_Style * pStyle;
2327     UT_uint32 n;
2328 
NumberedStyleNumberedStyle2329     NumberedStyle(const PD_Style * _pStyle, UT_uint32 _n) :
2330 		pStyle(_pStyle), n(_n) {}
2331 };
2332 
2333 /*!
2334  * Clear the style hash.
2335  */
_clearStyles()2336 void IE_Exp_RTF::_clearStyles()
2337 {
2338 	m_hashStyles.purgeData();
2339 }
2340 
2341 #ifdef _MSC_VER	// MSVC++ warns about 'e' : unreferenced local variable
2342 #pragma warning(disable: 4101)
2343 #endif
2344 
2345 
2346 /*!
2347  * Select styles for export. This inserts all styles to be exported
2348  * into the style hash. Also, it makes sure that all fonts used in
2349  * styles are present in the font table.
2350  */
_selectStyles()2351 void IE_Exp_RTF::_selectStyles()
2352 {
2353     _clearStyles();
2354 
2355     UT_uint32 i;
2356     UT_uint32 nStyleNumber = 0;
2357     const char * szName;
2358     const PD_Style * pStyle;
2359 	UT_GenericVector<PD_Style*> vecStyles;
2360 	getDoc()->getAllUsedStyles(&vecStyles);
2361 
2362 	UT_GenericVector<PD_Style*> * pStyles = NULL;
2363 	getDoc()->enumStyles(pStyles);
2364 	UT_return_if_fail( pStyles );
2365 	UT_uint32 iStyleCount = getDoc()->getStyleCount();
2366 
2367 	for (i = 0; i < iStyleCount; ++i)
2368 	{
2369 		// DOM: hack for 2069 - we'll export all styles instead of just
2370 		// user-defined styles and used styles. To fix it (and export fewer
2371 		// styles in general) make this routine recursive to include
2372 		// parent (basedon) styles as well...
2373 		pStyle = pStyles->getNthItem(i);
2374 		UT_return_if_fail( pStyle );
2375 
2376 		szName = pStyle->getName();
2377 
2378 		if (true /* pStyle->isUserDefined() || (vecStyles.findItem((void *) pStyle) >= 0)*/)
2379 		{
2380 			//
2381 			// Add this style to the hash
2382 			//
2383 			NumberedStyle * pns = (NumberedStyle *) m_hashStyles.pick(szName);
2384 			if(pns == NULL)
2385 			{
2386 				m_hashStyles.insert(szName, new NumberedStyle(pStyle, ++nStyleNumber));
2387 				{
2388 					_rtf_font_info fi;
2389 
2390 					if (fi.init(static_cast<s_RTF_AttrPropAdapter_Style>(pStyle))) {
2391 						if (_findFont(&fi) == -1)
2392 							_addFont(&fi);
2393 					}
2394 				}
2395 				//
2396 				// Now do it for the field font as well
2397 				//
2398 				{
2399 					_rtf_font_info fi;
2400 
2401 					if (fi.init(static_cast<s_RTF_AttrPropAdapter_Style>(pStyle),true)) {
2402 						if (_findFont(&fi) == -1)
2403 							_addFont(&fi);
2404 					}
2405 				}
2406 			}
2407 		}
2408     }
2409 
2410 	delete pStyles;
2411 }
2412 
2413 
2414 /*!
2415  * Return the style number that was assigned to the given style.
2416  * The style must be present in the style hash.
2417  */
_getStyleNumber(const PD_Style * pStyle)2418 UT_uint32 IE_Exp_RTF::_getStyleNumber(const PD_Style * pStyle)
2419 {
2420     return _getStyleNumber(pStyle->getName());
2421 }
2422 
2423 /*!
2424  * Return the style number that was assigned to the named style.
2425  * The style must be present in the style hash.
2426  */
_getStyleNumber(const gchar * szStyle)2427 UT_uint32 IE_Exp_RTF::_getStyleNumber(const gchar * szStyle)
2428 {
2429 	if(strcmp(szStyle, "Normal Clean")== 0)
2430 	{
2431 		szStyle = "Normal";
2432 	}
2433 	NumberedStyle * pns = (NumberedStyle*)m_hashStyles.pick(szStyle);
2434 	UT_ASSERT_HARMLESS(pns);
2435 	if(pns != NULL )
2436 	{
2437 		return pns->n;
2438 	}
2439 	else
2440 	{
2441 		pns = (NumberedStyle*)m_hashStyles.pick("Normal");
2442 		return pns->n;
2443 	}
2444 }
2445 
2446 /*!
2447  * Write the stylesheets group of the RTF header. Only styles that
2448  * are used by the document are written.
2449  */
_write_stylesheets(void)2450 void IE_Exp_RTF::_write_stylesheets(void)
2451 {
2452     if (getDoc()->getStyleCount() == 0) return;
2453 
2454     _rtf_nl();
2455     _rtf_open_brace();
2456     _rtf_keyword("stylesheet");
2457 
2458     UT_GenericStringMap<NumberedStyle*>::UT_Cursor hc(&m_hashStyles);
2459     const NumberedStyle * pns;
2460     for (pns = hc.first(); hc.is_valid(); pns = hc.next())
2461 	{
2462 		const PD_Style * pStyle = pns->pStyle;
2463 		_rtf_nl();
2464 		_rtf_open_brace();
2465 
2466 		if (pStyle->isCharStyle())
2467 		{
2468 			_rtf_keyword("*");
2469 			_rtf_keyword("cs", pns->n);
2470 		}
2471 		else
2472 		{
2473 			_rtf_keyword("s", pns->n);
2474 		}
2475 
2476 		_write_style_fmt(pStyle);
2477 
2478 		const PD_Style * pStyleBasedOn =  reinterpret_cast<const PD_Style *> (pStyle->getBasedOn());
2479 		// TODO: Can this really return NULL?
2480 		if (pStyleBasedOn != NULL)
2481 		{
2482 			_rtf_keyword("sbasedon", _getStyleNumber(pStyleBasedOn));
2483 		}
2484 
2485 		const PD_Style * pStyleNext = reinterpret_cast<const PD_Style *> (pStyle->getFollowedBy());
2486 		// TODO: Can this really return NULL?
2487 		if (pStyleNext != NULL)
2488 		{
2489 			_rtf_keyword("snext", _getStyleNumber(pStyleNext));
2490 		}
2491 		_rtf_pcdata(pStyle->getName(), true);
2492 		_rtf_semi();
2493 		_rtf_close_brace();
2494     }
2495 
2496     _rtf_close_brace();
2497 }
2498 
2499 /*!
2500  * Write the listatble group of the RTF header.
2501  */
_write_listtable(void)2502 void IE_Exp_RTF::_write_listtable(void)
2503 {
2504 	UT_sint32 iCount = getDoc()->getListsCount();
2505     if (iCount == 0) return;
2506 //
2507 // Openning RTF comments for the listtable
2508 //
2509     _rtf_nl();
2510     _rtf_open_brace();
2511     _rtf_keyword("*");
2512     _rtf_keyword("listtable");
2513 //
2514 // OK scan the lists in the document to build up the list info.
2515 // The first loop just builds a vector of parentless lists.
2516 //
2517 	UT_sint32 i,j =0;
2518 	bool bFoundChild = false;
2519 	fl_AutoNum * pAuto = NULL;
2520 	fl_AutoNum * pInner = NULL;
2521 	ie_exp_RTF_MsWord97ListMulti * pList97 = NULL;
2522 	for(i=0; i< iCount; i++)
2523 	{
2524 		pAuto = getDoc()->getNthList(i);
2525 		if(pAuto->getParent() == NULL)
2526 		{
2527 			bFoundChild = false;
2528 			for(j =0; (j< iCount) && !bFoundChild; j++)
2529 			{
2530 				pInner = getDoc()->getNthList(j);
2531 				if(pInner->getParentID() == pAuto->getID())
2532 //
2533 // Found a child of pList97, it must be a multi-level list.
2534 //
2535 				{
2536 					xxx_UT_DEBUGMSG(("SEVIOR: Adding %x to multi-level \n",pAuto));
2537 					m_vecMultiLevel.addItem((void *) new ie_exp_RTF_MsWord97ListMulti(pAuto));
2538 					bFoundChild = true;
2539 					break;
2540 				}
2541 			}
2542 			if(!bFoundChild)
2543 			{
2544 				xxx_UT_DEBUGMSG(("SEVIOR: Adding %x to simple \n",pAuto));
2545 				m_vecSimpleList.addItem((void *) new ie_exp_RTF_MsWord97ListSimple(pAuto));
2546 			}
2547 		}
2548 	}
2549 //
2550 // OK now fill the MultiLevel list structure.
2551 //
2552 
2553 	UT_sint32 k;
2554 	for(k=0; k < m_vecMultiLevel.getItemCount(); k++)
2555 	{
2556 		pList97 = (ie_exp_RTF_MsWord97ListMulti *) m_vecMultiLevel.getNthItem(k);
2557 //
2558 // For each level in the list RTF97 structure find the first matching
2559 // List.
2560 //
2561 // Start at level 1 (level 0 is the base)
2562 //
2563 		UT_uint32 depth=0;
2564 		bool bFoundAtPrevLevel = true;
2565 		for(depth = 1; depth < 10; depth++)
2566 		{
2567 //
2568 // Now loop through all the lists in the document and find a list whose parent
2569 // is the same as the list one level lower.
2570 //
2571 			if(!bFoundAtPrevLevel)
2572 			{
2573 				ie_exp_RTF_MsWord97List * pCur97 = new ie_exp_RTF_MsWord97List(pList97->getAuto());
2574 				xxx_UT_DEBUGMSG(("SEVIOR: Adding NULL level at depth %d \n",depth));
2575 				pList97->addLevel(depth, pCur97);
2576 			}
2577 			else
2578 			{
2579 				bFoundAtPrevLevel = false;
2580 				for(i=0; i < iCount; i++)
2581 				{
2582 					pAuto = (fl_AutoNum *) getDoc()->getNthList(i);
2583 					pInner = pAuto->getParent();
2584 					fl_AutoNum * pAutoLevel = pList97->getListAtLevel(depth-1,0)->getAuto();
2585 //
2586 // OK got it! pAuto is the one we want.
2587 //
2588 					if(pInner != NULL && pInner == pAutoLevel)
2589 					{
2590 						bFoundAtPrevLevel = true;
2591 						ie_exp_RTF_MsWord97List * pCur97 = new ie_exp_RTF_MsWord97List(pAuto);
2592 						xxx_UT_DEBUGMSG(("SEVIOR: Adding level %x at depth %d \n",pCur97,depth));
2593 						pList97->addLevel(depth, pCur97);
2594 					}
2595 				}
2596 			}
2597 			if(!bFoundAtPrevLevel)
2598 			{
2599 				ie_exp_RTF_MsWord97List * pCur97 = new ie_exp_RTF_MsWord97List(pList97->getAuto());
2600 				xxx_UT_DEBUGMSG(("SEVIOR: Adding NULL level at depth %d \n",depth));
2601 				pList97->addLevel(depth, pCur97);
2602 			}
2603 
2604 		}
2605 	}
2606 //
2607 // OK we got the simple and multi-list structures full.
2608 // Now fill the override structure.
2609 	for(i=0; i< iCount; i++)
2610 	{
2611 		pAuto = getDoc()->getNthList(i);
2612 		ie_exp_RTF_ListOveride * pOver = new ie_exp_RTF_ListOveride(pAuto);
2613 		pOver->setOverideID(i+1);
2614 		m_vecOverides.addItem((void *) pOver);
2615 	}
2616 //
2617 // OK that's everything. Now generate the RTF Header.
2618 //
2619 // MultiLevel lists
2620 //
2621 	for(k=0; k< m_vecMultiLevel.getItemCount(); k++)
2622 	{
2623 		_rtf_nl();
2624 		_output_MultiLevelRTF(getNthMultiLevel(k));
2625 	}
2626 //
2627 // Simple Lists
2628 //
2629 	for(k=0; k< m_vecSimpleList.getItemCount(); k++)
2630 	{
2631 		_rtf_nl();
2632 		_output_SimpleListRTF(getNthSimple(k));
2633 	}
2634 //
2635 // \*\listtable is done now!
2636 //
2637     _rtf_close_brace();
2638 //
2639 // Overides. Start with the \*\listoverridetable keyword.
2640 //
2641     _rtf_nl();
2642     _rtf_open_brace();
2643     _rtf_keyword("*");
2644     _rtf_keyword("listoverridetable");
2645 	for(i=0; i< m_vecOverides.getItemCount(); i++)
2646 	{
2647 		_rtf_nl();
2648 		_output_OveridesRTF(getNthOveride(i),i);
2649 	}
2650 //
2651 // Finished!
2652 //
2653     _rtf_close_brace();
2654 	_rtf_nl();
2655 }
2656 
2657 /*!
2658  * Get ith Multilevel list
2659  */
getNthMultiLevel(UT_uint32 i) const2660 ie_exp_RTF_MsWord97ListMulti * IE_Exp_RTF::getNthMultiLevel(UT_uint32 i) const
2661 {
2662 	return (ie_exp_RTF_MsWord97ListMulti *) m_vecMultiLevel.getNthItem(i);
2663 }
2664 
2665 /*!
2666  * Get ith Simple list
2667  */
getNthSimple(UT_uint32 i) const2668 ie_exp_RTF_MsWord97ListSimple *  IE_Exp_RTF::getNthSimple(UT_uint32 i) const
2669 {
2670 	return (ie_exp_RTF_MsWord97ListSimple *) m_vecSimpleList.getNthItem(i);
2671 }
2672 
2673 /*!
2674  * Get ith Overide
2675  */
getNthOveride(UT_uint32 i) const2676 ie_exp_RTF_ListOveride *  IE_Exp_RTF::getNthOveride(UT_uint32 i) const
2677 {
2678 	return (ie_exp_RTF_ListOveride *) m_vecOverides.getNthItem(i);
2679 }
2680 
2681 /*!
2682  * Get Number of multilevel lists in the document
2683  */
getMultiLevelCount(void) const2684 UT_uint32  IE_Exp_RTF::getMultiLevelCount(void) const
2685 {
2686 	return m_vecMultiLevel.getItemCount();
2687 }
2688 
2689 /*!
2690  * Get Number of simple lists in the document
2691  */
getSimpleListCount(void) const2692 UT_uint32  IE_Exp_RTF::getSimpleListCount(void) const
2693 {
2694 	return m_vecSimpleList.getItemCount();
2695 }
2696 /*!
2697  * Get Number of overides in the document
2698  */
getOverideCount(void) const2699 UT_uint32  IE_Exp_RTF::getOverideCount(void) const
2700 {
2701 	return m_vecOverides.getItemCount();
2702 }
2703 
2704 /*!
2705  * Return the the number of the overide that matches the given ID.
2706  * Returns 0 on failure to find a matching ID.
2707  */
getMatchingOverideNum(UT_uint32 ID)2708 UT_uint32 IE_Exp_RTF::getMatchingOverideNum(UT_uint32 ID)
2709 {
2710 	UT_uint32 baseid = ID;
2711 	UT_uint32 i=0;
2712 	for(i=0; i< getOverideCount(); i++)
2713 	{
2714 		ie_exp_RTF_ListOveride * pOver = getNthOveride(i);
2715 		if(pOver->doesOverideMatch(baseid))
2716 		{
2717 			return pOver->getOverideID();
2718 		}
2719 	}
2720 	return 0;
2721 }
2722 
2723 /*!
2724  * Actually output the RTF from a multi-level list
2725  \param ie_exp_RTF_MsWord97ListMulti * pMulti pointer to a multi-level list
2726  * structure.
2727  */
_output_MultiLevelRTF(ie_exp_RTF_MsWord97ListMulti * pMulti)2728 void IE_Exp_RTF::_output_MultiLevelRTF(ie_exp_RTF_MsWord97ListMulti * pMulti)
2729 {
2730 	_rtf_open_brace();
2731 	_rtf_keyword("list");
2732 #if 0
2733 	UT_uint32 tempID = UT_rand();
2734 	while(tempID < 10000)
2735 	{
2736 		tempID = UT_rand();
2737 	}
2738 #else
2739 	UT_uint32 tempID = getDoc()->getUID(UT_UniqueId::List);
2740 #endif
2741 
2742 	_rtf_keyword("listtemplateid",tempID);
2743 	UT_uint32 i = 0;
2744 	fl_AutoNum * pAuto = NULL;
2745 	ie_exp_RTF_MsWord97List * pList97 = NULL;
2746 	for(i=0; i < 9 ; i++)
2747 	{
2748 		_rtf_open_brace();
2749 		_rtf_keyword("listlevel");
2750 		pList97 = pMulti->getListAtLevel(i,0);
2751 		if(pList97 != NULL)
2752 		{
2753 //
2754 // Strategy: Dump out all the list info for the first list in each level. Then
2755 // use the overides to redefine subsequent lists at each level.
2756 //
2757 			pAuto = pList97->getAuto();
2758 			if(i==0 && pAuto->getParent() != NULL)
2759 			{
2760 				UT_ASSERT_NOT_REACHED();
2761 			}
2762 			_output_ListRTF(pAuto,i);
2763 		}
2764 		else
2765 		{
2766 			_output_ListRTF(NULL,i);
2767 		}
2768 		_rtf_close_brace();
2769 	}
2770 	_rtf_keyword("listid",pMulti->getID());
2771 	_rtf_close_brace();
2772 }
2773 
2774 
2775 /*!
2776  * This method outputs the RTF defintion of the list pointed to by pAuto
2777  */
_output_LevelText(fl_AutoNum * pAuto,UT_uint32 iLevel,UT_UCSChar bulletsym)2778 void IE_Exp_RTF::_output_LevelText(fl_AutoNum * pAuto, UT_uint32 iLevel, UT_UCSChar bulletsym)
2779 {
2780 	UT_String LevelText;
2781 	UT_String LevelNumbers;
2782 	UT_uint32 lenText;
2783 	UT_uint32 ifoundLevel=iLevel;
2784 //
2785 // Level Text and Level Numbers
2786 //
2787 	_rtf_open_brace();
2788 	_rtf_keyword("leveltext");
2789 	if(bulletsym == 0)
2790 	{
2791 		_generate_level_Text(pAuto,LevelText,LevelNumbers,lenText,ifoundLevel);
2792 		UT_String tmp;
2793 		_rtf_nonascii_hex2(lenText,tmp);
2794 		tmp += LevelText;
2795 		tmp += ";";
2796 		xxx_UT_DEBUGMSG(("SEVIOR: Final level text string is %s \n",tmp.c_str()));
2797 		write(tmp.c_str());
2798 		_rtf_close_brace();
2799 		_rtf_open_brace();
2800 		_rtf_keyword("levelnumbers");
2801 		write(LevelNumbers.c_str());
2802 		write(";");
2803 	}
2804 	else
2805 	{
2806 		_rtf_keyword("'01");
2807 		std::string sBullet = UT_std_string_sprintf("\\u%d",(UT_sint32) bulletsym);
2808 		write(sBullet.c_str());
2809 		write(" ;");
2810 		_rtf_close_brace();
2811 		_rtf_open_brace();
2812 		_rtf_keyword("levelnumbers");
2813 		write(";");
2814 	}
2815 	_rtf_close_brace();
2816 }
2817 
2818 /*!
2819  * This method generates the leveltext and levelnumber strings.HOWEVER it does
2820  * not generate the leading text string which is the length of the string. It
2821  * is the responsibility of the calling routine to this.
2822  */
_generate_level_Text(fl_AutoNum * pAuto,UT_String & LevelText,UT_String & LevelNumbers,UT_uint32 & lenText,UT_uint32 & ifoundLevel)2823 void IE_Exp_RTF::_generate_level_Text(fl_AutoNum * pAuto,UT_String & LevelText,UT_String &LevelNumbers, UT_uint32 & lenText, UT_uint32 & ifoundLevel)
2824 {
2825 	xxx_UT_DEBUGMSG(("SEVIOR: pAuto %x \n",pAuto));
2826 	if(pAuto)
2827 	{
2828 		xxx_UT_DEBUGMSG(("SEVIOR: pAuto-getParent() %x \n",pAuto->getParent()));
2829 	}
2830 	if(pAuto && (pAuto->getParent() == NULL))
2831 	{
2832 		UT_String LeftSide = pAuto->getDelim();
2833 		UT_String RightSide;
2834 		_get_LeftRight_Side(LeftSide,RightSide);
2835 		xxx_UT_DEBUGMSG(("SEVIOR: Top - leftside = %s rightside = %s \n",LeftSide.c_str(),RightSide.c_str()));
2836 		UT_String place;
2837 		UT_uint32 locPlace = (UT_uint32) LeftSide.size();
2838 		_rtf_nonascii_hex2(locPlace+1,place);
2839 		LevelNumbers = place;
2840 		ifoundLevel = 1;
2841 		LevelText.clear();
2842 		if(LeftSide.size() > 0)
2843 		{
2844 			LevelText = LeftSide;
2845 		}
2846 		place.clear();
2847 		_rtf_nonascii_hex2(ifoundLevel-1,place);
2848 		LevelText += place;
2849 		if(RightSide.size() > 0)
2850 		{
2851 			LevelText += RightSide;
2852 		}
2853 		lenText = LeftSide.size() + RightSide.size() + 1;
2854 		xxx_UT_DEBUGMSG(("SEVIOR: Level %d LevelText %s  \n",ifoundLevel,LevelText.c_str()));
2855 		return;
2856 	}
2857 	else if((pAuto != NULL) && ( pAuto->getParent() != NULL))
2858 	{
2859 		_generate_level_Text(pAuto->getParent(),LevelText,LevelNumbers,lenText,
2860 							 ifoundLevel);
2861 		UT_String LeftSide = pAuto->getDelim();
2862 		UT_String RightSide;
2863 		_get_LeftRight_Side(LeftSide,RightSide);
2864 		UT_String str;
2865 //
2866 // FIXME. Implement this when level decimal works
2867 
2868 		if(pAuto->getParent()->getDecimal() && *(pAuto->getParent()->getDecimal()))
2869 		{
2870 			if(RightSide.size() > 0)
2871 			{
2872 				xxx_UT_DEBUGMSG(("SEVIOR: RightSide =%s last char = %c \n",RightSide.c_str(),RightSide[RightSide.size()-1]));
2873 			}
2874 			if(RightSide.size()== 0)
2875 			{
2876 				RightSide += pAuto->getParent()->getDecimal();
2877 			}
2878 			else if(RightSide[RightSide.size()-1] != '.')
2879 			{
2880 				RightSide += pAuto->getParent()->getDecimal();
2881 			}
2882 		}
2883 		ifoundLevel++;
2884 		UT_uint32 locPlace = lenText + LeftSide.size();
2885 		str.clear();
2886 		_rtf_nonascii_hex2(locPlace+1,str);
2887 		LevelNumbers += str;
2888 		lenText = lenText + LeftSide.size() + RightSide.size() + 1;
2889 		str.clear();
2890 		_rtf_nonascii_hex2(ifoundLevel-1,str);
2891 		LevelText += LeftSide;
2892 		LevelText += str;
2893 		LevelText += RightSide;
2894 		xxx_UT_DEBUGMSG(("SEVIOR: Level %d LevelText %s  \n",ifoundLevel,LevelText.c_str()));
2895 		return;
2896 	}
2897 	else
2898 	{
2899 		UT_uint32 i=0;
2900 		lenText = 0;
2901 		LevelText.clear();
2902 		LevelNumbers.clear();
2903 		UT_String str;
2904 		for(i=0; i<= ifoundLevel; i++)
2905 		{
2906 			str.clear();
2907 			_rtf_nonascii_hex2(i,str);
2908 			LevelText += str;
2909 			str.clear();
2910 			_rtf_nonascii_hex2(lenText+1,str);
2911 			LevelNumbers += str;
2912 			if(i<ifoundLevel)
2913 			{
2914 				LevelText += ".";
2915 				lenText += 2;
2916 			}
2917 			else
2918 			{
2919 				lenText += 1;
2920 			}
2921 		}
2922 	}
2923 	return;
2924 }
2925 
2926 
2927 /*!
2928  * This method splits the abiword List delim string into text to the left
2929  * and right of the "%L" marker. The input string is in LeftSide.
2930  */
_get_LeftRight_Side(UT_String & LeftSide,UT_String & RightSide)2931 void IE_Exp_RTF::_get_LeftRight_Side(UT_String & LeftSide, UT_String & RightSide)
2932 {
2933 	const char * psz = strstr(LeftSide.c_str(),"%L");
2934 	xxx_UT_DEBUGMSG(("SEVIOR: Substring = %s Total is %s \n",psz,LeftSide.c_str()));
2935 	if(psz != NULL)
2936 	{
2937 		UT_uint32 index = (UT_uint32) (psz - LeftSide.c_str());
2938 		UT_uint32 len = (UT_uint32) strlen(LeftSide.c_str());
2939 		xxx_UT_DEBUGMSG(("SEVIOR: index = %d len =%d \n",index,len));
2940 		if(index+2 < len)
2941 		{
2942 			RightSide = LeftSide.substr(index+2,len);
2943 		}
2944 		else
2945 		{
2946 			RightSide.clear();
2947 		}
2948 		if(index > 0)
2949 		{
2950 			LeftSide = LeftSide.substr(0,index);
2951 		}
2952 		else
2953 		{
2954 			LeftSide.clear();
2955 		}
2956 	}
2957 	else
2958 	{
2959 		RightSide.clear();
2960 	}
2961 }
2962 
2963 /*!
2964  * This method outputs the RTF defintion of the list pointed to by pAuto
2965  */
_output_ListRTF(fl_AutoNum * pAuto,UT_uint32 iLevel)2966 void IE_Exp_RTF::_output_ListRTF(fl_AutoNum * pAuto, UT_uint32 iLevel)
2967 {
2968 // List Type
2969 	UT_sint32 Param = 0;
2970 	UT_UCSChar bulletsym=0;
2971 	FL_ListType lType = NUMBERED_LIST;
2972 	if(pAuto != NULL)
2973 	{
2974 		lType = pAuto->getType();
2975 	}
2976 	else
2977 	{
2978 		lType = NUMBERED_LIST;
2979 	}
2980 	switch(lType)
2981 	{
2982 	default:
2983 	case NUMBERED_LIST:
2984 		Param = 0;
2985 		break;
2986 	case UPPERROMAN_LIST:
2987 		Param = 1;
2988 		break;
2989 	case LOWERROMAN_LIST:
2990 		Param = 2;
2991 		break;
2992 	case UPPERCASE_LIST:
2993 		Param = 3;
2994 		break;
2995 	case HEBREW_LIST:
2996 		Param = 45;
2997 		break;
2998 	case LOWERCASE_LIST:
2999 		Param = 4;
3000 		break;
3001 	case BULLETED_LIST:
3002 		Param = 23;
3003 		bulletsym = 0x2022;
3004 		break;
3005 	case DASHED_LIST:
3006 		Param = 23;
3007 		bulletsym = 0x002D;
3008 		break;
3009 	case SQUARE_LIST:
3010 		Param = 23;
3011 		bulletsym = 0x25A0;
3012 		break;
3013 	case TRIANGLE_LIST:
3014 		Param = 23;
3015 		bulletsym = 0x25B2;
3016 		break;
3017 	case DIAMOND_LIST:
3018 		Param = 23;
3019 		bulletsym = 0x2666;
3020 		break;
3021 	case STAR_LIST:
3022 		Param = 23;
3023 		bulletsym = 0x2733;
3024 		break;
3025 	case IMPLIES_LIST:
3026 		Param = 23;
3027 		bulletsym = 0x21D2;
3028 		break;
3029 	case TICK_LIST:
3030 		Param = 23;
3031 		bulletsym = 0x2713;
3032 		break;
3033 	case BOX_LIST:
3034 		Param = 23;
3035 		bulletsym = 0x2752;
3036 		break;
3037 	case HAND_LIST:
3038 		Param = 23;
3039 		bulletsym = 0x261E;
3040 		break;
3041 	case HEART_LIST:
3042 		Param = 23;
3043 		bulletsym = 0x2665;
3044 		break;
3045 	case ARROWHEAD_LIST:
3046 		Param = 23;
3047 		bulletsym = 0x27A3;
3048 		break;
3049 	}
3050 	_rtf_keyword("levelnfc",Param);
3051 	UT_sint32 startParam = 0;
3052 	if(pAuto)
3053 	{
3054 		startParam  = pAuto->getStartValue32();
3055 	}
3056 	else
3057 	{
3058 		startParam = 1;
3059 	}
3060 	_rtf_keyword("levelstartat",startParam);
3061 	_rtf_keyword("levelspace",0);
3062 	_rtf_keyword("levelfollow",0);
3063 	if(pAuto == NULL)
3064 	{
3065 		float marg = LIST_DEFAULT_INDENT;
3066 		float indent =  (float)LIST_DEFAULT_INDENT_LABEL;
3067 		UT_String smarg;
3068 		UT_String sindent;
3069 		marg = (((float) iLevel) +1.0f) * marg;
3070 		UT_String_sprintf(smarg,"%fin",marg);
3071 		UT_String_sprintf(sindent,"%fin",indent);
3072 		_rtf_keyword_ifnotdefault_twips("li",(char*)smarg.c_str(),0);
3073 		_rtf_keyword_ifnotdefault_twips("fi",(char*)sindent.c_str(),0);
3074 	}
3075 //
3076 // Output the indents and alignments. Use the first sdh to get these.
3077 //
3078 	else
3079 	{
3080 		pf_Frag_Strux* sdh = pAuto->getFirstItem();
3081 		const char * szIndent = NULL;
3082 		const char * szAlign = NULL;
3083 		// TODO -- we have a problem here; props and attrs are, due to revisions, view dependent and
3084 		// we have no access to the view, so we will assume that revisions are showing and will ask
3085 		// for the cumulative result of all of them (revision level PD_MAX_REVISION)
3086 		//
3087 		if(sdh != NULL)
3088 		{
3089 			bool bres = getDoc()->getPropertyFromSDH(sdh,true,PD_MAX_REVISION,"text-indent",&szIndent);
3090 			if(bres)
3091 			{
3092 				_rtf_keyword_ifnotdefault_twips("fi",(char*)szIndent,0);
3093 			}
3094 			bres = getDoc()->getPropertyFromSDH(sdh,true,PD_MAX_REVISION,"margin-left",&szAlign);
3095 			if(bres)
3096 			{
3097 				_rtf_keyword_ifnotdefault_twips("li",(char*)szAlign,0);
3098 			}
3099 		}
3100 	}
3101 
3102 // Leveltext and levelnumbers
3103 //
3104 	_output_LevelText(pAuto,iLevel,bulletsym);
3105 }
3106 
3107 /*!
3108  * Actually output the RTF from a Simple list
3109  \param ie_exp_RTF_MsWord97ListSimple * pSimple pointer to a Simple list
3110  * structure.
3111  */
_output_SimpleListRTF(ie_exp_RTF_MsWord97ListSimple * pSimple)3112 void IE_Exp_RTF::_output_SimpleListRTF(ie_exp_RTF_MsWord97ListSimple * pSimple)
3113 {
3114 	_rtf_open_brace();
3115 	_rtf_keyword("list");
3116 #if 0
3117 	UT_uint32 tempID = UT_rand();
3118 	while(tempID < 10000)
3119 	{
3120 		tempID = UT_rand();
3121 	}
3122 #else
3123 	UT_uint32 tempID = getDoc()->getUID(UT_UniqueId::List);
3124 #endif
3125 	_rtf_keyword("listtemplateid",tempID);
3126 	_rtf_keyword("listsimple");
3127 	fl_AutoNum * pAuto = pSimple->getAuto();
3128 	_rtf_open_brace();
3129 	_rtf_keyword("listlevel");
3130 //
3131 // Strategy: Dump out all the list info for the first list in each level.
3132 //
3133 	_output_ListRTF(pAuto,0);
3134 	_rtf_close_brace();
3135 	_rtf_keyword("listid",pSimple->getID());
3136 	_rtf_close_brace();
3137 }
3138 
3139 /*!
3140  * Actually output the RTF from an Overide
3141  \param ie_exp_RTF_Overide * pOver pointer to an Overide definition
3142  */
_output_OveridesRTF(ie_exp_RTF_ListOveride * pOver,UT_uint32)3143 void IE_Exp_RTF::_output_OveridesRTF(ie_exp_RTF_ListOveride * pOver, UT_uint32 /*iOver*/)
3144 {
3145 	_rtf_open_brace();
3146 	_rtf_keyword("listoverride");
3147 	_rtf_keyword("listoverridecount",0);
3148 	fl_AutoNum * pAuto = pOver->getAutoNum();
3149 	fl_AutoNum * pTop = pAuto;
3150 	while(pTop->getParent())
3151 	{
3152 		pTop = pTop->getParent();
3153 	}
3154 	_rtf_keyword("listid",pTop->getID());
3155 //
3156 // Strategy: Dump out all the list info for the first list in each level.
3157 //
3158 	_output_ListRTF(pAuto,0);
3159 	_rtf_keyword("ls",pOver->getOverideID());
3160 	_rtf_close_brace();
3161 }
3162 
_write_rtf_trailer(void)3163 bool IE_Exp_RTF::_write_rtf_trailer(void)
3164 {
3165 	while (m_braceLevel>0)
3166 		_rtf_close_brace();
3167 	return (m_error == 0);
3168 }
3169 
3170 /*!
3171  * Find a font in the font table. Return the index of the font. If
3172  * it is not found, return -1.
3173  */
_findFont(const _rtf_font_info * pfi) const3174 UT_sint32 IE_Exp_RTF::_findFont(const _rtf_font_info * pfi) const
3175 {
3176 	UT_return_val_if_fail(pfi, -1);
3177 
3178 	UT_uint32 k;
3179 	UT_uint32 kLimit = m_vecFonts.getItemCount();
3180 
3181 	for (k=0; k<kLimit; k++)
3182 	{
3183 		const _rtf_font_info * pk = (const _rtf_font_info *)m_vecFonts.getNthItem(k);
3184 		if (pk->_is_same(*pfi))
3185 			return k;
3186 	}
3187 
3188 	return -1;
3189 }
3190 
_findFont(const s_RTF_AttrPropAdapter * apa) const3191 UT_sint32 IE_Exp_RTF::_findFont(const s_RTF_AttrPropAdapter * apa) const
3192 {
3193 	static UT_sint32 ifont = 0;
3194 
3195 	_rtf_font_info fi;
3196 
3197 	if (fi.init(*apa)) {
3198 		ifont = _findFont(&fi);
3199 		return ifont;
3200 	}
3201 
3202 	return -1;
3203 }
3204 
3205 /*!
3206  * Add a font to the font table. The font must not be present
3207  * in the font table at the start of the call.
3208  */
_addFont(const _rtf_font_info * pfi)3209 void IE_Exp_RTF::_addFont(const _rtf_font_info * pfi)
3210 {
3211 	UT_return_if_fail(pfi && (_findFont(pfi)==-1));
3212 
3213 	_rtf_font_info * pNew = new _rtf_font_info (*pfi);
3214 
3215 	if (pNew)
3216 		m_vecFonts.addItem(pNew);
3217 }
3218 
3219 /*
3220  * Convert a UCS4 string into an ASCII string by using \uXXXXX
3221  * sequences to escape any non-ascii data.
3222  *
3223  * returns true if escaping was necesary and false otherwise.
3224  * iAltChars specifies the number of alternative characters to provide.
3225  */
s_escapeString(UT_UTF8String & sOutStr,UT_UCS4String & sInStr,UT_uint32 iAltChars)3226 bool IE_Exp_RTF::s_escapeString(UT_UTF8String &sOutStr,
3227                                 UT_UCS4String &sInStr,
3228                                 UT_uint32 iAltChars)
3229 {
3230 	sOutStr = "";  // Empty output string.
3231 	bool bRetVal = false;
3232 
3233 	// Loop for each character in the UCS-4 string checking for
3234 	// non-ascii characters.
3235 	for (UT_uint32 i=0; i<sInStr.size(); i++)
3236 	{
3237 		// If an ASCII character append to output string.
3238 		if (sInStr[i] <= 0x7f)
3239 		{
3240 			sOutStr += sInStr[i];
3241 			continue;
3242 		}
3243 		// If a code point representable in UCS-2 (without surrogates)
3244 		// write out a \uXXXXX escaped sequence.
3245 		if (sInStr[i] > 0x7f && sInStr[i] <=0xffff)
3246 		{
3247 			bRetVal = true;
3248 			// RTF is limited to +-32K ints, therefore negative numbers
3249 			// are needed to represent some unicode chars.
3250 			signed short tmp = (signed short) ((unsigned short) sInStr[i]);
3251 			// Append a \uXXXXX sequence.
3252 			sOutStr += UT_UTF8String_sprintf("\\u%d",tmp);
3253 			// Append alternative chars.
3254 			if (iAltChars)
3255 				sOutStr += " ";
3256 			for (UT_uint32 j=0; j<iAltChars; j++)
3257 				sOutStr += "?";
3258 			continue;
3259 		}
3260 		// TODO: Strictly speaking we should be using the UTF-16 encoding,
3261 		// since we ought to represent unicode values above 0xffff as surrogate
3262 		// pairs. However, the abi util classes lack a UT_UCS2String class.
3263 		// We could use UT_convert() to convert to UCS-2, but that's rather messy.
3264 		// In any case, unicode points above 0xffff are rather rare, so for the time
3265 		// being we just assert and replace such code points with a "?".
3266 		UT_ASSERT_HARMLESS(UT_NOT_IMPLEMENTED);
3267 		sOutStr += "?";
3268 	}
3269 
3270 	return bRetVal;
3271 }
3272 
3273 /*
3274  * Various access functions for above.
3275  */
s_escapeString(UT_UTF8String & sOutStr,const char * szInStr,UT_uint32 iSize,UT_uint32 iAltChars)3276 bool IE_Exp_RTF::s_escapeString(UT_UTF8String &sOutStr,
3277                                 const char * szInStr,
3278                                 UT_uint32 iSize,  /* 0 == NULL terminated */
3279                                 UT_uint32 iAltChars)
3280 {
3281 	UT_UCS4String sUCS4InStr(szInStr, iSize);
3282 	return IE_Exp_RTF::s_escapeString(sOutStr, sUCS4InStr, iAltChars);
3283 }
3284 
s_escapeString(std::string & outStr,const std::string & inStr,UT_uint32 iAltChars)3285 bool IE_Exp_RTF::s_escapeString( std::string& outStr, const std::string& inStr,
3286                                  UT_uint32 iAltChars )
3287 {
3288     UT_UTF8String sOutStr;
3289     bool ret = s_escapeString( sOutStr, inStr.c_str(), inStr.length(), iAltChars );
3290     outStr = sOutStr.utf8_str();
3291     return ret;
3292 }
3293 
s_escapeString(const std::string & inStr,UT_uint32 iAltChars)3294 std::string IE_Exp_RTF::s_escapeString( const std::string& inStr, UT_uint32 iAltChars )
3295 {
3296     UT_UTF8String sOutStr;
3297     /*bool ret =*/ s_escapeString( sOutStr, inStr.c_str(), inStr.length(), iAltChars );
3298     return (std::string)sOutStr.utf8_str();
3299 }
3300 
3301 
s_escapeXMLString(const std::string & inStr)3302 std::string IE_Exp_RTF::s_escapeXMLString( const std::string& inStr )
3303 {
3304     //
3305     // &7d; is }
3306     //
3307     std::string s = inStr;
3308     s = replace_all( s, "&7d;",  "&7d;&7d;" );
3309     s = replace_all( s, "}",  "&7d;" );
3310 //    s = s_escapeString( s );
3311     return s;
3312 }
3313 
3314 
3315 
_rtf_font_info()3316 _rtf_font_info::_rtf_font_info()
3317 {
3318 	// TODO: set these to some default bogus values
3319 }
3320 
init(const s_RTF_AttrPropAdapter & apa,bool bDoFieldFont)3321 bool _rtf_font_info::init(const s_RTF_AttrPropAdapter & apa, bool bDoFieldFont)
3322 {
3323 	// Not a typo. The AbiWord "font-family" property is what RTF
3324 	// calls font name. It has values like "Courier New".
3325 	const char * szName = NULL;
3326 	if(!bDoFieldFont)
3327 	{
3328 		szName = apa.getProperty("font-family");
3329 		if(szName != NULL)
3330 		{
3331 			m_szName = szName;
3332 		}
3333 	}
3334 	else
3335 	{
3336 		szName = apa.getProperty("field-font");
3337 		if(szName != NULL)
3338 		{
3339 			m_szName = szName;
3340 		}
3341 	}
3342 	if (szName == NULL || strcmp(szName, "NULL") == 0)  // Field-font is "NULL" when there is no special field-font.
3343 	{                                                      // We don't want it in the \fonttbl
3344 		return false;
3345 	}
3346 
3347 	static const char * t_ff[] = { "fnil", "froman", "fswiss", "fmodern", "fscript", "fdecor", "ftech", "fbidi" };
3348 	GR_Font::FontFamilyEnum ff;
3349 	GR_Font::FontPitchEnum fp;
3350 	bool tt;
3351 	GR_Font::s_getGenericFontProperties((char*)szName, &ff, &fp, &tt);
3352 
3353 	if ((ff >= 0) && (ff < (int)G_N_ELEMENTS(t_ff)))
3354 		szFamily = t_ff[ff];
3355 	else
3356 		szFamily = t_ff[GR_Font::FF_Unknown];
3357 	nCharset = XAP_EncodingManager::get_instance()->getWinCharsetCode();
3358 	nPitch = fp;
3359 	fTrueType = tt;
3360 
3361 	return true;
3362 }
3363 
~_rtf_font_info(void)3364 _rtf_font_info::~_rtf_font_info(void)
3365 {
3366 }
3367 
init(const char * szFontName)3368 bool _rtf_font_info::init(const char * szFontName)
3369 {
3370     // Not a typo. The AbiWord "font-family" property is what RTF
3371     // calls font name. It has values like "Courier New".
3372     if (szFontName == NULL)
3373 	{
3374 		return false;
3375 	}
3376 
3377 	m_szName = szFontName;
3378 
3379     static const char * t_ff[] = { "fnil", "froman", "fswiss", "fmodern", "fscript", "fdecor", "ftech", "fbidi" };
3380     GR_Font::FontFamilyEnum ff;
3381     GR_Font::FontPitchEnum fp;
3382     bool tt;
3383     GR_Font::s_getGenericFontProperties(m_szName.c_str(), &ff, &fp, &tt);
3384 
3385     if ((ff >= 0) && (ff < (int)G_N_ELEMENTS(t_ff)))
3386 		szFamily = t_ff[ff];
3387     else
3388 		szFamily = t_ff[GR_Font::FF_Unknown];
3389     nCharset = XAP_EncodingManager::get_instance()->getWinCharsetCode();
3390     nPitch = fp;
3391     fTrueType = tt;
3392 
3393 	return true;
3394 }
3395 
3396 /*!
3397  * True if the two objects represent the same RTF font.
3398  */
_is_same(const _rtf_font_info & fi) const3399 bool _rtf_font_info::_is_same(const _rtf_font_info & fi) const
3400 {
3401 	bool bMatchFontFamily = false;
3402 	bool bMatchFontName = true;
3403 	if(szFamily && *szFamily && fi.szFamily && *fi.szFamily)
3404 	{
3405 		bMatchFontFamily =  strcmp(szFamily, fi.szFamily) == 0;
3406 	}
3407 	else if ( szFamily == fi.szFamily) // Both null pointers
3408 	{
3409 		bMatchFontFamily = true;
3410 	}
3411 	else if (  szFamily && fi.szFamily && *szFamily == *fi.szFamily) // Both pointer to NULLs
3412 	{
3413 		bMatchFontFamily = true;
3414 	}
3415 	if((m_szName.size() > 0) && (fi.m_szName.size() >0))
3416 	{
3417 		bMatchFontName =  strcmp(m_szName.c_str(), fi.m_szName.c_str()) == 0;
3418 	}
3419 	else if ( m_szName.size()  == fi.m_szName.size()) // Both null pointers
3420 	{
3421 		bMatchFontName = true;
3422 	}
3423     return bMatchFontFamily
3424 	&& nCharset == fi.nCharset
3425 	&& nPitch == fi.nPitch
3426 	&& bMatchFontName
3427 	&& fTrueType == fi.fTrueType;
3428 }
3429 
ie_exp_RTF_MsWord97List(fl_AutoNum * pAuto)3430 ie_exp_RTF_MsWord97List::ie_exp_RTF_MsWord97List(fl_AutoNum * pAuto)
3431 {
3432 	m_pAutoNum = pAuto;
3433 	m_Id = pAuto->getID();
3434 }
3435 
~ie_exp_RTF_MsWord97List(void)3436 ie_exp_RTF_MsWord97List::~ie_exp_RTF_MsWord97List(void)
3437 {
3438 }
3439 
ie_exp_RTF_MsWord97ListSimple(fl_AutoNum * pAuto)3440 ie_exp_RTF_MsWord97ListSimple::ie_exp_RTF_MsWord97ListSimple(fl_AutoNum * pAuto)
3441 	: ie_exp_RTF_MsWord97List( pAuto)
3442 {
3443 }
3444 
3445 
~ie_exp_RTF_MsWord97ListSimple(void)3446 ie_exp_RTF_MsWord97ListSimple::~ie_exp_RTF_MsWord97ListSimple(void)
3447 {
3448 }
3449 
3450 
ie_exp_RTF_MsWord97ListMulti(fl_AutoNum * pAuto)3451 ie_exp_RTF_MsWord97ListMulti::ie_exp_RTF_MsWord97ListMulti(fl_AutoNum * pAuto)
3452 	: ie_exp_RTF_MsWord97List( pAuto)
3453 {
3454 	UT_uint32 i = 0;
3455 	for(i=0; i < 9 ; i++)
3456 	{
3457 		m_vLevels[i] = NULL;
3458 	}
3459 	addLevel(0, (ie_exp_RTF_MsWord97List *) this);
3460 }
3461 
3462 
~ie_exp_RTF_MsWord97ListMulti(void)3463 ie_exp_RTF_MsWord97ListMulti::~ie_exp_RTF_MsWord97ListMulti(void)
3464 {
3465 	UT_uint32 i = 0;
3466 	delete m_vLevels[0];
3467 	for(i=1; i < 9 ; i++)
3468 	{
3469 		if(m_vLevels[i] != NULL)
3470 		{
3471 			UT_Vector * pV = m_vLevels[i];
3472 			UT_VECTOR_PURGEALL(ie_exp_RTF_MsWord97List *, (*pV));
3473 			delete pV;
3474 			m_vLevels[i]  = NULL;
3475 		}
3476 	}
3477 }
3478 
3479 /*!
3480  * Add a list to a level
3481 \param iLevel to add the list too
3482 \param ie_exp_RTF_MsWord97List * pList97 list to added at this level
3483  */
addLevel(UT_uint32 iLevel,ie_exp_RTF_MsWord97List * pList97)3484 void ie_exp_RTF_MsWord97ListMulti::addLevel(UT_uint32 iLevel, ie_exp_RTF_MsWord97List * pList97)
3485 {
3486 	if(iLevel > 8)
3487 	{
3488 		iLevel = 8;
3489 	}
3490 	if(m_vLevels[iLevel] == NULL)
3491 	{
3492 		UT_Vector * pVecList97 = new UT_Vector;
3493 		pVecList97->addItem((void *) pList97);
3494 		m_vLevels[iLevel] = pVecList97;
3495 		pVecList97->addItem((void *) pList97);
3496 	}
3497 	else
3498 	{
3499 		m_vLevels[iLevel]->addItem((void *) pList97);
3500 	}
3501 }
3502 
3503 /*!
3504  * Return the nthList List at level iLevel
3505  \param iLevel the level which we want to lists for
3506  \param nthList the list at the level we want
3507  */
getListAtLevel(UT_uint32 iLevel,UT_uint32 nthList)3508 ie_exp_RTF_MsWord97List * ie_exp_RTF_MsWord97ListMulti::getListAtLevel(UT_uint32 iLevel, UT_uint32 nthList)
3509 {
3510 	if(iLevel > 8)
3511 	{
3512 		iLevel = 8;
3513 	}
3514 	if(m_vLevels[iLevel] == NULL)
3515 	{
3516 		return NULL;
3517 	}
3518 	UT_uint32 icount = m_vLevels[iLevel]->getItemCount();
3519 	if(icount > nthList)
3520 	{
3521 		ie_exp_RTF_MsWord97List * pList97 = (ie_exp_RTF_MsWord97List * ) m_vLevels[iLevel]->getNthItem(nthList);
3522 		return pList97;
3523 	}
3524 	else
3525 	{
3526 		return NULL;
3527 	}
3528 }
3529 
3530 
3531 /*!
3532  * Return the listID of the first list element at the level that contains
3533  * the list that matches listID
3534  \param listID the listID we're looking for.
3535  \retval the List ID number of the first list on the level that contains
3536  the ID. Return 0 if there is no match in the structure.
3537  */
getMatchingID(UT_uint32 listID)3538 UT_uint32 ie_exp_RTF_MsWord97ListMulti::getMatchingID(UT_uint32 listID)
3539 {
3540 	UT_uint32 i;
3541 	UT_sint32 j;
3542 	ie_exp_RTF_MsWord97List * pList97 = NULL;
3543 	bool bFound = false;
3544 	UT_uint32 foundID = 0;
3545 	UT_uint32 firstID = 0;
3546 	for(i=0; (i < 8) && !bFound; i++)
3547 	{
3548 		for(j=0; m_vLevels[i] && (j < m_vLevels[i]->getItemCount()) && !bFound; j++)
3549 		{
3550 			pList97 = (ie_exp_RTF_MsWord97List *) m_vLevels[i]->getNthItem(j);
3551 			if(j==0)
3552 			{
3553 				firstID = pList97->getID();
3554 			}
3555 			bFound = pList97->getID() == listID;
3556 			if(bFound)
3557 			{
3558 				foundID = firstID;
3559 			}
3560 		}
3561 	}
3562 	return foundID;
3563 }
3564 
ie_exp_RTF_ListOveride(fl_AutoNum * pAuto)3565 ie_exp_RTF_ListOveride::ie_exp_RTF_ListOveride(fl_AutoNum * pAuto)
3566 {
3567 	m_pAutoNum = pAuto;
3568 	m_AbiListID = pAuto->getID();
3569 }
3570 
3571 
~ie_exp_RTF_ListOveride(void)3572 ie_exp_RTF_ListOveride::~ie_exp_RTF_ListOveride(void)
3573 {
3574 }
3575 
3576 
3577 
3578 
3579 
3580 
3581 
3582