]> git.saurik.com Git - wxWidgets.git/blob - src/richtext/richtextxml.cpp
replace use of 'long/int bitmapType' with 'wxBitmapType bitmapType' in richtext and...
[wxWidgets.git] / src / richtext / richtextxml.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/richtext/richtextxml.cpp
3 // Purpose: XML and HTML I/O for wxRichTextCtrl
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 2005-09-30
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
14
15 #ifdef __BORLANDC__
16 #pragma hdrstop
17 #endif
18
19 #if wxUSE_RICHTEXT && wxUSE_XML
20
21 #include "wx/richtext/richtextxml.h"
22
23 #ifndef WX_PRECOMP
24 #include "wx/intl.h"
25 #include "wx/module.h"
26 #endif
27
28 #include "wx/filename.h"
29 #include "wx/clipbrd.h"
30 #include "wx/wfstream.h"
31 #include "wx/sstream.h"
32 #include "wx/txtstrm.h"
33 #include "wx/tokenzr.h"
34 #include "wx/xml/xml.h"
35
36 IMPLEMENT_DYNAMIC_CLASS(wxRichTextXMLHandler, wxRichTextFileHandler)
37
38 #if wxUSE_STREAMS
39 bool wxRichTextXMLHandler::DoLoadFile(wxRichTextBuffer *buffer, wxInputStream& stream)
40 {
41 if (!stream.IsOk())
42 return false;
43
44 buffer->ResetAndClearCommands();
45 buffer->Clear();
46
47 wxXmlDocument* xmlDoc = new wxXmlDocument;
48 bool success = true;
49
50 // This is the encoding to convert to (memory encoding rather than file encoding)
51 wxString encoding(wxT("UTF-8"));
52
53 #if !wxUSE_UNICODE && wxUSE_INTL
54 encoding = wxLocale::GetSystemEncodingName();
55 #endif
56
57 if (!xmlDoc->Load(stream, encoding))
58 {
59 buffer->ResetAndClearCommands();
60 success = false;
61 }
62 else
63 {
64 if (xmlDoc->GetRoot() && xmlDoc->GetRoot()->GetType() == wxXML_ELEMENT_NODE && xmlDoc->GetRoot()->GetName() == wxT("richtext"))
65 {
66 wxXmlNode* child = xmlDoc->GetRoot()->GetChildren();
67 while (child)
68 {
69 if (child->GetType() == wxXML_ELEMENT_NODE)
70 {
71 wxString name = child->GetName();
72 if (name == wxT("richtext-version"))
73 {
74 }
75 else
76 ImportXML(buffer, child);
77 }
78
79 child = child->GetNext();
80 }
81 }
82 else
83 {
84 success = false;
85 }
86 }
87
88 delete xmlDoc;
89
90 buffer->UpdateRanges();
91
92 return success;
93 }
94
95 /// Recursively import an object
96 bool wxRichTextXMLHandler::ImportXML(wxRichTextBuffer* buffer, wxXmlNode* node)
97 {
98 wxString name = node->GetName();
99
100 bool doneChildren = false;
101
102 if (name == wxT("paragraphlayout"))
103 {
104 wxString partial = node->GetAttribute(wxT("partialparagraph"), wxEmptyString);
105 if (partial == wxT("true"))
106 buffer->SetPartialParagraph(true);
107 }
108 else if (name == wxT("paragraph"))
109 {
110 wxRichTextParagraph* para = new wxRichTextParagraph(buffer);
111 buffer->AppendChild(para);
112
113 GetStyle(para->GetAttributes(), node, true);
114
115 wxXmlNode* child = node->GetChildren();
116 while (child)
117 {
118 wxString childName = child->GetName();
119 if (childName == wxT("text"))
120 {
121 wxString text;
122 wxXmlNode* textChild = child->GetChildren();
123 while (textChild)
124 {
125 if (textChild->GetType() == wxXML_TEXT_NODE ||
126 textChild->GetType() == wxXML_CDATA_SECTION_NODE)
127 {
128 wxString text2 = textChild->GetContent();
129
130 // Strip whitespace from end
131 if (!text2.empty() && text2[text2.length()-1] == wxT('\n'))
132 text2 = text2.Mid(0, text2.length()-1);
133
134 if (!text2.empty() && text2[0] == wxT('"'))
135 text2 = text2.Mid(1);
136 if (!text2.empty() && text2[text2.length()-1] == wxT('"'))
137 text2 = text2.Mid(0, text2.length() - 1);
138
139 text += text2;
140 }
141 textChild = textChild->GetNext();
142 }
143
144 wxRichTextPlainText* textObject = new wxRichTextPlainText(text, para);
145 GetStyle(textObject->GetAttributes(), child, false);
146
147 para->AppendChild(textObject);
148 }
149 else if (childName == wxT("symbol"))
150 {
151 // This is a symbol that XML can't read in the normal way
152 wxString text;
153 wxXmlNode* textChild = child->GetChildren();
154 while (textChild)
155 {
156 if (textChild->GetType() == wxXML_TEXT_NODE ||
157 textChild->GetType() == wxXML_CDATA_SECTION_NODE)
158 {
159 wxString text2 = textChild->GetContent();
160 text += text2;
161 }
162 textChild = textChild->GetNext();
163 }
164
165 wxString actualText;
166 actualText << (wxChar) wxAtoi(text);
167
168 wxRichTextPlainText* textObject = new wxRichTextPlainText(actualText, para);
169 GetStyle(textObject->GetAttributes(), child, false);
170
171 para->AppendChild(textObject);
172 }
173 else if (childName == wxT("image"))
174 {
175 wxBitmapType imageType = wxBITMAP_TYPE_PNG;
176 wxString value = node->GetAttribute(wxT("imagetype"), wxEmptyString);
177 if (!value.empty())
178 {
179 int type = wxAtoi(value);
180
181 // note: 0 == wxBITMAP_TYPE_INVALID
182 if (type <= 0 || type >= wxBITMAP_TYPE_MAX)
183 wxLogWarning("Invalid bitmap type specified for <image> tag: %d", type);
184 else
185 imageType = (wxBitmapType)type;
186 }
187
188 wxString data;
189
190 wxXmlNode* imageChild = child->GetChildren();
191 while (imageChild)
192 {
193 wxString childName = imageChild->GetName();
194 if (childName == wxT("data"))
195 {
196 wxXmlNode* dataChild = imageChild->GetChildren();
197 while (dataChild)
198 {
199 data = dataChild->GetContent();
200 // wxLogDebug(data);
201 dataChild = dataChild->GetNext();
202 }
203
204 }
205 imageChild = imageChild->GetNext();
206 }
207
208 if (!data.empty())
209 {
210 wxRichTextImage* imageObj = new wxRichTextImage(para);
211 GetStyle(imageObj->GetAttributes(), child, false);
212 para->AppendChild(imageObj);
213
214 wxStringInputStream strStream(data);
215
216 imageObj->GetImageBlock().ReadHex(strStream, data.length(), imageType);
217 }
218 }
219 child = child->GetNext();
220 }
221
222 doneChildren = true;
223 }
224 else if (name == wxT("stylesheet"))
225 {
226 if (GetFlags() & wxRICHTEXT_HANDLER_INCLUDE_STYLESHEET)
227 {
228 wxRichTextStyleSheet* sheet = new wxRichTextStyleSheet;
229 wxString sheetName = node->GetAttribute(wxT("name"), wxEmptyString);
230 wxString sheetDescription = node->GetAttribute(wxT("description"), wxEmptyString);
231 sheet->SetName(sheetName);
232 sheet->SetDescription(sheetDescription);
233
234 wxXmlNode* child = node->GetChildren();
235 while (child)
236 {
237 ImportStyleDefinition(sheet, child);
238
239 child = child->GetNext();
240 }
241
242 // Notify that styles have changed. If this is vetoed by the app,
243 // the new sheet will be deleted. If it is not vetoed, the
244 // old sheet will be deleted and replaced with the new one.
245 buffer->SetStyleSheetAndNotify(sheet);
246 }
247 doneChildren = true;
248 }
249
250 if (!doneChildren)
251 {
252 wxXmlNode* child = node->GetChildren();
253 while (child)
254 {
255 ImportXML(buffer, child);
256 child = child->GetNext();
257 }
258 }
259
260 return true;
261 }
262
263 bool wxRichTextXMLHandler::ImportStyleDefinition(wxRichTextStyleSheet* sheet, wxXmlNode* node)
264 {
265 wxString styleType = node->GetName();
266 wxString styleName = node->GetAttribute(wxT("name"), wxEmptyString);
267 wxString baseStyleName = node->GetAttribute(wxT("basestyle"), wxEmptyString);
268
269 if (styleName.IsEmpty())
270 return false;
271
272 if (styleType == wxT("characterstyle"))
273 {
274 wxRichTextCharacterStyleDefinition* def = new wxRichTextCharacterStyleDefinition(styleName);
275 def->SetBaseStyle(baseStyleName);
276
277 wxXmlNode* child = node->GetChildren();
278 while (child)
279 {
280 if (child->GetName() == wxT("style"))
281 {
282 wxTextAttr attr;
283 GetStyle(attr, child, false);
284 def->SetStyle(attr);
285 }
286 child = child->GetNext();
287 }
288
289 sheet->AddCharacterStyle(def);
290 }
291 else if (styleType == wxT("paragraphstyle"))
292 {
293 wxRichTextParagraphStyleDefinition* def = new wxRichTextParagraphStyleDefinition(styleName);
294
295 wxString nextStyleName = node->GetAttribute(wxT("nextstyle"), wxEmptyString);
296 def->SetNextStyle(nextStyleName);
297 def->SetBaseStyle(baseStyleName);
298
299 wxXmlNode* child = node->GetChildren();
300 while (child)
301 {
302 if (child->GetName() == wxT("style"))
303 {
304 wxTextAttr attr;
305 GetStyle(attr, child, false);
306 def->SetStyle(attr);
307 }
308 child = child->GetNext();
309 }
310
311 sheet->AddParagraphStyle(def);
312 }
313 else if (styleType == wxT("liststyle"))
314 {
315 wxRichTextListStyleDefinition* def = new wxRichTextListStyleDefinition(styleName);
316
317 wxString nextStyleName = node->GetAttribute(wxT("nextstyle"), wxEmptyString);
318 def->SetNextStyle(nextStyleName);
319 def->SetBaseStyle(baseStyleName);
320
321 wxXmlNode* child = node->GetChildren();
322 while (child)
323 {
324 if (child->GetName() == wxT("style"))
325 {
326 wxTextAttr attr;
327 GetStyle(attr, child, false);
328
329 wxString styleLevel = child->GetAttribute(wxT("level"), wxEmptyString);
330 if (styleLevel.IsEmpty())
331 {
332 def->SetStyle(attr);
333 }
334 else
335 {
336 int level = wxAtoi(styleLevel);
337 if (level > 0 && level <= 10)
338 {
339 def->SetLevelAttributes(level-1, attr);
340 }
341 }
342 }
343 child = child->GetNext();
344 }
345
346 sheet->AddListStyle(def);
347 }
348
349 return true;
350 }
351
352 //-----------------------------------------------------------------------------
353 // xml support routines
354 //-----------------------------------------------------------------------------
355
356 bool wxRichTextXMLHandler::HasParam(wxXmlNode* node, const wxString& param)
357 {
358 return (GetParamNode(node, param) != NULL);
359 }
360
361 wxXmlNode *wxRichTextXMLHandler::GetParamNode(wxXmlNode* node, const wxString& param)
362 {
363 wxCHECK_MSG(node, NULL, wxT("You can't access node data before it was initialized!"));
364
365 wxXmlNode *n = node->GetChildren();
366
367 while (n)
368 {
369 if (n->GetType() == wxXML_ELEMENT_NODE && n->GetName() == param)
370 return n;
371 n = n->GetNext();
372 }
373 return NULL;
374 }
375
376
377 wxString wxRichTextXMLHandler::GetNodeContent(wxXmlNode *node)
378 {
379 wxXmlNode *n = node;
380 if (n == NULL) return wxEmptyString;
381 n = n->GetChildren();
382
383 while (n)
384 {
385 if (n->GetType() == wxXML_TEXT_NODE ||
386 n->GetType() == wxXML_CDATA_SECTION_NODE)
387 return n->GetContent();
388 n = n->GetNext();
389 }
390 return wxEmptyString;
391 }
392
393
394 wxString wxRichTextXMLHandler::GetParamValue(wxXmlNode *node, const wxString& param)
395 {
396 if (param.empty())
397 return GetNodeContent(node);
398 else
399 return GetNodeContent(GetParamNode(node, param));
400 }
401
402 wxString wxRichTextXMLHandler::GetText(wxXmlNode *node, const wxString& param, bool WXUNUSED(translate))
403 {
404 wxXmlNode *parNode = GetParamNode(node, param);
405 if (!parNode)
406 parNode = node;
407 wxString str1(GetNodeContent(parNode));
408 return str1;
409 }
410
411 // For use with earlier versions of wxWidgets
412 #ifndef WXUNUSED_IN_UNICODE
413 #if wxUSE_UNICODE
414 #define WXUNUSED_IN_UNICODE(x) WXUNUSED(x)
415 #else
416 #define WXUNUSED_IN_UNICODE(x) x
417 #endif
418 #endif
419
420 // write string to output:
421 inline static void OutputString(wxOutputStream& stream, const wxString& str,
422 wxMBConv *WXUNUSED_IN_UNICODE(convMem) = NULL, wxMBConv *convFile = NULL)
423 {
424 if (str.empty()) return;
425 #if wxUSE_UNICODE
426 if (convFile)
427 {
428 const wxWX2MBbuf buf(str.mb_str(*convFile));
429 stream.Write((const char*)buf, strlen((const char*)buf));
430 }
431 else
432 {
433 const wxWX2MBbuf buf(str.mb_str(wxConvUTF8));
434 stream.Write((const char*)buf, strlen((const char*)buf));
435 }
436 #else
437 if ( convFile == NULL )
438 stream.Write(str.mb_str(), str.Len());
439 else
440 {
441 wxString str2(str.wc_str(*convMem), *convFile);
442 stream.Write(str2.mb_str(), str2.Len());
443 }
444 #endif
445 }
446
447 // Same as above, but create entities first.
448 // Translates '<' to "&lt;", '>' to "&gt;" and '&' to "&amp;"
449 static void OutputStringEnt(wxOutputStream& stream, const wxString& str,
450 wxMBConv *convMem = NULL, wxMBConv *convFile = NULL)
451 {
452 wxString buf;
453 size_t i, last, len;
454 wxChar c;
455
456 len = str.Len();
457 last = 0;
458 for (i = 0; i < len; i++)
459 {
460 c = str.GetChar(i);
461
462 // Original code excluded "&amp;" but we _do_ want to convert
463 // the ampersand beginning &amp; because otherwise when read in,
464 // the original "&amp;" becomes "&".
465
466 if (c == wxT('<') || c == wxT('>') || c == wxT('"') ||
467 (c == wxT('&') /* && (str.Mid(i+1, 4) != wxT("amp;")) */ ))
468 {
469 OutputString(stream, str.Mid(last, i - last), convMem, convFile);
470 switch (c)
471 {
472 case wxT('<'):
473 OutputString(stream, wxT("&lt;"), NULL, NULL);
474 break;
475 case wxT('>'):
476 OutputString(stream, wxT("&gt;"), NULL, NULL);
477 break;
478 case wxT('&'):
479 OutputString(stream, wxT("&amp;"), NULL, NULL);
480 break;
481 case wxT('"'):
482 OutputString(stream, wxT("&quot;"), NULL, NULL);
483 break;
484 default: break;
485 }
486 last = i + 1;
487 }
488 else if (wxUChar(c) > 127)
489 {
490 OutputString(stream, str.Mid(last, i - last), convMem, convFile);
491
492 wxString s(wxT("&#"));
493 s << (int) c;
494 s << wxT(";");
495 OutputString(stream, s, NULL, NULL);
496 last = i + 1;
497 }
498 }
499 OutputString(stream, str.Mid(last, i - last), convMem, convFile);
500 }
501
502 static wxString AttributeToXML(const wxString& str)
503 {
504 wxString str1;
505 size_t i, last, len;
506 wxChar c;
507
508 len = str.Len();
509 last = 0;
510 for (i = 0; i < len; i++)
511 {
512 c = str.GetChar(i);
513
514 // Original code excluded "&amp;" but we _do_ want to convert
515 // the ampersand beginning &amp; because otherwise when read in,
516 // the original "&amp;" becomes "&".
517
518 if (c == wxT('<') || c == wxT('>') || c == wxT('"') ||
519 (c == wxT('&') /* && (str.Mid(i+1, 4) != wxT("amp;")) */ ))
520 {
521 str1 += str.Mid(last, i - last);
522 switch (c)
523 {
524 case wxT('<'):
525 str1 += wxT("&lt;");
526 break;
527 case wxT('>'):
528 str1 += wxT("&gt;");
529 break;
530 case wxT('&'):
531 str1 += wxT("&amp;");
532 break;
533 case wxT('"'):
534 str1 += wxT("&quot;");
535 break;
536 default: break;
537 }
538 last = i + 1;
539 }
540 else if (wxUChar(c) > 127)
541 {
542 str1 += str.Mid(last, i - last);
543
544 wxString s(wxT("&#"));
545 s << (int) c;
546 s << wxT(";");
547 str1 += s;
548 last = i + 1;
549 }
550 }
551 str1 += str.Mid(last, i - last);
552 return str1;
553 }
554
555 inline static void OutputIndentation(wxOutputStream& stream, int indent)
556 {
557 wxString str = wxT("\n");
558 for (int i = 0; i < indent; i++)
559 str << wxT(' ') << wxT(' ');
560 OutputString(stream, str, NULL, NULL);
561 }
562
563 // Convert a colour to a 6-digit hex string
564 static wxString ColourToHexString(const wxColour& col)
565 {
566 wxString hex;
567
568 hex += wxDecToHex(col.Red());
569 hex += wxDecToHex(col.Green());
570 hex += wxDecToHex(col.Blue());
571
572 return hex;
573 }
574
575 // Convert 6-digit hex string to a colour
576 static wxColour HexStringToColour(const wxString& hex)
577 {
578 unsigned char r = (unsigned char)wxHexToDec(hex.Mid(0, 2));
579 unsigned char g = (unsigned char)wxHexToDec(hex.Mid(2, 2));
580 unsigned char b = (unsigned char)wxHexToDec(hex.Mid(4, 2));
581
582 return wxColour(r, g, b);
583 }
584
585 bool wxRichTextXMLHandler::DoSaveFile(wxRichTextBuffer *buffer, wxOutputStream& stream)
586 {
587 if (!stream.IsOk())
588 return false;
589
590 wxString version(wxT("1.0") ) ;
591
592 bool deleteConvFile = false;
593 wxString fileEncoding;
594 wxMBConv* convFile = NULL;
595
596 #if wxUSE_UNICODE
597 fileEncoding = wxT("UTF-8");
598 convFile = & wxConvUTF8;
599 #else
600 fileEncoding = wxT("ISO-8859-1");
601 convFile = & wxConvISO8859_1;
602 #endif
603
604 // If SetEncoding has been called, change the output encoding.
605 if (!m_encoding.empty() && m_encoding.Lower() != fileEncoding.Lower())
606 {
607 if (m_encoding == wxT("<System>"))
608 {
609 #if wxUSE_INTL
610 fileEncoding = wxLocale::GetSystemEncodingName();
611 // if !wxUSE_INTL, we fall back to UTF-8 or ISO-8859-1 below
612 #endif
613 }
614 else
615 {
616 fileEncoding = m_encoding;
617 }
618
619 // GetSystemEncodingName may not have returned a name
620 if (fileEncoding.empty())
621 #if wxUSE_UNICODE
622 fileEncoding = wxT("UTF-8");
623 #else
624 fileEncoding = wxT("ISO-8859-1");
625 #endif
626 convFile = new wxCSConv(fileEncoding);
627 deleteConvFile = true;
628 }
629
630 #if !wxUSE_UNICODE
631 wxMBConv* convMem = wxConvCurrent;
632 #else
633 wxMBConv* convMem = NULL;
634 #endif
635
636 wxString s ;
637 s.Printf(wxT("<?xml version=\"%s\" encoding=\"%s\"?>\n"),
638 version, fileEncoding);
639 OutputString(stream, s, NULL, NULL);
640 OutputString(stream, wxT("<richtext version=\"1.0.0.0\" xmlns=\"http://www.wxwidgets.org\">") , NULL, NULL);
641
642 int level = 1;
643
644 if (buffer->GetStyleSheet() && (GetFlags() & wxRICHTEXT_HANDLER_INCLUDE_STYLESHEET))
645 {
646 OutputIndentation(stream, level);
647 wxString nameAndDescr;
648 if (!buffer->GetStyleSheet()->GetName().IsEmpty())
649 nameAndDescr << wxT(" name=\"") << buffer->GetStyleSheet()->GetName() << wxT("\"");
650 if (!buffer->GetStyleSheet()->GetDescription().IsEmpty())
651 nameAndDescr << wxT(" description=\"") << buffer->GetStyleSheet()->GetDescription() << wxT("\"");
652 OutputString(stream, wxString(wxT("<stylesheet")) + nameAndDescr + wxT(">"), convMem, convFile);
653
654 int i;
655
656 for (i = 0; i < (int) buffer->GetStyleSheet()->GetCharacterStyleCount(); i++)
657 {
658 wxRichTextCharacterStyleDefinition* def = buffer->GetStyleSheet()->GetCharacterStyle(i);
659 ExportStyleDefinition(stream, convMem, convFile, def, level + 1);
660 }
661
662 for (i = 0; i < (int) buffer->GetStyleSheet()->GetParagraphStyleCount(); i++)
663 {
664 wxRichTextParagraphStyleDefinition* def = buffer->GetStyleSheet()->GetParagraphStyle(i);
665 ExportStyleDefinition(stream, convMem, convFile, def, level + 1);
666 }
667
668 for (i = 0; i < (int) buffer->GetStyleSheet()->GetListStyleCount(); i++)
669 {
670 wxRichTextListStyleDefinition* def = buffer->GetStyleSheet()->GetListStyle(i);
671 ExportStyleDefinition(stream, convMem, convFile, def, level + 1);
672 }
673
674 OutputIndentation(stream, level);
675 OutputString(stream, wxT("</stylesheet>"), convMem, convFile);
676 }
677
678
679 bool success = ExportXML(stream, convMem, convFile, *buffer, level);
680
681 OutputString(stream, wxT("\n</richtext>") , NULL, NULL);
682 OutputString(stream, wxT("\n"), NULL, NULL);
683
684 if (deleteConvFile)
685 delete convFile;
686
687 return success;
688 }
689
690 /// Recursively export an object
691 bool wxRichTextXMLHandler::ExportXML(wxOutputStream& stream, wxMBConv* convMem, wxMBConv* convFile, wxRichTextObject& obj, int indent)
692 {
693 wxString objectName;
694 if (obj.IsKindOf(CLASSINFO(wxRichTextParagraphLayoutBox)))
695 objectName = wxT("paragraphlayout");
696 else if (obj.IsKindOf(CLASSINFO(wxRichTextParagraph)))
697 objectName = wxT("paragraph");
698 else if (obj.IsKindOf(CLASSINFO(wxRichTextPlainText)))
699 objectName = wxT("text");
700 else if (obj.IsKindOf(CLASSINFO(wxRichTextImage)))
701 objectName = wxT("image");
702 else
703 objectName = wxT("object");
704
705 bool terminateTag = true;
706
707 if (obj.IsKindOf(CLASSINFO(wxRichTextPlainText)))
708 {
709 wxRichTextPlainText& textObj = (wxRichTextPlainText&) obj;
710
711 wxString style = CreateStyle(obj.GetAttributes(), false);
712
713 int i;
714 int last = 0;
715 const wxString& text = textObj.GetText();
716 int len = (int) text.Length();
717
718 if (len == 0)
719 {
720 i = 0;
721 OutputIndentation(stream, indent);
722 OutputString(stream, wxT("<") + objectName, convMem, convFile);
723 OutputString(stream, style + wxT(">"), convMem, convFile);
724 OutputString(stream, wxT("</text>"), convMem, convFile);
725 }
726 else for (i = 0; i < len; i++)
727 {
728 int c = (int) text[i];
729 if ((c < 32 || c == 34) && c != 9 && c != 10 && c != 13)
730 {
731 if (i > 0)
732 {
733 OutputIndentation(stream, indent);
734 OutputString(stream, wxT("<") + objectName, convMem, convFile);
735
736 OutputString(stream, style + wxT(">"), convMem, convFile);
737
738 wxString fragment(text.Mid(last, i-last));
739 if (!fragment.empty() && (fragment[0] == wxT(' ') || fragment[fragment.length()-1] == wxT(' ')))
740 {
741 OutputString(stream, wxT("\""), convMem, convFile);
742 OutputStringEnt(stream, fragment, convMem, convFile);
743 OutputString(stream, wxT("\""), convMem, convFile);
744 }
745 else
746 OutputStringEnt(stream, fragment, convMem, convFile);
747
748 OutputString(stream, wxT("</text>"), convMem, convFile);
749 }
750
751
752 // Output this character as a number in a separate tag, because XML can't cope
753 // with entities below 32 except for 9, 10 and 13
754 last = i + 1;
755 OutputIndentation(stream, indent);
756 OutputString(stream, wxT("<symbol"), convMem, convFile);
757
758 OutputString(stream, style + wxT(">"), convMem, convFile);
759 OutputString(stream, wxString::Format(wxT("%d"), c), convMem, convFile);
760
761 OutputString(stream, wxT("</symbol>"), convMem, convFile);
762 }
763 }
764
765 wxString fragment;
766 if (last == 0)
767 fragment = text;
768 else
769 fragment = text.Mid(last, i-last);
770
771 if (last < len)
772 {
773 OutputIndentation(stream, indent);
774 OutputString(stream, wxT("<") + objectName, convMem, convFile);
775
776 OutputString(stream, style + wxT(">"), convMem, convFile);
777
778 if (!fragment.empty() && (fragment[0] == wxT(' ') || fragment[fragment.length()-1] == wxT(' ')))
779 {
780 OutputString(stream, wxT("\""), convMem, convFile);
781 OutputStringEnt(stream, fragment, convMem, convFile);
782 OutputString(stream, wxT("\""), convMem, convFile);
783 }
784 else
785 OutputStringEnt(stream, fragment, convMem, convFile);
786 }
787 else
788 terminateTag = false;
789 }
790 else if (obj.IsKindOf(CLASSINFO(wxRichTextImage)))
791 {
792 wxRichTextImage& imageObj = (wxRichTextImage&) obj;
793
794 wxString style = CreateStyle(obj.GetAttributes(), false);
795
796 if (imageObj.GetImage().Ok() && !imageObj.GetImageBlock().Ok())
797 imageObj.MakeBlock();
798
799 OutputIndentation(stream, indent);
800 OutputString(stream, wxT("<") + objectName, convMem, convFile);
801 if (!imageObj.GetImageBlock().Ok())
802 {
803 // No data
804 OutputString(stream, style + wxT(">"), convMem, convFile);
805 }
806 else
807 {
808 OutputString(stream, wxString::Format(wxT(" imagetype=\"%d\"") + style + wxT(">"), (int) imageObj.GetImageBlock().GetImageType()));
809 }
810
811 OutputIndentation(stream, indent+1);
812 OutputString(stream, wxT("<data>"), convMem, convFile);
813
814 imageObj.GetImageBlock().WriteHex(stream);
815
816 OutputString(stream, wxT("</data>"), convMem, convFile);
817 }
818 else if (obj.IsKindOf(CLASSINFO(wxRichTextCompositeObject)))
819 {
820 OutputIndentation(stream, indent);
821 OutputString(stream, wxT("<") + objectName, convMem, convFile);
822
823 bool isPara = false;
824 if (objectName == wxT("paragraph") || objectName == wxT("paragraphlayout"))
825 isPara = true;
826
827 wxString style = CreateStyle(obj.GetAttributes(), isPara);
828
829 if (objectName == wxT("paragraphlayout") && ((wxRichTextParagraphLayoutBox&) obj).GetPartialParagraph())
830 style << wxT(" partialparagraph=\"true\"");
831
832 OutputString(stream, style + wxT(">"), convMem, convFile);
833
834 wxRichTextCompositeObject& composite = (wxRichTextCompositeObject&) obj;
835 size_t i;
836 for (i = 0; i < composite.GetChildCount(); i++)
837 {
838 wxRichTextObject* child = composite.GetChild(i);
839 ExportXML(stream, convMem, convFile, *child, indent+1);
840 }
841 }
842
843 if (objectName != wxT("text"))
844 OutputIndentation(stream, indent);
845
846 if (terminateTag)
847 OutputString(stream, wxT("</") + objectName + wxT(">"), convMem, convFile);
848
849 return true;
850 }
851
852 bool wxRichTextXMLHandler::ExportStyleDefinition(wxOutputStream& stream, wxMBConv* convMem, wxMBConv* convFile, wxRichTextStyleDefinition* def, int level)
853 {
854 wxRichTextCharacterStyleDefinition* charDef = wxDynamicCast(def, wxRichTextCharacterStyleDefinition);
855 wxRichTextParagraphStyleDefinition* paraDef = wxDynamicCast(def, wxRichTextParagraphStyleDefinition);
856 wxRichTextListStyleDefinition* listDef = wxDynamicCast(def, wxRichTextListStyleDefinition);
857
858 wxString baseStyle = def->GetBaseStyle();
859 wxString baseStyleProp;
860 if (!baseStyle.IsEmpty())
861 baseStyleProp = wxT(" basestyle=\"") + baseStyle + wxT("\"");
862
863 wxString descr = def->GetDescription();
864 wxString descrProp;
865 if (!descr.IsEmpty())
866 descrProp = wxT(" description=\"") + descr + wxT("\"");
867
868 if (charDef)
869 {
870 OutputIndentation(stream, level);
871 OutputString(stream, wxT("<characterstyle") + baseStyleProp + descrProp + wxT(">"), convMem, convFile);
872
873 level ++;
874
875 wxString style = CreateStyle(def->GetStyle(), false);
876
877 OutputIndentation(stream, level);
878 OutputString(stream, wxT("<style ") + style + wxT(">"), convMem, convFile);
879
880 OutputIndentation(stream, level);
881 OutputString(stream, wxT("</style>"), convMem, convFile);
882
883 level --;
884
885 OutputIndentation(stream, level);
886 OutputString(stream, wxT("</characterstyle>"), convMem, convFile);
887 }
888 else if (listDef)
889 {
890 OutputIndentation(stream, level);
891
892 if (!listDef->GetNextStyle().IsEmpty())
893 baseStyleProp << wxT(" basestyle=\"") << listDef->GetNextStyle() << wxT("\"");
894
895 OutputString(stream, wxT("<liststyle") + baseStyleProp + descrProp + wxT(">"), convMem, convFile);
896
897 level ++;
898
899 wxString style = CreateStyle(def->GetStyle(), false);
900
901 OutputIndentation(stream, level);
902 OutputString(stream, wxT("<style ") + style + wxT(">"), convMem, convFile);
903
904 OutputIndentation(stream, level);
905 OutputString(stream, wxT("</style>"), convMem, convFile);
906
907 int i;
908 for (i = 0; i < 10; i ++)
909 {
910 wxTextAttr* levelAttr = listDef->GetLevelAttributes(i);
911 if (levelAttr)
912 {
913 wxString style = CreateStyle(def->GetStyle(), false);
914 wxString levelStr = wxString::Format(wxT(" level=\"%d\" "), (i+1));
915
916 OutputIndentation(stream, level);
917 OutputString(stream, wxT("<style ") + levelStr + style + wxT(">"), convMem, convFile);
918
919 OutputIndentation(stream, level);
920 OutputString(stream, wxT("</style>"), convMem, convFile);
921 }
922 }
923
924 level --;
925
926 OutputIndentation(stream, level);
927 OutputString(stream, wxT("</liststyle>"), convMem, convFile);
928 }
929 else if (paraDef)
930 {
931 OutputIndentation(stream, level);
932
933 if (!paraDef->GetNextStyle().IsEmpty())
934 baseStyleProp << wxT(" basestyle=\"") << paraDef->GetNextStyle() << wxT("\"");
935
936 OutputString(stream, wxT("<paragraphstyle") + baseStyleProp + descrProp + wxT(">"), convMem, convFile);
937
938 level ++;
939
940 wxString style = CreateStyle(def->GetStyle(), false);
941
942 OutputIndentation(stream, level);
943 OutputString(stream, wxT("<style ") + style + wxT(">"), convMem, convFile);
944
945 OutputIndentation(stream, level);
946 OutputString(stream, wxT("</style>"), convMem, convFile);
947
948 level --;
949
950 OutputIndentation(stream, level);
951 OutputString(stream, wxT("</paragraphstyle>"), convMem, convFile);
952 }
953
954 return true;
955 }
956
957 /// Create style parameters
958 wxString wxRichTextXMLHandler::CreateStyle(const wxTextAttr& attr, bool isPara)
959 {
960 wxString str;
961 if (attr.HasTextColour() && attr.GetTextColour().Ok())
962 {
963 str << wxT(" textcolor=\"#") << ColourToHexString(attr.GetTextColour()) << wxT("\"");
964 }
965 if (attr.HasBackgroundColour() && attr.GetBackgroundColour().Ok())
966 {
967 str << wxT(" bgcolor=\"#") << ColourToHexString(attr.GetBackgroundColour()) << wxT("\"");
968 }
969
970 if (attr.HasFontSize())
971 str << wxT(" fontsize=\"") << attr.GetFontSize() << wxT("\"");
972
973 //if (attr.HasFontFamily())
974 // str << wxT(" fontfamily=\"") << attr.GetFont().GetFamily() << wxT("\"");
975
976 if (attr.HasFontItalic())
977 str << wxT(" fontstyle=\"") << attr.GetFontStyle() << wxT("\"");
978
979 if (attr.HasFontWeight())
980 str << wxT(" fontweight=\"") << attr.GetFontWeight() << wxT("\"");
981
982 if (attr.HasFontUnderlined())
983 str << wxT(" fontunderlined=\"") << (int) attr.GetFontUnderlined() << wxT("\"");
984
985 if (attr.HasFontFaceName())
986 str << wxT(" fontface=\"") << attr.GetFontFaceName() << wxT("\"");
987
988 if (attr.HasTextEffects())
989 {
990 str << wxT(" texteffects=\"");
991 str << attr.GetTextEffects();
992 str << wxT("\"");
993
994 str << wxT(" texteffectflags=\"");
995 str << attr.GetTextEffectFlags();
996 str << wxT("\"");
997 }
998
999 if (!attr.GetCharacterStyleName().empty())
1000 str << wxT(" characterstyle=\"") << wxString(attr.GetCharacterStyleName()) << wxT("\"");
1001
1002 if (attr.HasURL())
1003 str << wxT(" url=\"") << AttributeToXML(attr.GetURL()) << wxT("\"");
1004
1005 if (isPara)
1006 {
1007 if (attr.HasAlignment())
1008 str << wxT(" alignment=\"") << (int) attr.GetAlignment() << wxT("\"");
1009
1010 if (attr.HasLeftIndent())
1011 {
1012 str << wxT(" leftindent=\"") << (int) attr.GetLeftIndent() << wxT("\"");
1013 str << wxT(" leftsubindent=\"") << (int) attr.GetLeftSubIndent() << wxT("\"");
1014 }
1015
1016 if (attr.HasRightIndent())
1017 str << wxT(" rightindent=\"") << (int) attr.GetRightIndent() << wxT("\"");
1018
1019 if (attr.HasParagraphSpacingAfter())
1020 str << wxT(" parspacingafter=\"") << (int) attr.GetParagraphSpacingAfter() << wxT("\"");
1021
1022 if (attr.HasParagraphSpacingBefore())
1023 str << wxT(" parspacingbefore=\"") << (int) attr.GetParagraphSpacingBefore() << wxT("\"");
1024
1025 if (attr.HasLineSpacing())
1026 str << wxT(" linespacing=\"") << (int) attr.GetLineSpacing() << wxT("\"");
1027
1028 if (attr.HasBulletStyle())
1029 str << wxT(" bulletstyle=\"") << (int) attr.GetBulletStyle() << wxT("\"");
1030
1031 if (attr.HasBulletNumber())
1032 str << wxT(" bulletnumber=\"") << (int) attr.GetBulletNumber() << wxT("\"");
1033
1034 if (attr.HasBulletText())
1035 {
1036 // If using a bullet symbol, convert to integer in case it's a non-XML-friendly character.
1037 // Otherwise, assume it's XML-friendly text such as outline numbering, e.g. 1.2.3.1
1038 if (!attr.GetBulletText().IsEmpty() && (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL))
1039 str << wxT(" bulletsymbol=\"") << (int) (attr.GetBulletText()[0]) << wxT("\"");
1040 else
1041 str << wxT(" bullettext=\"") << attr.GetBulletText() << wxT("\"");
1042
1043 str << wxT(" bulletfont=\"") << attr.GetBulletFont() << wxT("\"");
1044 }
1045
1046 if (attr.HasBulletName())
1047 str << wxT(" bulletname=\"") << attr.GetBulletName() << wxT("\"");
1048
1049 if (!attr.GetParagraphStyleName().empty())
1050 str << wxT(" parstyle=\"") << wxString(attr.GetParagraphStyleName()) << wxT("\"");
1051
1052 if (!attr.GetListStyleName().empty())
1053 str << wxT(" liststyle=\"") << wxString(attr.GetListStyleName()) << wxT("\"");
1054
1055 if (attr.HasTabs())
1056 {
1057 str << wxT(" tabs=\"");
1058 size_t i;
1059 for (i = 0; i < attr.GetTabs().GetCount(); i++)
1060 {
1061 if (i > 0)
1062 str << wxT(",");
1063 str << attr.GetTabs()[i];
1064 }
1065 str << wxT("\"");
1066 }
1067
1068 if (attr.HasPageBreak())
1069 {
1070 str << wxT(" pagebreak=\"1\"");
1071 }
1072
1073 if (attr.HasOutlineLevel())
1074 str << wxT(" outlinelevel=\"") << (int) attr.GetOutlineLevel() << wxT("\"");
1075
1076 }
1077
1078 return str;
1079 }
1080
1081 /// Get style parameters
1082 bool wxRichTextXMLHandler::GetStyle(wxTextAttr& attr, wxXmlNode* node, bool isPara)
1083 {
1084 wxString fontFacename;
1085 int fontSize = 12;
1086 // int fontFamily = wxDEFAULT;
1087 int fontWeight = wxNORMAL;
1088 int fontStyle = wxNORMAL;
1089 bool fontUnderlined = false;
1090
1091 // int fontFlags = 0;
1092
1093 fontFacename = node->GetAttribute(wxT("fontface"), wxEmptyString);
1094 if (!fontFacename.IsEmpty())
1095 attr.SetFontFaceName(fontFacename);
1096
1097 wxString value;
1098 //value = node->GetAttribute(wxT("fontfamily"), wxEmptyString);
1099 //if (!value.empty())
1100 // fontFamily = wxAtoi(value);
1101
1102 value = node->GetAttribute(wxT("fontstyle"), wxEmptyString);
1103 if (!value.empty())
1104 {
1105 fontStyle = wxAtoi(value);
1106 attr.SetFontStyle(fontStyle);
1107 }
1108
1109 value = node->GetAttribute(wxT("fontsize"), wxEmptyString);
1110 if (!value.empty())
1111 {
1112 fontSize = wxAtoi(value);
1113 attr.SetFontSize(fontSize);
1114 }
1115
1116 value = node->GetAttribute(wxT("fontweight"), wxEmptyString);
1117 if (!value.empty())
1118 {
1119 fontWeight = wxAtoi(value);
1120 attr.SetFontWeight(fontWeight);
1121 }
1122
1123 value = node->GetAttribute(wxT("fontunderlined"), wxEmptyString);
1124 if (!value.empty())
1125 {
1126 fontUnderlined = wxAtoi(value) != 0;
1127 attr.SetFontUnderlined(fontUnderlined);
1128 }
1129
1130 value = node->GetAttribute(wxT("textcolor"), wxEmptyString);
1131 if (!value.empty())
1132 {
1133 if (value[0] == wxT('#'))
1134 attr.SetTextColour(HexStringToColour(value.Mid(1)));
1135 else
1136 attr.SetTextColour(value);
1137 }
1138
1139 value = node->GetAttribute(wxT("bgcolor"), wxEmptyString);
1140 if (!value.empty())
1141 {
1142 if (value[0] == wxT('#'))
1143 attr.SetBackgroundColour(HexStringToColour(value.Mid(1)));
1144 else
1145 attr.SetBackgroundColour(value);
1146 }
1147
1148 value = node->GetAttribute(wxT("characterstyle"), wxEmptyString);
1149 if (!value.empty())
1150 attr.SetCharacterStyleName(value);
1151
1152 value = node->GetAttribute(wxT("texteffects"), wxEmptyString);
1153 if (!value.IsEmpty())
1154 {
1155 attr.SetTextEffects(wxAtoi(value));
1156 }
1157
1158 value = node->GetAttribute(wxT("texteffectflags"), wxEmptyString);
1159 if (!value.IsEmpty())
1160 {
1161 attr.SetTextEffectFlags(wxAtoi(value));
1162 }
1163
1164 value = node->GetAttribute(wxT("url"), wxEmptyString);
1165 if (!value.empty())
1166 attr.SetURL(value);
1167
1168 // Set paragraph attributes
1169 if (isPara)
1170 {
1171 value = node->GetAttribute(wxT("alignment"), wxEmptyString);
1172 if (!value.empty())
1173 attr.SetAlignment((wxTextAttrAlignment) wxAtoi(value));
1174
1175 int leftSubIndent = 0;
1176 int leftIndent = 0;
1177 bool hasLeftIndent = false;
1178
1179 value = node->GetAttribute(wxT("leftindent"), wxEmptyString);
1180 if (!value.empty())
1181 {
1182 leftIndent = wxAtoi(value);
1183 hasLeftIndent = true;
1184 }
1185
1186 value = node->GetAttribute(wxT("leftsubindent"), wxEmptyString);
1187 if (!value.empty())
1188 {
1189 leftSubIndent = wxAtoi(value);
1190 hasLeftIndent = true;
1191 }
1192
1193 if (hasLeftIndent)
1194 attr.SetLeftIndent(leftIndent, leftSubIndent);
1195
1196 value = node->GetAttribute(wxT("rightindent"), wxEmptyString);
1197 if (!value.empty())
1198 attr.SetRightIndent(wxAtoi(value));
1199
1200 value = node->GetAttribute(wxT("parspacingbefore"), wxEmptyString);
1201 if (!value.empty())
1202 attr.SetParagraphSpacingBefore(wxAtoi(value));
1203
1204 value = node->GetAttribute(wxT("parspacingafter"), wxEmptyString);
1205 if (!value.empty())
1206 attr.SetParagraphSpacingAfter(wxAtoi(value));
1207
1208 value = node->GetAttribute(wxT("linespacing"), wxEmptyString);
1209 if (!value.empty())
1210 attr.SetLineSpacing(wxAtoi(value));
1211
1212 value = node->GetAttribute(wxT("bulletstyle"), wxEmptyString);
1213 if (!value.empty())
1214 attr.SetBulletStyle(wxAtoi(value));
1215
1216 value = node->GetAttribute(wxT("bulletnumber"), wxEmptyString);
1217 if (!value.empty())
1218 attr.SetBulletNumber(wxAtoi(value));
1219
1220 value = node->GetAttribute(wxT("bulletsymbol"), wxEmptyString);
1221 if (!value.empty())
1222 {
1223 wxChar ch = wxAtoi(value);
1224 wxString s;
1225 s << ch;
1226 attr.SetBulletText(s);
1227 }
1228
1229 value = node->GetAttribute(wxT("bullettext"), wxEmptyString);
1230 if (!value.empty())
1231 attr.SetBulletText(value);
1232
1233 value = node->GetAttribute(wxT("bulletfont"), wxEmptyString);
1234 if (!value.empty())
1235 attr.SetBulletFont(value);
1236
1237 value = node->GetAttribute(wxT("bulletname"), wxEmptyString);
1238 if (!value.empty())
1239 attr.SetBulletName(value);
1240
1241 value = node->GetAttribute(wxT("parstyle"), wxEmptyString);
1242 if (!value.empty())
1243 attr.SetParagraphStyleName(value);
1244
1245 value = node->GetAttribute(wxT("liststyle"), wxEmptyString);
1246 if (!value.empty())
1247 attr.SetListStyleName(value);
1248
1249 value = node->GetAttribute(wxT("tabs"), wxEmptyString);
1250 if (!value.empty())
1251 {
1252 wxArrayInt tabs;
1253 wxStringTokenizer tkz(value, wxT(","));
1254 while (tkz.HasMoreTokens())
1255 {
1256 wxString token = tkz.GetNextToken();
1257 tabs.Add(wxAtoi(token));
1258 }
1259 attr.SetTabs(tabs);
1260 }
1261
1262 value = node->GetAttribute(wxT("pagebreak"), wxEmptyString);
1263 if (!value.IsEmpty())
1264 {
1265 attr.SetPageBreak(wxAtoi(value) != 0);
1266 }
1267
1268 value = node->GetAttribute(wxT("outlinelevel"), wxEmptyString);
1269 if (!value.IsEmpty())
1270 {
1271 attr.SetOutlineLevel(wxAtoi(value));
1272 }
1273 }
1274
1275 return true;
1276 }
1277
1278 #endif
1279 // wxUSE_STREAMS
1280
1281 #endif
1282 // wxUSE_RICHTEXT && wxUSE_XML
1283