1 /* AbiWord
2 * Copyright (C) 2001 AbiSource, Inc.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301 USA.
18 */
19
20 #include <stdlib.h>
21 #include <string.h>
22
23 #include "ut_string.h"
24 #include "ut_bytebuf.h"
25 #include "ut_base64.h"
26 #include "ut_units.h"
27 #include "ut_wctomb.h"
28 #include "pt_Types.h"
29 #include "ie_exp_HRText.h"
30 #include "pd_Document.h"
31 #include "pp_AttrProp.h"
32 #include "px_ChangeRecord.h"
33 #include "px_CR_Object.h"
34 #include "px_CR_Span.h"
35 #include "px_CR_Strux.h"
36 #include "xap_EncodingManager.h"
37 #include "ut_debugmsg.h"
38 #include "ut_string_class.h"
39 #include "ut_hash.h"
40 #include "xap_Module.h"
41
42 #ifdef ABI_PLUGIN_BUILTIN
43 #define abi_plugin_register abipgn_hrtext_register
44 #define abi_plugin_unregister abipgn_hrtext_unregister
45 #define abi_plugin_supports_version abipgn_hrtext_supports_version
46 // dll exports break static linking
47 #define ABI_BUILTIN_FAR_CALL extern "C"
48 #else
49 #define ABI_BUILTIN_FAR_CALL ABI_FAR_CALL
50 ABI_PLUGIN_DECLARE("HRText")
51 #endif
52
53 // our delimiters
54 #define BOLD_DELIM "*"
55 #define ITALIC_DELIM "/"
56 #define UNDERLINE_DELIM "_"
57 #define UNDERLINE_DELIM_CHAR '_'
58 #define LIST_DELIM "* "
59 #define BLOCK_DELIM "| "
60 #define PLAIN_DELIM "| "
61 #define SUPERSCRIPT_DELIM "^"
62 #define SUBSCRIPT_DELIM "_"
63
64 /*****************************************************************/
65 /*****************************************************************/
66
67 // completely generic code to allow this to be a plugin
68
69 // we use a reference-counted sniffer
70 static IE_Exp_HRText_Sniffer * m_sniffer = 0;
71
72 ABI_BUILTIN_FAR_CALL
abi_plugin_register(XAP_ModuleInfo * mi)73 int abi_plugin_register (XAP_ModuleInfo * mi)
74 {
75
76 if (!m_sniffer)
77 {
78 m_sniffer = new IE_Exp_HRText_Sniffer ();
79 }
80
81 mi->name = "HRText Exporter";
82 mi->desc = "Export HRText Documents";
83 mi->version = ABI_VERSION_STRING;
84 mi->author = "Abi the Ant";
85 mi->usage = "No Usage";
86
87 IE_Exp::registerExporter (m_sniffer);
88 return 1;
89 }
90
91 ABI_BUILTIN_FAR_CALL
abi_plugin_unregister(XAP_ModuleInfo * mi)92 int abi_plugin_unregister (XAP_ModuleInfo * mi)
93 {
94 mi->name = 0;
95 mi->desc = 0;
96 mi->version = 0;
97 mi->author = 0;
98 mi->usage = 0;
99
100 UT_ASSERT (m_sniffer);
101
102 IE_Exp::unregisterExporter (m_sniffer);
103 delete m_sniffer;
104 m_sniffer = 0;
105
106 return 1;
107 }
108
109 ABI_BUILTIN_FAR_CALL
abi_plugin_supports_version(UT_uint32,UT_uint32,UT_uint32)110 int abi_plugin_supports_version (UT_uint32 /*major*/, UT_uint32 /*minor*/,
111 UT_uint32 /*release*/)
112 {
113 return 1;
114 }
115
116 /*****************************************************************/
117 /*****************************************************************/
118
IE_Exp_HRText_Sniffer()119 IE_Exp_HRText_Sniffer::IE_Exp_HRText_Sniffer () :
120 IE_ExpSniffer("AbiHRText::Text/human-readable (NWS)")
121 {
122 //
123 }
124
recognizeSuffix(const char * szSuffix)125 bool IE_Exp_HRText_Sniffer::recognizeSuffix(const char * szSuffix)
126 {
127 return (!g_ascii_strcasecmp(szSuffix,".nws"));
128 }
129
constructExporter(PD_Document * pDocument,IE_Exp ** ppie)130 UT_Error IE_Exp_HRText_Sniffer::constructExporter(PD_Document * pDocument,
131 IE_Exp ** ppie)
132 {
133 IE_Exp_HRText * p = new IE_Exp_HRText(pDocument);
134 *ppie = p;
135 return UT_OK;
136 }
137
getDlgLabels(const char ** pszDesc,const char ** pszSuffixList,IEFileType * ft)138 bool IE_Exp_HRText_Sniffer::getDlgLabels(const char ** pszDesc,
139 const char ** pszSuffixList,
140 IEFileType * ft)
141 {
142 *pszDesc = "Newsgroup Formatted Text (.nws)";
143 *pszSuffixList = "*.nws";
144 *ft = getFileType();
145 return true;
146 }
147
148 /*****************************************************************/
149 /*****************************************************************/
150
IE_Exp_HRText(PD_Document * pDocument)151 IE_Exp_HRText::IE_Exp_HRText(PD_Document * pDocument)
152 : IE_Exp(pDocument), m_pListener(0)
153 {
154 m_error = UT_OK;
155 }
156
~IE_Exp_HRText()157 IE_Exp_HRText::~IE_Exp_HRText()
158 {
159 }
160
161 /*****************************************************************/
162 /*****************************************************************/
163
164 #define BT_NORMAL 1
165 #define BT_HEADING1 2
166 #define BT_HEADING2 3
167 #define BT_HEADING3 4
168 #define BT_BLOCKTEXT 5
169 #define BT_PLAINTEXT 6
170 #define BT_NUMBEREDLIST 7
171 #define BT_BULLETLIST 8
172
173 class s_HRText_Listener : public PL_Listener
174 {
175 public:
176 s_HRText_Listener(PD_Document * pDocument,
177 IE_Exp_HRText * pie);
178 virtual ~s_HRText_Listener();
179
180 virtual bool populate(fl_ContainerLayout* sfh,
181 const PX_ChangeRecord * pcr);
182
183 virtual bool populateStrux(pf_Frag_Strux* sdh,
184 const PX_ChangeRecord * pcr,
185 fl_ContainerLayout* * psfh);
186
187 virtual bool change(fl_ContainerLayout* sfh,
188 const PX_ChangeRecord * pcr);
189
190 virtual bool insertStrux(fl_ContainerLayout* sfh,
191 const PX_ChangeRecord * pcr,
192 pf_Frag_Strux* sdh,
193 PL_ListenerId lid,
194 void (* pfnBindHandles)(pf_Frag_Strux* sdhNew,
195 PL_ListenerId lid,
196 fl_ContainerLayout* sfhNew));
197
198 virtual bool signal(UT_uint32 iSignal);
199
200 protected:
201 void _closeSection(void);
202 void _closeTag(void);
203 void _closeSpan(void);
204 void _openTag(PT_AttrPropIndex api);
205 void _openSection(PT_AttrPropIndex api);
206 void _openSpan(PT_AttrPropIndex api);
207 void _outputData(const UT_UCSChar * p, UT_uint32 length);
208 void _handleDataItems(void);
209 void _convertFontSize(char* szDest, const char* pszFontSize);
210 void _convertColor(char* szDest, const char* pszColor);
211
212 PD_Document * m_pDocument;
213 IE_Exp_HRText * m_pie;
214 bool m_bInSection;
215 bool m_bInBlock;
216 bool m_bInSpan;
217 bool m_bNextIsSpace;
218 bool m_bInList;
219 const PP_AttrProp* m_pAP_Span;
220
221 // Need to look up proper type, and place to stick #defines...
222
223 char m_iDecoration;
224 UT_uint16 m_iBlockType; // BT_*
225 UT_uint16 m_iListDepth; // 0 corresponds to not in a list
226 UT_Wctomb m_wctomb;
227
228 UT_StringPtrMap * m_pList;
229 };
230
_closeSection(void)231 void s_HRText_Listener::_closeSection(void)
232 {
233 if (!m_bInSection)
234 {
235 return;
236 }
237
238 m_bInSection = false;
239 return;
240 }
241
_closeTag(void)242 void s_HRText_Listener::_closeTag(void)
243 {
244 if (!m_bInBlock)
245 {
246 return;
247 }
248
249 #ifndef WIN32
250 m_pie->write("\n\n");
251 #else
252 m_pie->write("\r\n\r\n");
253 #endif
254
255 m_bInBlock = false;
256 return;
257 }
258
_openTag(PT_AttrPropIndex api)259 void s_HRText_Listener::_openTag(PT_AttrPropIndex api)
260 {
261 if (!m_bInSection)
262 {
263 return;
264 }
265
266 const PP_AttrProp * pAP = NULL;
267 bool bHaveProp = m_pDocument->getAttrProp(api,&pAP);
268 UT_uint16 * piVal;
269
270 if (bHaveProp && pAP)
271 {
272 const gchar * szValue;
273 //const gchar * szLevel;
274 const gchar * szListID;
275 const gchar * szProps;
276
277 if (
278 (pAP->getAttribute(static_cast<const gchar*>(PT_STYLE_ATTRIBUTE_NAME), szValue))
279 )
280 {
281 if(pAP->getAttribute("listid", szListID) &&
282 0 != strcmp(szListID, "0"))
283 {
284 // we're in a list
285 // todo: maybe check the list level and insert tabs here?
286 if(pAP->getProperty("list-style",szProps) &&
287 0 == strcmp(szProps, "Numbered List"))
288 {
289 // it's a numeric list, have we seen it before?
290 if(!m_pList->pick(static_cast<const char *>(szListID)))
291 {
292 //todo: can you set a list number start-value in abiword?
293 piVal = new UT_uint16(1);
294 m_pList->insert(static_cast<const char *>(szListID),static_cast<void *>(piVal));
295 }
296 UT_uint16 * pTemp = const_cast<UT_uint16 *>(static_cast<const UT_uint16 *>(m_pList->pick(static_cast<const char *>(szListID))));
297 m_pie->write(UT_String_sprintf("%d", *pTemp).c_str());
298 *pTemp = *pTemp + 1;
299 }
300 else
301 {
302 // assume it's a bullet list
303 m_pie->write(LIST_DELIM);
304 }
305 }
306 else
307 {
308 if(0 == strcmp(szValue, "Block Text"))
309 {
310 // <p style="Block Text"> ...
311
312 m_iBlockType = BT_BLOCKTEXT;
313 m_pie->write(BLOCK_DELIM);
314 }
315 else if(0 == strcmp(szValue, "Plain Text"))
316 {
317 // <p style="Plain Text"> ...
318
319 m_iBlockType = BT_PLAINTEXT;
320 m_pie->write(PLAIN_DELIM);
321 }
322 }
323 }
324 }
325 m_bInBlock = true;
326 }
327
_openSection(PT_AttrPropIndex)328 void s_HRText_Listener::_openSection(PT_AttrPropIndex /* api*/)
329 {
330 #ifndef WIN32
331 m_pie->write("\n");
332 #else
333 m_pie->write("\r\n");
334 #endif
335 }
336
_openSpan(PT_AttrPropIndex api)337 void s_HRText_Listener::_openSpan(PT_AttrPropIndex api)
338 {
339 if (!m_bInBlock)
340 {
341 return;
342 }
343
344 const PP_AttrProp * pAP = NULL;
345 bool bHaveProp = m_pDocument->getAttrProp(api,&pAP);
346
347 if (bHaveProp && pAP)
348 {
349 const gchar * szValue;
350
351 if (
352 (pAP->getProperty("font-weight", szValue))
353 && !strcmp(szValue, "bold")
354 )
355 {
356 m_pie->write(BOLD_DELIM);
357 }
358
359 if (
360 (pAP->getProperty("font-style", szValue))
361 && !strcmp(szValue, "italic")
362 )
363 {
364 m_pie->write(ITALIC_DELIM);
365 }
366
367
368 if (
369 (pAP->getProperty("text-decoration", szValue))
370 )
371 {
372 const gchar* pszDecor = szValue;
373
374 gchar* p;
375 if (!(p = g_strdup(pszDecor)))
376 {
377 // TODO outofmem
378 }
379
380 UT_ASSERT(p || !pszDecor);
381 gchar* q = strtok(p, " ");
382
383 while (q)
384 {
385 if (0 == strcmp(q, "underline"))
386 {
387 m_iDecoration = UNDERLINE_DELIM_CHAR;
388 m_pie->write(UNDERLINE_DELIM);
389 }
390
391 q = strtok(NULL, " ");
392 }
393
394 FREEP(p);
395 }
396
397 if (pAP->getProperty("text-position", szValue))
398 {
399 if (!strcmp("superscript", szValue))
400 {
401 m_pie->write(SUPERSCRIPT_DELIM);
402 }
403 else if (!strcmp("subscript", szValue))
404 {
405 m_pie->write(SUBSCRIPT_DELIM);
406 }
407 }
408
409 m_bInSpan = true;
410 m_pAP_Span = pAP;
411 }
412 }
413
_closeSpan(void)414 void s_HRText_Listener::_closeSpan(void)
415 {
416 if (!m_bInSpan)
417 return;
418
419 const PP_AttrProp * pAP = m_pAP_Span;
420
421 if (pAP)
422 {
423
424 const gchar * szValue;
425
426 if (
427 (pAP->getProperty("text-decoration", szValue))
428 && strcmp(szValue, "none")
429 )
430 {
431 if (m_iDecoration)
432 m_pie->write(&m_iDecoration, 1);
433 }
434
435 if (
436 (pAP->getProperty("font-style", szValue))
437 && !strcmp(szValue, "italic")
438 )
439 {
440 m_pie->write(ITALIC_DELIM);
441 }
442
443 if (
444 (pAP->getProperty("font-weight", szValue))
445 && !strcmp(szValue, "bold")
446 )
447 {
448 m_pie->write(BOLD_DELIM);
449 }
450
451 m_pAP_Span = NULL;
452 }
453
454 m_bInSpan = false;
455 return;
456 }
457
_outputData(const UT_UCSChar * data,UT_uint32 length)458 void s_HRText_Listener::_outputData(const UT_UCSChar * data, UT_uint32 length)
459 {
460 UT_String sBuf;
461 const UT_UCSChar * pData;
462
463 int mbLen;
464 char pC[MB_LEN_MAX];
465
466 UT_ASSERT(sizeof(UT_Byte) == sizeof(char));
467
468 sBuf.reserve(length);
469 for (pData=data; (pData<data+length); /**/)
470 {
471 if(!m_wctomb.wctomb(pC,mbLen,*pData))
472 {
473 mbLen=1;
474 pC[0]='?';
475 m_wctomb.initialize();
476 }
477 pData++;
478 if (mbLen>1)
479 {
480 sBuf += pC;
481 }
482 else
483 {
484 sBuf += static_cast<char>(pC[0]);
485 }
486 }
487
488 m_pie->write(sBuf.c_str(),sBuf.size());
489 }
490
s_HRText_Listener(PD_Document * pDocument,IE_Exp_HRText * pie)491 s_HRText_Listener::s_HRText_Listener(PD_Document * pDocument,
492 IE_Exp_HRText * pie)
493 {
494 m_pDocument = pDocument;
495 m_pie = pie;
496 m_bInSection = false;
497 m_bInBlock = false;
498 m_bInSpan = false;
499 m_bNextIsSpace = false;
500 m_bInList = false;
501 m_iListDepth = 0;
502 m_iDecoration = 0;
503
504 m_pList = new UT_StringPtrMap(10);
505 }
506
~s_HRText_Listener()507 s_HRText_Listener::~s_HRText_Listener()
508 {
509 _closeSpan();
510 _closeTag();
511 _closeSection();
512 _handleDataItems();
513
514
515 // free memory used to store list bullet numbers
516 UT_GenericVector<const UT_String*>* pKeyList = m_pList->keys();
517 if (pKeyList)
518 {
519 for(UT_sint32 i = 0; i < pKeyList->getItemCount(); i++)
520 delete const_cast<UT_uint16 *>(static_cast<const UT_uint16 *>(m_pList->pick(pKeyList->getLastItem()->c_str())));
521 }
522 DELETEP(pKeyList);
523 DELETEP(m_pList);
524 }
525
populate(fl_ContainerLayout *,const PX_ChangeRecord * pcr)526 bool s_HRText_Listener::populate(fl_ContainerLayout* /*sfh*/,
527 const PX_ChangeRecord * pcr)
528 {
529 switch (pcr->getType())
530 {
531 case PX_ChangeRecord::PXT_InsertSpan:
532 {
533 const PX_ChangeRecord_Span * pcrs = static_cast<const PX_ChangeRecord_Span *> (pcr);
534
535 PT_AttrPropIndex api = pcr->getIndexAP();
536 if (api)
537 {
538 _openSpan(api);
539 }
540
541 PT_BufIndex bi = pcrs->getBufIndex();
542 _outputData(m_pDocument->getPointer(bi),pcrs->getLength());
543
544 if (api)
545 _closeSpan();
546 return true;
547 }
548
549 case PX_ChangeRecord::PXT_InsertObject:
550 {
551 return true;
552 }
553
554 case PX_ChangeRecord::PXT_InsertFmtMark:
555 return true;
556
557 default:
558 UT_ASSERT(0);
559 return false;
560 }
561 }
562
populateStrux(pf_Frag_Strux *,const PX_ChangeRecord * pcr,fl_ContainerLayout ** psfh)563 bool s_HRText_Listener::populateStrux(pf_Frag_Strux* /*sdh*/,
564 const PX_ChangeRecord * pcr,
565 fl_ContainerLayout* * psfh)
566 {
567 UT_ASSERT(pcr->getType() == PX_ChangeRecord::PXT_InsertStrux);
568 const PX_ChangeRecord_Strux * pcrx = static_cast<const PX_ChangeRecord_Strux *> (pcr);
569 *psfh = 0; // we don't need it.
570
571 switch (pcrx->getStruxType())
572 {
573 case PTX_SectionEndnote:
574 case PTX_SectionHdrFtr:
575 case PTX_Section:
576 {
577 _closeSpan();
578 _closeTag();
579 _closeSection();
580
581 PT_AttrPropIndex indexAP = pcr->getIndexAP();
582 const PP_AttrProp* pAP = NULL;
583 if (m_pDocument->getAttrProp(indexAP, &pAP) && pAP)
584 {
585 const gchar* pszSectionType = NULL;
586 pAP->getAttribute("type", pszSectionType);
587 if (
588 !pszSectionType
589 || (0 == strcmp(pszSectionType, "doc"))
590 )
591 {
592 _openSection(pcr->getIndexAP());
593 m_bInSection = true;
594 }
595 else
596 {
597 m_bInSection = false;
598 }
599 }
600 else
601 {
602 m_bInSection = false;
603 }
604
605 return true;
606 }
607
608 case PTX_Block:
609 {
610 _closeSpan();
611 _closeTag();
612 _openTag(pcr->getIndexAP());
613 return true;
614 }
615
616
617 case PTX_SectionTable:
618 case PTX_EndTable:
619 case PTX_SectionCell:
620 case PTX_EndCell:
621 case PTX_EndFrame:
622 case PTX_EndMarginnote:
623 case PTX_EndFootnote:
624 case PTX_SectionFrame:
625 case PTX_SectionMarginnote:
626 case PTX_SectionFootnote:
627 case PTX_EndEndnote:
628 default:
629 UT_ASSERT(UT_TODO);
630 return true;
631 }
632 }
633
change(fl_ContainerLayout *,const PX_ChangeRecord *)634 bool s_HRText_Listener::change(fl_ContainerLayout* /*sfh*/,
635 const PX_ChangeRecord * /*pcr*/)
636 {
637 UT_ASSERT(0); // this function is not used.
638 return false;
639 }
640
insertStrux(fl_ContainerLayout *,const PX_ChangeRecord *,pf_Frag_Strux *,PL_ListenerId,void (*)(pf_Frag_Strux *,PL_ListenerId,fl_ContainerLayout *))641 bool s_HRText_Listener::insertStrux(fl_ContainerLayout* /*sfh*/,
642 const PX_ChangeRecord * /*pcr*/,
643 pf_Frag_Strux* /*sdh*/,
644 PL_ListenerId /* lid */,
645 void (* /*pfnBindHandles*/)(pf_Frag_Strux* /* sdhNew */,
646 PL_ListenerId /* lid */,
647 fl_ContainerLayout* /* sfhNew */))
648 {
649 UT_ASSERT(0); // this function is not used.
650 return false;
651 }
652
signal(UT_uint32)653 bool s_HRText_Listener::signal(UT_uint32 /* iSignal */)
654 {
655 UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
656 return false;
657 }
658
659
660 /*****************************************************************/
661 /*****************************************************************/
662
_writeDocument(void)663 UT_Error IE_Exp_HRText::_writeDocument(void)
664 {
665 m_pListener = new s_HRText_Listener(getDoc(),this);
666 if (!m_pListener)
667 return UT_IE_NOMEMORY;
668 if (!getDoc()->tellListener(static_cast<PL_Listener *>(m_pListener)))
669 return UT_ERROR;
670
671 delete m_pListener;
672 m_pListener = NULL;
673
674 return ((m_error) ? UT_IE_COULDNOTWRITE : UT_OK);
675 }
676
677 /*****************************************************************/
678 /*****************************************************************/
679
_handleDataItems(void)680 void s_HRText_Listener::_handleDataItems(void)
681 {
682 /* Not much we can do with these in text. */
683 }
684