1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/richtext/richtextxml.cpp
3 // Purpose: XML and HTML I/O for wxRichTextCtrl
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
19 #if wxUSE_RICHTEXT && wxUSE_XML
21 #include "wx/richtext/richtextxml.h"
25 #include "wx/module.h"
29 #include "wx/filename.h"
30 #include "wx/clipbrd.h"
31 #include "wx/wfstream.h"
32 #include "wx/sstream.h"
33 #include "wx/txtstrm.h"
34 #include "wx/tokenzr.h"
35 #include "wx/xml/xml.h"
37 IMPLEMENT_DYNAMIC_CLASS(wxRichTextXMLHandler
, wxRichTextFileHandler
)
40 bool wxRichTextXMLHandler::DoLoadFile(wxRichTextBuffer
*buffer
, wxInputStream
& stream
)
45 buffer
->ResetAndClearCommands();
48 wxXmlDocument
* xmlDoc
= new wxXmlDocument
;
51 // This is the encoding to convert to (memory encoding rather than file encoding)
52 wxString
encoding(wxT("UTF-8"));
54 #if !wxUSE_UNICODE && wxUSE_INTL
55 encoding
= wxLocale::GetSystemEncodingName();
58 if (!xmlDoc
->Load(stream
, encoding
))
60 buffer
->ResetAndClearCommands();
65 if (xmlDoc
->GetRoot() && xmlDoc
->GetRoot()->GetType() == wxXML_ELEMENT_NODE
&& xmlDoc
->GetRoot()->GetName() == wxT("richtext"))
67 wxXmlNode
* child
= xmlDoc
->GetRoot()->GetChildren();
70 if (child
->GetType() == wxXML_ELEMENT_NODE
)
72 wxString name
= child
->GetName();
73 if (name
== wxT("richtext-version"))
77 ImportXML(buffer
, child
);
80 child
= child
->GetNext();
91 buffer
->UpdateRanges();
96 /// Recursively import an object
97 bool wxRichTextXMLHandler::ImportXML(wxRichTextBuffer
* buffer
, wxXmlNode
* node
)
99 wxString name
= node
->GetName();
101 bool doneChildren
= false;
103 if (name
== wxT("paragraphlayout"))
105 wxString partial
= node
->GetAttribute(wxT("partialparagraph"), wxEmptyString
);
106 if (partial
== wxT("true"))
107 buffer
->SetPartialParagraph(true);
109 else if (name
== wxT("paragraph"))
111 wxRichTextParagraph
* para
= new wxRichTextParagraph(buffer
);
112 buffer
->AppendChild(para
);
114 GetStyle(para
->GetAttributes(), node
, true);
116 wxXmlNode
* child
= node
->GetChildren();
119 wxString childName
= child
->GetName();
120 if (childName
== wxT("text"))
123 wxXmlNode
* textChild
= child
->GetChildren();
126 if (textChild
->GetType() == wxXML_TEXT_NODE
||
127 textChild
->GetType() == wxXML_CDATA_SECTION_NODE
)
129 wxString text2
= textChild
->GetContent();
131 // Strip whitespace from end
132 if (!text2
.empty() && text2
[text2
.length()-1] == wxT('\n'))
133 text2
= text2
.Mid(0, text2
.length()-1);
135 if (!text2
.empty() && text2
[0] == wxT('"'))
136 text2
= text2
.Mid(1);
137 if (!text2
.empty() && text2
[text2
.length()-1] == wxT('"'))
138 text2
= text2
.Mid(0, text2
.length() - 1);
142 textChild
= textChild
->GetNext();
145 wxRichTextPlainText
* textObject
= new wxRichTextPlainText(text
, para
);
146 GetStyle(textObject
->GetAttributes(), child
, false);
148 para
->AppendChild(textObject
);
150 else if (childName
== wxT("symbol"))
152 // This is a symbol that XML can't read in the normal way
154 wxXmlNode
* textChild
= child
->GetChildren();
157 if (textChild
->GetType() == wxXML_TEXT_NODE
||
158 textChild
->GetType() == wxXML_CDATA_SECTION_NODE
)
160 wxString text2
= textChild
->GetContent();
163 textChild
= textChild
->GetNext();
167 actualText
<< (wxChar
) wxAtoi(text
);
169 wxRichTextPlainText
* textObject
= new wxRichTextPlainText(actualText
, para
);
170 GetStyle(textObject
->GetAttributes(), child
, false);
172 para
->AppendChild(textObject
);
174 else if (childName
== wxT("image"))
176 wxBitmapType imageType
= wxBITMAP_TYPE_PNG
;
177 wxString value
= child
->GetAttribute(wxT("imagetype"), wxEmptyString
);
180 int type
= wxAtoi(value
);
182 // note: 0 == wxBITMAP_TYPE_INVALID
183 if (type
<= 0 || type
>= wxBITMAP_TYPE_MAX
)
185 wxLogWarning("Invalid bitmap type specified for <image> tag: %d", type
);
189 imageType
= (wxBitmapType
)type
;
195 wxXmlNode
* imageChild
= child
->GetChildren();
198 wxString childName
= imageChild
->GetName();
199 if (childName
== wxT("data"))
201 wxXmlNode
* dataChild
= imageChild
->GetChildren();
204 data
= dataChild
->GetContent();
206 dataChild
= dataChild
->GetNext();
210 imageChild
= imageChild
->GetNext();
215 wxRichTextImage
* imageObj
= new wxRichTextImage(para
);
216 GetStyle(imageObj
->GetAttributes(), child
, false);
217 para
->AppendChild(imageObj
);
219 wxStringInputStream
strStream(data
);
221 imageObj
->GetImageBlock().ReadHex(strStream
, data
.length(), imageType
);
224 child
= child
->GetNext();
229 else if (name
== wxT("stylesheet"))
231 if (GetFlags() & wxRICHTEXT_HANDLER_INCLUDE_STYLESHEET
)
233 wxRichTextStyleSheet
* sheet
= new wxRichTextStyleSheet
;
234 wxString sheetName
= node
->GetAttribute(wxT("name"), wxEmptyString
);
235 wxString sheetDescription
= node
->GetAttribute(wxT("description"), wxEmptyString
);
236 sheet
->SetName(sheetName
);
237 sheet
->SetDescription(sheetDescription
);
239 wxXmlNode
* child
= node
->GetChildren();
242 ImportStyleDefinition(sheet
, child
);
244 child
= child
->GetNext();
247 // Notify that styles have changed. If this is vetoed by the app,
248 // the new sheet will be deleted. If it is not vetoed, the
249 // old sheet will be deleted and replaced with the new one.
250 buffer
->SetStyleSheetAndNotify(sheet
);
257 wxXmlNode
* child
= node
->GetChildren();
260 ImportXML(buffer
, child
);
261 child
= child
->GetNext();
268 bool wxRichTextXMLHandler::ImportStyleDefinition(wxRichTextStyleSheet
* sheet
, wxXmlNode
* node
)
270 wxString styleType
= node
->GetName();
271 wxString styleName
= node
->GetAttribute(wxT("name"), wxEmptyString
);
272 wxString baseStyleName
= node
->GetAttribute(wxT("basestyle"), wxEmptyString
);
274 if (styleName
.IsEmpty())
277 if (styleType
== wxT("characterstyle"))
279 wxRichTextCharacterStyleDefinition
* def
= new wxRichTextCharacterStyleDefinition(styleName
);
280 def
->SetBaseStyle(baseStyleName
);
282 wxXmlNode
* child
= node
->GetChildren();
285 if (child
->GetName() == wxT("style"))
288 GetStyle(attr
, child
, false);
291 child
= child
->GetNext();
294 sheet
->AddCharacterStyle(def
);
296 else if (styleType
== wxT("paragraphstyle"))
298 wxRichTextParagraphStyleDefinition
* def
= new wxRichTextParagraphStyleDefinition(styleName
);
300 wxString nextStyleName
= node
->GetAttribute(wxT("nextstyle"), wxEmptyString
);
301 def
->SetNextStyle(nextStyleName
);
302 def
->SetBaseStyle(baseStyleName
);
304 wxXmlNode
* child
= node
->GetChildren();
307 if (child
->GetName() == wxT("style"))
310 GetStyle(attr
, child
, false);
313 child
= child
->GetNext();
316 sheet
->AddParagraphStyle(def
);
318 else if (styleType
== wxT("liststyle"))
320 wxRichTextListStyleDefinition
* def
= new wxRichTextListStyleDefinition(styleName
);
322 wxString nextStyleName
= node
->GetAttribute(wxT("nextstyle"), wxEmptyString
);
323 def
->SetNextStyle(nextStyleName
);
324 def
->SetBaseStyle(baseStyleName
);
326 wxXmlNode
* child
= node
->GetChildren();
329 if (child
->GetName() == wxT("style"))
332 GetStyle(attr
, child
, false);
334 wxString styleLevel
= child
->GetAttribute(wxT("level"), wxEmptyString
);
335 if (styleLevel
.IsEmpty())
341 int level
= wxAtoi(styleLevel
);
342 if (level
> 0 && level
<= 10)
344 def
->SetLevelAttributes(level
-1, attr
);
348 child
= child
->GetNext();
351 sheet
->AddListStyle(def
);
357 //-----------------------------------------------------------------------------
358 // xml support routines
359 //-----------------------------------------------------------------------------
361 bool wxRichTextXMLHandler::HasParam(wxXmlNode
* node
, const wxString
& param
)
363 return (GetParamNode(node
, param
) != NULL
);
366 wxXmlNode
*wxRichTextXMLHandler::GetParamNode(wxXmlNode
* node
, const wxString
& param
)
368 wxCHECK_MSG(node
, NULL
, wxT("You can't access node data before it was initialized!"));
370 wxXmlNode
*n
= node
->GetChildren();
374 if (n
->GetType() == wxXML_ELEMENT_NODE
&& n
->GetName() == param
)
382 wxString
wxRichTextXMLHandler::GetNodeContent(wxXmlNode
*node
)
385 if (n
== NULL
) return wxEmptyString
;
386 n
= n
->GetChildren();
390 if (n
->GetType() == wxXML_TEXT_NODE
||
391 n
->GetType() == wxXML_CDATA_SECTION_NODE
)
392 return n
->GetContent();
395 return wxEmptyString
;
399 wxString
wxRichTextXMLHandler::GetParamValue(wxXmlNode
*node
, const wxString
& param
)
402 return GetNodeContent(node
);
404 return GetNodeContent(GetParamNode(node
, param
));
407 wxString
wxRichTextXMLHandler::GetText(wxXmlNode
*node
, const wxString
& param
, bool WXUNUSED(translate
))
409 wxXmlNode
*parNode
= GetParamNode(node
, param
);
412 wxString
str1(GetNodeContent(parNode
));
416 // For use with earlier versions of wxWidgets
417 #ifndef WXUNUSED_IN_UNICODE
419 #define WXUNUSED_IN_UNICODE(x) WXUNUSED(x)
421 #define WXUNUSED_IN_UNICODE(x) x
425 // write string to output:
426 inline static void OutputString(wxOutputStream
& stream
, const wxString
& str
,
427 wxMBConv
*WXUNUSED_IN_UNICODE(convMem
) = NULL
, wxMBConv
*convFile
= NULL
)
429 if (str
.empty()) return;
433 const wxWX2MBbuf
buf(str
.mb_str(*convFile
));
434 stream
.Write((const char*)buf
, strlen((const char*)buf
));
438 const wxWX2MBbuf
buf(str
.mb_str(wxConvUTF8
));
439 stream
.Write((const char*)buf
, strlen((const char*)buf
));
442 if ( convFile
== NULL
)
443 stream
.Write(str
.mb_str(), str
.Len());
446 wxString
str2(str
.wc_str(*convMem
), *convFile
);
447 stream
.Write(str2
.mb_str(), str2
.Len());
452 // Same as above, but create entities first.
453 // Translates '<' to "<", '>' to ">" and '&' to "&"
454 static void OutputStringEnt(wxOutputStream
& stream
, const wxString
& str
,
455 wxMBConv
*convMem
= NULL
, wxMBConv
*convFile
= NULL
)
463 for (i
= 0; i
< len
; i
++)
467 // Original code excluded "&" but we _do_ want to convert
468 // the ampersand beginning & because otherwise when read in,
469 // the original "&" becomes "&".
471 if (c
== wxT('<') || c
== wxT('>') || c
== wxT('"') ||
472 (c
== wxT('&') /* && (str.Mid(i+1, 4) != wxT("amp;")) */ ))
474 OutputString(stream
, str
.Mid(last
, i
- last
), convMem
, convFile
);
478 OutputString(stream
, wxT("<"), NULL
, NULL
);
481 OutputString(stream
, wxT(">"), NULL
, NULL
);
484 OutputString(stream
, wxT("&"), NULL
, NULL
);
487 OutputString(stream
, wxT("""), NULL
, NULL
);
493 else if (wxUChar(c
) > 127)
495 OutputString(stream
, str
.Mid(last
, i
- last
), convMem
, convFile
);
497 wxString
s(wxT("&#"));
501 s
<< (int) wxUChar(c
);
504 OutputString(stream
, s
, NULL
, NULL
);
508 OutputString(stream
, str
.Mid(last
, i
- last
), convMem
, convFile
);
511 static wxString
AttributeToXML(const wxString
& str
)
519 for (i
= 0; i
< len
; i
++)
523 // Original code excluded "&" but we _do_ want to convert
524 // the ampersand beginning & because otherwise when read in,
525 // the original "&" becomes "&".
527 if (c
== wxT('<') || c
== wxT('>') || c
== wxT('"') ||
528 (c
== wxT('&') /* && (str.Mid(i+1, 4) != wxT("amp;")) */ ))
530 str1
+= str
.Mid(last
, i
- last
);
540 str1
+= wxT("&");
543 str1
+= wxT(""");
549 else if (wxUChar(c
) > 127)
551 str1
+= str
.Mid(last
, i
- last
);
553 wxString
s(wxT("&#"));
557 s
<< (int) wxUChar(c
);
564 str1
+= str
.Mid(last
, i
- last
);
568 inline static void OutputIndentation(wxOutputStream
& stream
, int indent
)
570 wxString str
= wxT("\n");
571 for (int i
= 0; i
< indent
; i
++)
572 str
<< wxT(' ') << wxT(' ');
573 OutputString(stream
, str
, NULL
, NULL
);
576 // Convert a colour to a 6-digit hex string
577 static wxString
ColourToHexString(const wxColour
& col
)
581 hex
+= wxDecToHex(col
.Red());
582 hex
+= wxDecToHex(col
.Green());
583 hex
+= wxDecToHex(col
.Blue());
588 // Convert 6-digit hex string to a colour
589 static wxColour
HexStringToColour(const wxString
& hex
)
591 unsigned char r
= (unsigned char)wxHexToDec(hex
.Mid(0, 2));
592 unsigned char g
= (unsigned char)wxHexToDec(hex
.Mid(2, 2));
593 unsigned char b
= (unsigned char)wxHexToDec(hex
.Mid(4, 2));
595 return wxColour(r
, g
, b
);
598 bool wxRichTextXMLHandler::DoSaveFile(wxRichTextBuffer
*buffer
, wxOutputStream
& stream
)
603 wxString
version(wxT("1.0") ) ;
605 bool deleteConvFile
= false;
606 wxString fileEncoding
;
607 wxMBConv
* convFile
= NULL
;
610 fileEncoding
= wxT("UTF-8");
611 convFile
= & wxConvUTF8
;
613 fileEncoding
= wxT("ISO-8859-1");
614 convFile
= & wxConvISO8859_1
;
617 // If SetEncoding has been called, change the output encoding.
618 if (!m_encoding
.empty() && m_encoding
.Lower() != fileEncoding
.Lower())
620 if (m_encoding
== wxT("<System>"))
623 fileEncoding
= wxLocale::GetSystemEncodingName();
624 // if !wxUSE_INTL, we fall back to UTF-8 or ISO-8859-1 below
629 fileEncoding
= m_encoding
;
632 // GetSystemEncodingName may not have returned a name
633 if (fileEncoding
.empty())
635 fileEncoding
= wxT("UTF-8");
637 fileEncoding
= wxT("ISO-8859-1");
639 convFile
= new wxCSConv(fileEncoding
);
640 deleteConvFile
= true;
644 wxMBConv
* convMem
= wxConvCurrent
;
646 wxMBConv
* convMem
= NULL
;
650 s
.Printf(wxT("<?xml version=\"%s\" encoding=\"%s\"?>\n"),
651 version
, fileEncoding
);
652 OutputString(stream
, s
, NULL
, NULL
);
653 OutputString(stream
, wxT("<richtext version=\"1.0.0.0\" xmlns=\"http://www.wxwidgets.org\">") , NULL
, NULL
);
657 if (buffer
->GetStyleSheet() && (GetFlags() & wxRICHTEXT_HANDLER_INCLUDE_STYLESHEET
))
659 OutputIndentation(stream
, level
);
660 wxString nameAndDescr
;
661 if (!buffer
->GetStyleSheet()->GetName().IsEmpty())
662 nameAndDescr
<< wxT(" name=\"") << buffer
->GetStyleSheet()->GetName() << wxT("\"");
663 if (!buffer
->GetStyleSheet()->GetDescription().IsEmpty())
664 nameAndDescr
<< wxT(" description=\"") << buffer
->GetStyleSheet()->GetDescription() << wxT("\"");
665 OutputString(stream
, wxString(wxT("<stylesheet")) + nameAndDescr
+ wxT(">"), convMem
, convFile
);
669 for (i
= 0; i
< (int) buffer
->GetStyleSheet()->GetCharacterStyleCount(); i
++)
671 wxRichTextCharacterStyleDefinition
* def
= buffer
->GetStyleSheet()->GetCharacterStyle(i
);
672 ExportStyleDefinition(stream
, convMem
, convFile
, def
, level
+ 1);
675 for (i
= 0; i
< (int) buffer
->GetStyleSheet()->GetParagraphStyleCount(); i
++)
677 wxRichTextParagraphStyleDefinition
* def
= buffer
->GetStyleSheet()->GetParagraphStyle(i
);
678 ExportStyleDefinition(stream
, convMem
, convFile
, def
, level
+ 1);
681 for (i
= 0; i
< (int) buffer
->GetStyleSheet()->GetListStyleCount(); i
++)
683 wxRichTextListStyleDefinition
* def
= buffer
->GetStyleSheet()->GetListStyle(i
);
684 ExportStyleDefinition(stream
, convMem
, convFile
, def
, level
+ 1);
687 OutputIndentation(stream
, level
);
688 OutputString(stream
, wxT("</stylesheet>"), convMem
, convFile
);
692 bool success
= ExportXML(stream
, convMem
, convFile
, *buffer
, level
);
694 OutputString(stream
, wxT("\n</richtext>") , NULL
, NULL
);
695 OutputString(stream
, wxT("\n"), NULL
, NULL
);
703 /// Recursively export an object
704 bool wxRichTextXMLHandler::ExportXML(wxOutputStream
& stream
, wxMBConv
* convMem
, wxMBConv
* convFile
, wxRichTextObject
& obj
, int indent
)
707 if (obj
.IsKindOf(CLASSINFO(wxRichTextParagraphLayoutBox
)))
708 objectName
= wxT("paragraphlayout");
709 else if (obj
.IsKindOf(CLASSINFO(wxRichTextParagraph
)))
710 objectName
= wxT("paragraph");
711 else if (obj
.IsKindOf(CLASSINFO(wxRichTextPlainText
)))
712 objectName
= wxT("text");
713 else if (obj
.IsKindOf(CLASSINFO(wxRichTextImage
)))
714 objectName
= wxT("image");
716 objectName
= wxT("object");
718 bool terminateTag
= true;
720 if (obj
.IsKindOf(CLASSINFO(wxRichTextPlainText
)))
722 wxRichTextPlainText
& textObj
= (wxRichTextPlainText
&) obj
;
724 wxString style
= CreateStyle(obj
.GetAttributes(), false);
728 const wxString
& text
= textObj
.GetText();
729 int len
= (int) text
.Length();
734 OutputIndentation(stream
, indent
);
735 OutputString(stream
, wxT("<") + objectName
, convMem
, convFile
);
736 OutputString(stream
, style
+ wxT(">"), convMem
, convFile
);
737 OutputString(stream
, wxT("</text>"), convMem
, convFile
);
739 else for (i
= 0; i
< len
; i
++)
742 int c
= (int) text
[i
];
744 int c
= (int) wxUChar(text
[i
]);
746 if ((c
< 32 || c
== 34) && /* c != 9 && */ c
!= 10 && c
!= 13)
750 wxString
fragment(text
.Mid(last
, i
-last
));
751 if (!fragment
.IsEmpty())
753 OutputIndentation(stream
, indent
);
754 OutputString(stream
, wxT("<") + objectName
, convMem
, convFile
);
756 OutputString(stream
, style
+ wxT(">"), convMem
, convFile
);
758 if (!fragment
.empty() && (fragment
[0] == wxT(' ') || fragment
[fragment
.length()-1] == wxT(' ')))
760 OutputString(stream
, wxT("\""), convMem
, convFile
);
761 OutputStringEnt(stream
, fragment
, convMem
, convFile
);
762 OutputString(stream
, wxT("\""), convMem
, convFile
);
765 OutputStringEnt(stream
, fragment
, convMem
, convFile
);
767 OutputString(stream
, wxT("</text>"), convMem
, convFile
);
772 // Output this character as a number in a separate tag, because XML can't cope
773 // with entities below 32 except for 10 and 13
775 OutputIndentation(stream
, indent
);
776 OutputString(stream
, wxT("<symbol"), convMem
, convFile
);
778 OutputString(stream
, style
+ wxT(">"), convMem
, convFile
);
779 OutputString(stream
, wxString::Format(wxT("%d"), c
), convMem
, convFile
);
781 OutputString(stream
, wxT("</symbol>"), convMem
, convFile
);
789 fragment
= text
.Mid(last
, i
-last
);
793 OutputIndentation(stream
, indent
);
794 OutputString(stream
, wxT("<") + objectName
, convMem
, convFile
);
796 OutputString(stream
, style
+ wxT(">"), convMem
, convFile
);
798 if (!fragment
.empty() && (fragment
[0] == wxT(' ') || fragment
[fragment
.length()-1] == wxT(' ')))
800 OutputString(stream
, wxT("\""), convMem
, convFile
);
801 OutputStringEnt(stream
, fragment
, convMem
, convFile
);
802 OutputString(stream
, wxT("\""), convMem
, convFile
);
805 OutputStringEnt(stream
, fragment
, convMem
, convFile
);
808 terminateTag
= false;
810 else if (obj
.IsKindOf(CLASSINFO(wxRichTextImage
)))
812 wxRichTextImage
& imageObj
= (wxRichTextImage
&) obj
;
814 wxString style
= CreateStyle(obj
.GetAttributes(), false);
816 if (imageObj
.GetImage().Ok() && !imageObj
.GetImageBlock().Ok())
817 imageObj
.MakeBlock();
819 OutputIndentation(stream
, indent
);
820 OutputString(stream
, wxT("<") + objectName
, convMem
, convFile
);
821 if (!imageObj
.GetImageBlock().Ok())
824 OutputString(stream
, style
+ wxT(">"), convMem
, convFile
);
828 OutputString(stream
, wxString::Format(wxT(" imagetype=\"%d\"") + style
+ wxT(">"), (int) imageObj
.GetImageBlock().GetImageType()));
831 OutputIndentation(stream
, indent
+1);
832 OutputString(stream
, wxT("<data>"), convMem
, convFile
);
834 imageObj
.GetImageBlock().WriteHex(stream
);
836 OutputString(stream
, wxT("</data>"), convMem
, convFile
);
838 else if (obj
.IsKindOf(CLASSINFO(wxRichTextCompositeObject
)))
840 OutputIndentation(stream
, indent
);
841 OutputString(stream
, wxT("<") + objectName
, convMem
, convFile
);
844 if (objectName
== wxT("paragraph") || objectName
== wxT("paragraphlayout"))
847 wxString style
= CreateStyle(obj
.GetAttributes(), isPara
);
849 if (objectName
== wxT("paragraphlayout") && ((wxRichTextParagraphLayoutBox
&) obj
).GetPartialParagraph())
850 style
<< wxT(" partialparagraph=\"true\"");
852 OutputString(stream
, style
+ wxT(">"), convMem
, convFile
);
854 wxRichTextCompositeObject
& composite
= (wxRichTextCompositeObject
&) obj
;
856 for (i
= 0; i
< composite
.GetChildCount(); i
++)
858 wxRichTextObject
* child
= composite
.GetChild(i
);
859 ExportXML(stream
, convMem
, convFile
, *child
, indent
+1);
863 if (objectName
!= wxT("text"))
864 OutputIndentation(stream
, indent
);
867 OutputString(stream
, wxT("</") + objectName
+ wxT(">"), convMem
, convFile
);
872 bool wxRichTextXMLHandler::ExportStyleDefinition(wxOutputStream
& stream
, wxMBConv
* convMem
, wxMBConv
* convFile
, wxRichTextStyleDefinition
* def
, int level
)
874 wxRichTextCharacterStyleDefinition
* charDef
= wxDynamicCast(def
, wxRichTextCharacterStyleDefinition
);
875 wxRichTextParagraphStyleDefinition
* paraDef
= wxDynamicCast(def
, wxRichTextParagraphStyleDefinition
);
876 wxRichTextListStyleDefinition
* listDef
= wxDynamicCast(def
, wxRichTextListStyleDefinition
);
878 wxString baseStyle
= def
->GetBaseStyle();
879 wxString baseStyleProp
;
880 if (!baseStyle
.IsEmpty())
881 baseStyleProp
= wxT(" basestyle=\"") + baseStyle
+ wxT("\"");
883 wxString descr
= def
->GetDescription();
885 if (!descr
.IsEmpty())
886 descrProp
= wxT(" description=\"") + descr
+ wxT("\"");
890 OutputIndentation(stream
, level
);
891 OutputString(stream
, wxT("<characterstyle") + baseStyleProp
+ descrProp
+ wxT(">"), convMem
, convFile
);
895 wxString style
= CreateStyle(def
->GetStyle(), false);
897 OutputIndentation(stream
, level
);
898 OutputString(stream
, wxT("<style ") + style
+ wxT(">"), convMem
, convFile
);
900 OutputIndentation(stream
, level
);
901 OutputString(stream
, wxT("</style>"), convMem
, convFile
);
905 OutputIndentation(stream
, level
);
906 OutputString(stream
, wxT("</characterstyle>"), convMem
, convFile
);
910 OutputIndentation(stream
, level
);
912 if (!listDef
->GetNextStyle().IsEmpty())
913 baseStyleProp
<< wxT(" basestyle=\"") << listDef
->GetNextStyle() << wxT("\"");
915 OutputString(stream
, wxT("<liststyle") + baseStyleProp
+ descrProp
+ wxT(">"), convMem
, convFile
);
919 wxString style
= CreateStyle(def
->GetStyle(), false);
921 OutputIndentation(stream
, level
);
922 OutputString(stream
, wxT("<style ") + style
+ wxT(">"), convMem
, convFile
);
924 OutputIndentation(stream
, level
);
925 OutputString(stream
, wxT("</style>"), convMem
, convFile
);
928 for (i
= 0; i
< 10; i
++)
930 wxTextAttr
* levelAttr
= listDef
->GetLevelAttributes(i
);
933 wxString style
= CreateStyle(def
->GetStyle(), false);
934 wxString levelStr
= wxString::Format(wxT(" level=\"%d\" "), (i
+1));
936 OutputIndentation(stream
, level
);
937 OutputString(stream
, wxT("<style ") + levelStr
+ style
+ wxT(">"), convMem
, convFile
);
939 OutputIndentation(stream
, level
);
940 OutputString(stream
, wxT("</style>"), convMem
, convFile
);
946 OutputIndentation(stream
, level
);
947 OutputString(stream
, wxT("</liststyle>"), convMem
, convFile
);
951 OutputIndentation(stream
, level
);
953 if (!paraDef
->GetNextStyle().IsEmpty())
954 baseStyleProp
<< wxT(" basestyle=\"") << paraDef
->GetNextStyle() << wxT("\"");
956 OutputString(stream
, wxT("<paragraphstyle") + baseStyleProp
+ descrProp
+ wxT(">"), convMem
, convFile
);
960 wxString style
= CreateStyle(def
->GetStyle(), false);
962 OutputIndentation(stream
, level
);
963 OutputString(stream
, wxT("<style ") + style
+ wxT(">"), convMem
, convFile
);
965 OutputIndentation(stream
, level
);
966 OutputString(stream
, wxT("</style>"), convMem
, convFile
);
970 OutputIndentation(stream
, level
);
971 OutputString(stream
, wxT("</paragraphstyle>"), convMem
, convFile
);
977 /// Create style parameters
978 wxString
wxRichTextXMLHandler::CreateStyle(const wxTextAttr
& attr
, bool isPara
)
981 if (attr
.HasTextColour() && attr
.GetTextColour().Ok())
983 str
<< wxT(" textcolor=\"#") << ColourToHexString(attr
.GetTextColour()) << wxT("\"");
985 if (attr
.HasBackgroundColour() && attr
.GetBackgroundColour().Ok())
987 str
<< wxT(" bgcolor=\"#") << ColourToHexString(attr
.GetBackgroundColour()) << wxT("\"");
990 if (attr
.HasFontSize())
991 str
<< wxT(" fontsize=\"") << attr
.GetFontSize() << wxT("\"");
993 if (attr
.HasFontFamily())
994 str
<< wxT(" fontfamily=\"") << attr
.GetFont().GetFamily() << wxT("\"");
996 if (attr
.HasFontItalic())
997 str
<< wxT(" fontstyle=\"") << attr
.GetFontStyle() << wxT("\"");
999 if (attr
.HasFontWeight())
1000 str
<< wxT(" fontweight=\"") << attr
.GetFontWeight() << wxT("\"");
1002 if (attr
.HasFontUnderlined())
1003 str
<< wxT(" fontunderlined=\"") << (int) attr
.GetFontUnderlined() << wxT("\"");
1005 if (attr
.HasFontFaceName())
1006 str
<< wxT(" fontface=\"") << attr
.GetFontFaceName() << wxT("\"");
1008 if (attr
.HasTextEffects())
1010 str
<< wxT(" texteffects=\"");
1011 str
<< attr
.GetTextEffects();
1014 str
<< wxT(" texteffectflags=\"");
1015 str
<< attr
.GetTextEffectFlags();
1019 if (!attr
.GetCharacterStyleName().empty())
1020 str
<< wxT(" characterstyle=\"") << wxString(attr
.GetCharacterStyleName()) << wxT("\"");
1023 str
<< wxT(" url=\"") << AttributeToXML(attr
.GetURL()) << wxT("\"");
1027 if (attr
.HasAlignment())
1028 str
<< wxT(" alignment=\"") << (int) attr
.GetAlignment() << wxT("\"");
1030 if (attr
.HasLeftIndent())
1032 str
<< wxT(" leftindent=\"") << (int) attr
.GetLeftIndent() << wxT("\"");
1033 str
<< wxT(" leftsubindent=\"") << (int) attr
.GetLeftSubIndent() << wxT("\"");
1036 if (attr
.HasRightIndent())
1037 str
<< wxT(" rightindent=\"") << (int) attr
.GetRightIndent() << wxT("\"");
1039 if (attr
.HasParagraphSpacingAfter())
1040 str
<< wxT(" parspacingafter=\"") << (int) attr
.GetParagraphSpacingAfter() << wxT("\"");
1042 if (attr
.HasParagraphSpacingBefore())
1043 str
<< wxT(" parspacingbefore=\"") << (int) attr
.GetParagraphSpacingBefore() << wxT("\"");
1045 if (attr
.HasLineSpacing())
1046 str
<< wxT(" linespacing=\"") << (int) attr
.GetLineSpacing() << wxT("\"");
1048 if (attr
.HasBulletStyle())
1049 str
<< wxT(" bulletstyle=\"") << (int) attr
.GetBulletStyle() << wxT("\"");
1051 if (attr
.HasBulletNumber())
1052 str
<< wxT(" bulletnumber=\"") << (int) attr
.GetBulletNumber() << wxT("\"");
1054 if (attr
.HasBulletText())
1056 // If using a bullet symbol, convert to integer in case it's a non-XML-friendly character.
1057 // Otherwise, assume it's XML-friendly text such as outline numbering, e.g. 1.2.3.1
1058 if (!attr
.GetBulletText().IsEmpty() && (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
))
1059 str
<< wxT(" bulletsymbol=\"") << (int) (attr
.GetBulletText()[0]) << wxT("\"");
1061 str
<< wxT(" bullettext=\"") << attr
.GetBulletText() << wxT("\"");
1063 str
<< wxT(" bulletfont=\"") << attr
.GetBulletFont() << wxT("\"");
1066 if (attr
.HasBulletName())
1067 str
<< wxT(" bulletname=\"") << attr
.GetBulletName() << wxT("\"");
1069 if (!attr
.GetParagraphStyleName().empty())
1070 str
<< wxT(" parstyle=\"") << wxString(attr
.GetParagraphStyleName()) << wxT("\"");
1072 if (!attr
.GetListStyleName().empty())
1073 str
<< wxT(" liststyle=\"") << wxString(attr
.GetListStyleName()) << wxT("\"");
1077 str
<< wxT(" tabs=\"");
1079 for (i
= 0; i
< attr
.GetTabs().GetCount(); i
++)
1083 str
<< attr
.GetTabs()[i
];
1088 if (attr
.HasPageBreak())
1090 str
<< wxT(" pagebreak=\"1\"");
1093 if (attr
.HasOutlineLevel())
1094 str
<< wxT(" outlinelevel=\"") << (int) attr
.GetOutlineLevel() << wxT("\"");
1101 /// Replace face name with current name for platform.
1102 /// TODO: introduce a virtual function or settable table to
1103 /// do this comprehensively.
1104 bool wxRichTextFixFaceName(wxString
& facename
)
1106 if (facename
.IsEmpty())
1110 if (facename
== wxT("Times"))
1112 facename
= wxT("Times New Roman");
1115 else if (facename
== wxT("Helvetica"))
1117 facename
= wxT("Arial");
1120 else if (facename
== wxT("Courier"))
1122 facename
= wxT("Courier New");
1128 if (facename
== wxT("Times New Roman"))
1130 facename
= wxT("Times");
1133 else if (facename
== wxT("Arial"))
1135 facename
= wxT("Helvetica");
1138 else if (facename
== wxT("Courier New"))
1140 facename
= wxT("Courier");
1148 /// Get style parameters
1149 bool wxRichTextXMLHandler::GetStyle(wxTextAttr
& attr
, wxXmlNode
* node
, bool isPara
)
1151 wxString fontFacename
;
1153 wxFontFamily fontFamily
= wxFONTFAMILY_DEFAULT
;
1154 wxFontWeight fontWeight
= wxFONTWEIGHT_NORMAL
;
1155 wxFontStyle fontStyle
= wxFONTSTYLE_NORMAL
;
1156 bool fontUnderlined
= false;
1158 // int fontFlags = 0;
1160 fontFacename
= node
->GetAttribute(wxT("fontface"), wxEmptyString
);
1161 if (!fontFacename
.IsEmpty())
1163 attr
.SetFontFaceName(fontFacename
);
1164 if (GetFlags() & wxRICHTEXT_HANDLER_CONVERT_FACENAMES
)
1165 wxRichTextFixFaceName(fontFacename
);
1169 value
= node
->GetAttribute(wxT("fontfamily"), wxEmptyString
);
1172 fontFamily
= (wxFontFamily
)wxAtoi(value
);
1173 attr
.SetFontFamily(fontFamily
);
1176 value
= node
->GetAttribute(wxT("fontstyle"), wxEmptyString
);
1179 fontStyle
= (wxFontStyle
)wxAtoi(value
);
1180 attr
.SetFontStyle(fontStyle
);
1183 value
= node
->GetAttribute(wxT("fontsize"), wxEmptyString
);
1186 fontSize
= wxAtoi(value
);
1187 attr
.SetFontSize(fontSize
);
1190 value
= node
->GetAttribute(wxT("fontweight"), wxEmptyString
);
1193 fontWeight
= (wxFontWeight
)wxAtoi(value
);
1194 attr
.SetFontWeight(fontWeight
);
1197 value
= node
->GetAttribute(wxT("fontunderlined"), wxEmptyString
);
1200 fontUnderlined
= wxAtoi(value
) != 0;
1201 attr
.SetFontUnderlined(fontUnderlined
);
1204 value
= node
->GetAttribute(wxT("textcolor"), wxEmptyString
);
1207 if (value
[0] == wxT('#'))
1208 attr
.SetTextColour(HexStringToColour(value
.Mid(1)));
1210 attr
.SetTextColour(value
);
1213 value
= node
->GetAttribute(wxT("bgcolor"), wxEmptyString
);
1216 if (value
[0] == wxT('#'))
1217 attr
.SetBackgroundColour(HexStringToColour(value
.Mid(1)));
1219 attr
.SetBackgroundColour(value
);
1222 value
= node
->GetAttribute(wxT("characterstyle"), wxEmptyString
);
1224 attr
.SetCharacterStyleName(value
);
1226 value
= node
->GetAttribute(wxT("texteffects"), wxEmptyString
);
1227 if (!value
.IsEmpty())
1229 attr
.SetTextEffects(wxAtoi(value
));
1232 value
= node
->GetAttribute(wxT("texteffectflags"), wxEmptyString
);
1233 if (!value
.IsEmpty())
1235 attr
.SetTextEffectFlags(wxAtoi(value
));
1238 value
= node
->GetAttribute(wxT("url"), wxEmptyString
);
1242 // Set paragraph attributes
1245 value
= node
->GetAttribute(wxT("alignment"), wxEmptyString
);
1247 attr
.SetAlignment((wxTextAttrAlignment
) wxAtoi(value
));
1249 int leftSubIndent
= 0;
1251 bool hasLeftIndent
= false;
1253 value
= node
->GetAttribute(wxT("leftindent"), wxEmptyString
);
1256 leftIndent
= wxAtoi(value
);
1257 hasLeftIndent
= true;
1260 value
= node
->GetAttribute(wxT("leftsubindent"), wxEmptyString
);
1263 leftSubIndent
= wxAtoi(value
);
1264 hasLeftIndent
= true;
1268 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
1270 value
= node
->GetAttribute(wxT("rightindent"), wxEmptyString
);
1272 attr
.SetRightIndent(wxAtoi(value
));
1274 value
= node
->GetAttribute(wxT("parspacingbefore"), wxEmptyString
);
1276 attr
.SetParagraphSpacingBefore(wxAtoi(value
));
1278 value
= node
->GetAttribute(wxT("parspacingafter"), wxEmptyString
);
1280 attr
.SetParagraphSpacingAfter(wxAtoi(value
));
1282 value
= node
->GetAttribute(wxT("linespacing"), wxEmptyString
);
1284 attr
.SetLineSpacing(wxAtoi(value
));
1286 value
= node
->GetAttribute(wxT("bulletstyle"), wxEmptyString
);
1288 attr
.SetBulletStyle(wxAtoi(value
));
1290 value
= node
->GetAttribute(wxT("bulletnumber"), wxEmptyString
);
1292 attr
.SetBulletNumber(wxAtoi(value
));
1294 value
= node
->GetAttribute(wxT("bulletsymbol"), wxEmptyString
);
1297 wxChar ch
= wxAtoi(value
);
1300 attr
.SetBulletText(s
);
1303 value
= node
->GetAttribute(wxT("bullettext"), wxEmptyString
);
1305 attr
.SetBulletText(value
);
1307 value
= node
->GetAttribute(wxT("bulletfont"), wxEmptyString
);
1309 attr
.SetBulletFont(value
);
1311 value
= node
->GetAttribute(wxT("bulletname"), wxEmptyString
);
1313 attr
.SetBulletName(value
);
1315 value
= node
->GetAttribute(wxT("parstyle"), wxEmptyString
);
1317 attr
.SetParagraphStyleName(value
);
1319 value
= node
->GetAttribute(wxT("liststyle"), wxEmptyString
);
1321 attr
.SetListStyleName(value
);
1323 value
= node
->GetAttribute(wxT("tabs"), wxEmptyString
);
1327 wxStringTokenizer
tkz(value
, wxT(","));
1328 while (tkz
.HasMoreTokens())
1330 wxString token
= tkz
.GetNextToken();
1331 tabs
.Add(wxAtoi(token
));
1336 value
= node
->GetAttribute(wxT("pagebreak"), wxEmptyString
);
1337 if (!value
.IsEmpty())
1339 attr
.SetPageBreak(wxAtoi(value
) != 0);
1342 value
= node
->GetAttribute(wxT("outlinelevel"), wxEmptyString
);
1343 if (!value
.IsEmpty())
1345 attr
.SetOutlineLevel(wxAtoi(value
));
1356 // wxUSE_RICHTEXT && wxUSE_XML