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, ¤t, &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