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"
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"
36 IMPLEMENT_DYNAMIC_CLASS(wxRichTextXMLHandler
, wxRichTextFileHandler
)
39 bool wxRichTextXMLHandler::DoLoadFile(wxRichTextBuffer
*buffer
, wxInputStream
& stream
)
44 buffer
->ResetAndClearCommands();
46 wxXmlDocument
* xmlDoc
= new wxXmlDocument
;
49 // This is the encoding to convert to (memory encoding rather than file encoding)
50 wxString
encoding(wxT("UTF-8"));
52 #if !wxUSE_UNICODE && wxUSE_INTL
53 encoding
= wxLocale::GetSystemEncodingName();
56 if (!xmlDoc
->Load(stream
, encoding
))
62 if (xmlDoc
->GetRoot() && xmlDoc
->GetRoot()->GetType() == wxXML_ELEMENT_NODE
&& xmlDoc
->GetRoot()->GetName() == wxT("richtext"))
64 wxXmlNode
* child
= xmlDoc
->GetRoot()->GetChildren();
67 if (child
->GetType() == wxXML_ELEMENT_NODE
)
69 wxString name
= child
->GetName();
70 if (name
== wxT("richtext-version"))
74 ImportXML(buffer
, child
);
77 child
= child
->GetNext();
88 buffer
->UpdateRanges();
93 /// Recursively import an object
94 bool wxRichTextXMLHandler::ImportXML(wxRichTextBuffer
* buffer
, wxXmlNode
* node
)
96 wxString name
= node
->GetName();
98 bool doneChildren
= false;
100 if (name
== wxT("paragraphlayout"))
102 wxString partial
= node
->GetPropVal(wxT("partialparagraph"), wxEmptyString
);
103 if (partial
== wxT("true"))
104 buffer
->SetPartialParagraph(true);
106 else if (name
== wxT("paragraph"))
108 wxRichTextParagraph
* para
= new wxRichTextParagraph(buffer
);
109 buffer
->AppendChild(para
);
111 GetStyle(para
->GetAttributes(), node
, true);
113 wxXmlNode
* child
= node
->GetChildren();
116 wxString childName
= child
->GetName();
117 if (childName
== wxT("text"))
120 wxXmlNode
* textChild
= child
->GetChildren();
123 if (textChild
->GetType() == wxXML_TEXT_NODE
||
124 textChild
->GetType() == wxXML_CDATA_SECTION_NODE
)
126 wxString text2
= textChild
->GetContent();
128 // Strip whitespace from end
129 if (!text2
.empty() && text2
[text2
.length()-1] == wxT('\n'))
130 text2
= text2
.Mid(0, text2
.length()-1);
132 if (!text2
.empty() && text2
[0] == wxT('"'))
133 text2
= text2
.Mid(1);
134 if (!text2
.empty() && text2
[text2
.length()-1] == wxT('"'))
135 text2
= text2
.Mid(0, text2
.length() - 1);
139 textChild
= textChild
->GetNext();
142 wxRichTextPlainText
* textObject
= new wxRichTextPlainText(text
, para
);
143 GetStyle(textObject
->GetAttributes(), child
, false);
145 para
->AppendChild(textObject
);
147 else if (childName
== wxT("symbol"))
149 // This is a symbol that XML can't read in the normal way
151 wxXmlNode
* textChild
= child
->GetChildren();
154 if (textChild
->GetType() == wxXML_TEXT_NODE
||
155 textChild
->GetType() == wxXML_CDATA_SECTION_NODE
)
157 wxString text2
= textChild
->GetContent();
160 textChild
= textChild
->GetNext();
164 actualText
<< (wxChar
) wxAtoi(text
);
166 wxRichTextPlainText
* textObject
= new wxRichTextPlainText(actualText
, para
);
167 GetStyle(textObject
->GetAttributes(), child
, false);
169 para
->AppendChild(textObject
);
171 else if (childName
== wxT("image"))
173 int imageType
= wxBITMAP_TYPE_PNG
;
174 wxString value
= node
->GetPropVal(wxT("imagetype"), wxEmptyString
);
176 imageType
= wxAtoi(value
);
180 wxXmlNode
* imageChild
= child
->GetChildren();
183 wxString childName
= imageChild
->GetName();
184 if (childName
== wxT("data"))
186 wxXmlNode
* dataChild
= imageChild
->GetChildren();
189 data
= dataChild
->GetContent();
191 dataChild
= dataChild
->GetNext();
195 imageChild
= imageChild
->GetNext();
200 wxRichTextImage
* imageObj
= new wxRichTextImage(para
);
201 para
->AppendChild(imageObj
);
203 wxStringInputStream
strStream(data
);
205 imageObj
->GetImageBlock().ReadHex(strStream
, data
.length(), imageType
);
208 child
= child
->GetNext();
213 else if (name
== wxT("stylesheet"))
215 if (GetFlags() & wxRICHTEXT_HANDLER_INCLUDE_STYLESHEET
)
217 wxRichTextStyleSheet
* sheet
= new wxRichTextStyleSheet
;
219 wxXmlNode
* child
= node
->GetChildren();
222 ImportStyleDefinition(sheet
, child
);
224 child
= child
->GetNext();
227 // Notify that styles have changed. If this is vetoed by the app,
228 // the new sheet will be deleted. If it is not vetoed, the
229 // old sheet will be deleted and replaced with the new one.
230 buffer
->SetStyleSheetAndNotify(sheet
);
237 wxXmlNode
* child
= node
->GetChildren();
240 ImportXML(buffer
, child
);
241 child
= child
->GetNext();
248 bool wxRichTextXMLHandler::ImportStyleDefinition(wxRichTextStyleSheet
* sheet
, wxXmlNode
* node
)
250 wxString styleType
= node
->GetName();
251 wxString styleName
= node
->GetPropVal(wxT("name"), wxEmptyString
);
252 wxString baseStyleName
= node
->GetPropVal(wxT("basestyle"), wxEmptyString
);
254 if (styleName
.IsEmpty())
257 if (styleType
== wxT("characterstyle"))
259 wxRichTextCharacterStyleDefinition
* def
= new wxRichTextCharacterStyleDefinition(styleName
);
260 def
->SetBaseStyle(baseStyleName
);
262 wxXmlNode
* child
= node
->GetChildren();
265 if (child
->GetName() == wxT("style"))
268 GetStyle(attr
, child
, false);
271 child
= child
->GetNext();
274 sheet
->AddCharacterStyle(def
);
276 else if (styleType
== wxT("paragraphstyle"))
278 wxRichTextParagraphStyleDefinition
* def
= new wxRichTextParagraphStyleDefinition(styleName
);
280 wxString nextStyleName
= node
->GetPropVal(wxT("nextstyle"), wxEmptyString
);
281 def
->SetNextStyle(nextStyleName
);
282 def
->SetBaseStyle(baseStyleName
);
284 wxXmlNode
* child
= node
->GetChildren();
287 if (child
->GetName() == wxT("style"))
290 GetStyle(attr
, child
, false);
293 child
= child
->GetNext();
296 sheet
->AddParagraphStyle(def
);
298 else if (styleType
== wxT("liststyle"))
300 wxRichTextListStyleDefinition
* def
= new wxRichTextListStyleDefinition(styleName
);
302 wxString nextStyleName
= node
->GetPropVal(wxT("nextstyle"), wxEmptyString
);
303 def
->SetNextStyle(nextStyleName
);
304 def
->SetBaseStyle(baseStyleName
);
306 wxXmlNode
* child
= node
->GetChildren();
309 if (child
->GetName() == wxT("style"))
312 GetStyle(attr
, child
, false);
314 wxString styleLevel
= child
->GetPropVal(wxT("level"), wxEmptyString
);
315 if (styleLevel
.IsEmpty())
321 int level
= wxAtoi(styleLevel
);
322 if (level
> 0 && level
<= 10)
324 def
->SetLevelAttributes(level
-1, attr
);
328 child
= child
->GetNext();
331 sheet
->AddListStyle(def
);
337 //-----------------------------------------------------------------------------
338 // xml support routines
339 //-----------------------------------------------------------------------------
341 bool wxRichTextXMLHandler::HasParam(wxXmlNode
* node
, const wxString
& param
)
343 return (GetParamNode(node
, param
) != NULL
);
346 wxXmlNode
*wxRichTextXMLHandler::GetParamNode(wxXmlNode
* node
, const wxString
& param
)
348 wxCHECK_MSG(node
, NULL
, wxT("You can't access node data before it was initialized!"));
350 wxXmlNode
*n
= node
->GetChildren();
354 if (n
->GetType() == wxXML_ELEMENT_NODE
&& n
->GetName() == param
)
362 wxString
wxRichTextXMLHandler::GetNodeContent(wxXmlNode
*node
)
365 if (n
== NULL
) return wxEmptyString
;
366 n
= n
->GetChildren();
370 if (n
->GetType() == wxXML_TEXT_NODE
||
371 n
->GetType() == wxXML_CDATA_SECTION_NODE
)
372 return n
->GetContent();
375 return wxEmptyString
;
379 wxString
wxRichTextXMLHandler::GetParamValue(wxXmlNode
*node
, const wxString
& param
)
382 return GetNodeContent(node
);
384 return GetNodeContent(GetParamNode(node
, param
));
387 wxString
wxRichTextXMLHandler::GetText(wxXmlNode
*node
, const wxString
& param
, bool WXUNUSED(translate
))
389 wxXmlNode
*parNode
= GetParamNode(node
, param
);
392 wxString
str1(GetNodeContent(parNode
));
396 // For use with earlier versions of wxWidgets
397 #ifndef WXUNUSED_IN_UNICODE
399 #define WXUNUSED_IN_UNICODE(x) WXUNUSED(x)
401 #define WXUNUSED_IN_UNICODE(x) x
405 // write string to output:
406 inline static void OutputString(wxOutputStream
& stream
, const wxString
& str
,
407 wxMBConv
*WXUNUSED_IN_UNICODE(convMem
) = NULL
, wxMBConv
*convFile
= NULL
)
409 if (str
.empty()) return;
413 const wxWX2MBbuf
buf(str
.mb_str(*convFile
));
414 stream
.Write((const char*)buf
, strlen((const char*)buf
));
418 const wxWX2MBbuf
buf(str
.mb_str(wxConvUTF8
));
419 stream
.Write((const char*)buf
, strlen((const char*)buf
));
422 if ( convFile
== NULL
)
423 stream
.Write(str
.mb_str(), str
.Len());
426 wxString
str2(str
.wc_str(*convMem
), *convFile
);
427 stream
.Write(str2
.mb_str(), str2
.Len());
432 // Same as above, but create entities first.
433 // Translates '<' to "<", '>' to ">" and '&' to "&"
434 static void OutputStringEnt(wxOutputStream
& stream
, const wxString
& str
,
435 wxMBConv
*convMem
= NULL
, wxMBConv
*convFile
= NULL
)
443 for (i
= 0; i
< len
; i
++)
447 // Original code excluded "&" but we _do_ want to convert
448 // the ampersand beginning & because otherwise when read in,
449 // the original "&" becomes "&".
451 if (c
== wxT('<') || c
== wxT('>') || c
== wxT('"') ||
452 (c
== wxT('&') /* && (str.Mid(i+1, 4) != wxT("amp;")) */ ))
454 OutputString(stream
, str
.Mid(last
, i
- last
), convMem
, convFile
);
458 OutputString(stream
, wxT("<"), NULL
, NULL
);
461 OutputString(stream
, wxT(">"), NULL
, NULL
);
464 OutputString(stream
, wxT("&"), NULL
, NULL
);
467 OutputString(stream
, wxT("""), NULL
, NULL
);
473 else if (wxUChar(c
) > 127)
475 OutputString(stream
, str
.Mid(last
, i
- last
), convMem
, convFile
);
477 wxString
s(wxT("&#"));
480 OutputString(stream
, s
, NULL
, NULL
);
484 OutputString(stream
, str
.Mid(last
, i
- last
), convMem
, convFile
);
487 inline static void OutputIndentation(wxOutputStream
& stream
, int indent
)
489 wxString str
= wxT("\n");
490 for (int i
= 0; i
< indent
; i
++)
491 str
<< wxT(' ') << wxT(' ');
492 OutputString(stream
, str
, NULL
, NULL
);
495 // Convert a colour to a 6-digit hex string
496 static wxString
ColourToHexString(const wxColour
& col
)
500 hex
+= wxDecToHex(col
.Red());
501 hex
+= wxDecToHex(col
.Green());
502 hex
+= wxDecToHex(col
.Blue());
507 // Convert 6-digit hex string to a colour
508 static wxColour
HexStringToColour(const wxString
& hex
)
510 unsigned char r
= (unsigned char)wxHexToDec(hex
.Mid(0, 2));
511 unsigned char g
= (unsigned char)wxHexToDec(hex
.Mid(2, 2));
512 unsigned char b
= (unsigned char)wxHexToDec(hex
.Mid(4, 2));
514 return wxColour(r
, g
, b
);
517 bool wxRichTextXMLHandler::DoSaveFile(wxRichTextBuffer
*buffer
, wxOutputStream
& stream
)
522 wxString
version(wxT("1.0") ) ;
524 bool deleteConvFile
= false;
525 wxString fileEncoding
;
526 wxMBConv
* convFile
= NULL
;
529 fileEncoding
= wxT("UTF-8");
530 convFile
= & wxConvUTF8
;
532 fileEncoding
= wxT("ISO-8859-1");
533 convFile
= & wxConvISO8859_1
;
536 // If SetEncoding has been called, change the output encoding.
537 if (!m_encoding
.empty() && m_encoding
.Lower() != fileEncoding
.Lower())
539 if (m_encoding
== wxT("<System>"))
541 fileEncoding
= wxLocale::GetSystemEncodingName();
545 fileEncoding
= m_encoding
;
548 // GetSystemEncodingName may not have returned a name
549 if (fileEncoding
.empty())
551 fileEncoding
= wxT("UTF-8");
553 fileEncoding
= wxT("ISO-8859-1");
555 convFile
= new wxCSConv(fileEncoding
);
556 deleteConvFile
= true;
560 wxMBConv
* convMem
= wxConvCurrent
;
562 wxMBConv
* convMem
= NULL
;
566 s
.Printf(wxT("<?xml version=\"%s\" encoding=\"%s\"?>\n"),
567 (const wxChar
*) version
, (const wxChar
*) fileEncoding
);
568 OutputString(stream
, s
, NULL
, NULL
);
569 OutputString(stream
, wxT("<richtext version=\"1.0.0.0\" xmlns=\"http://www.wxwidgets.org\">") , NULL
, NULL
);
573 if (buffer
->GetStyleSheet() && (GetFlags() & wxRICHTEXT_HANDLER_INCLUDE_STYLESHEET
))
575 OutputIndentation(stream
, level
);
576 OutputString(stream
, wxT("<stylesheet>"), convMem
, convFile
);
580 for (i
= 0; i
< (int) buffer
->GetStyleSheet()->GetCharacterStyleCount(); i
++)
582 wxRichTextCharacterStyleDefinition
* def
= buffer
->GetStyleSheet()->GetCharacterStyle(i
);
583 ExportStyleDefinition(stream
, convMem
, convFile
, def
, level
+ 1);
586 for (i
= 0; i
< (int) buffer
->GetStyleSheet()->GetParagraphStyleCount(); i
++)
588 wxRichTextParagraphStyleDefinition
* def
= buffer
->GetStyleSheet()->GetParagraphStyle(i
);
589 ExportStyleDefinition(stream
, convMem
, convFile
, def
, level
+ 1);
592 for (i
= 0; i
< (int) buffer
->GetStyleSheet()->GetListStyleCount(); i
++)
594 wxRichTextListStyleDefinition
* def
= buffer
->GetStyleSheet()->GetListStyle(i
);
595 ExportStyleDefinition(stream
, convMem
, convFile
, def
, level
+ 1);
598 OutputIndentation(stream
, level
);
599 OutputString(stream
, wxT("</stylesheet>"), convMem
, convFile
);
603 bool success
= ExportXML(stream
, convMem
, convFile
, *buffer
, level
);
605 OutputString(stream
, wxT("\n</richtext>") , NULL
, NULL
);
606 OutputString(stream
, wxT("\n"), NULL
, NULL
);
614 /// Recursively export an object
615 bool wxRichTextXMLHandler::ExportXML(wxOutputStream
& stream
, wxMBConv
* convMem
, wxMBConv
* convFile
, wxRichTextObject
& obj
, int indent
)
618 if (obj
.IsKindOf(CLASSINFO(wxRichTextParagraphLayoutBox
)))
619 objectName
= wxT("paragraphlayout");
620 else if (obj
.IsKindOf(CLASSINFO(wxRichTextParagraph
)))
621 objectName
= wxT("paragraph");
622 else if (obj
.IsKindOf(CLASSINFO(wxRichTextPlainText
)))
623 objectName
= wxT("text");
624 else if (obj
.IsKindOf(CLASSINFO(wxRichTextImage
)))
625 objectName
= wxT("image");
627 objectName
= wxT("object");
629 bool terminateTag
= true;
631 if (obj
.IsKindOf(CLASSINFO(wxRichTextPlainText
)))
633 wxRichTextPlainText
& textObj
= (wxRichTextPlainText
&) obj
;
635 wxString style
= CreateStyle(obj
.GetAttributes(), false);
639 const wxString
& text
= textObj
.GetText();
640 int len
= (int) text
.Length();
641 for (i
= 0; i
< len
; i
++)
643 int c
= (int) text
[i
];
644 if (c
< 32 && c
!= 9 && c
!= 10 && c
!= 13)
648 OutputIndentation(stream
, indent
);
649 OutputString(stream
, wxT("<") + objectName
, convMem
, convFile
);
651 OutputString(stream
, style
+ wxT(">"), convMem
, convFile
);
653 wxString
fragment(text
.Mid(last
, i
-last
));
654 if (!fragment
.empty() && (fragment
[0] == wxT(' ') || fragment
[fragment
.length()-1] == wxT(' ')))
656 OutputString(stream
, wxT("\""), convMem
, convFile
);
657 OutputStringEnt(stream
, fragment
, convMem
, convFile
);
658 OutputString(stream
, wxT("\""), convMem
, convFile
);
661 OutputStringEnt(stream
, fragment
, convMem
, convFile
);
663 OutputString(stream
, wxT("</text>"), convMem
, convFile
);
667 // Output this character as a number in a separate tag, because XML can't cope
668 // with entities below 32 except for 9, 10 and 13
670 OutputIndentation(stream
, indent
);
671 OutputString(stream
, wxT("<symbol"), convMem
, convFile
);
673 OutputString(stream
, style
+ wxT(">"), convMem
, convFile
);
674 OutputString(stream
, wxString::Format(wxT("%d"), c
), convMem
, convFile
);
676 OutputString(stream
, wxT("</symbol>"), convMem
, convFile
);
684 fragment
= text
.Mid(last
, i
-last
);
688 OutputIndentation(stream
, indent
);
689 OutputString(stream
, wxT("<") + objectName
, convMem
, convFile
);
691 OutputString(stream
, style
+ wxT(">"), convMem
, convFile
);
693 if (!fragment
.empty() && (fragment
[0] == wxT(' ') || fragment
[fragment
.length()-1] == wxT(' ')))
695 OutputString(stream
, wxT("\""), convMem
, convFile
);
696 OutputStringEnt(stream
, fragment
, convMem
, convFile
);
697 OutputString(stream
, wxT("\""), convMem
, convFile
);
700 OutputStringEnt(stream
, fragment
, convMem
, convFile
);
703 terminateTag
= false;
705 else if (obj
.IsKindOf(CLASSINFO(wxRichTextImage
)))
707 wxRichTextImage
& imageObj
= (wxRichTextImage
&) obj
;
709 if (imageObj
.GetImage().Ok() && !imageObj
.GetImageBlock().Ok())
710 imageObj
.MakeBlock();
712 OutputIndentation(stream
, indent
);
713 OutputString(stream
, wxT("<") + objectName
, convMem
, convFile
);
714 if (!imageObj
.GetImageBlock().Ok())
717 OutputString(stream
, wxT(">"), convMem
, convFile
);
721 OutputString(stream
, wxString::Format(wxT(" imagetype=\"%d\">"), (int) imageObj
.GetImageBlock().GetImageType()));
724 OutputIndentation(stream
, indent
+1);
725 OutputString(stream
, wxT("<data>"), convMem
, convFile
);
727 imageObj
.GetImageBlock().WriteHex(stream
);
729 OutputString(stream
, wxT("</data>"), convMem
, convFile
);
731 else if (obj
.IsKindOf(CLASSINFO(wxRichTextCompositeObject
)))
733 OutputIndentation(stream
, indent
);
734 OutputString(stream
, wxT("<") + objectName
, convMem
, convFile
);
737 if (objectName
== wxT("paragraph") || objectName
== wxT("paragraphlayout"))
740 wxString style
= CreateStyle(obj
.GetAttributes(), isPara
);
742 if (objectName
== wxT("paragraphlayout") && ((wxRichTextParagraphLayoutBox
&) obj
).GetPartialParagraph())
743 style
<< wxT(" partialparagraph=\"true\"");
745 OutputString(stream
, style
+ wxT(">"), convMem
, convFile
);
747 wxRichTextCompositeObject
& composite
= (wxRichTextCompositeObject
&) obj
;
749 for (i
= 0; i
< composite
.GetChildCount(); i
++)
751 wxRichTextObject
* child
= composite
.GetChild(i
);
752 ExportXML(stream
, convMem
, convFile
, *child
, indent
+1);
756 if (objectName
!= wxT("text"))
757 OutputIndentation(stream
, indent
);
760 OutputString(stream
, wxT("</") + objectName
+ wxT(">"), convMem
, convFile
);
765 bool wxRichTextXMLHandler::ExportStyleDefinition(wxOutputStream
& stream
, wxMBConv
* convMem
, wxMBConv
* convFile
, wxRichTextStyleDefinition
* def
, int level
)
767 wxRichTextCharacterStyleDefinition
* charDef
= wxDynamicCast(def
, wxRichTextCharacterStyleDefinition
);
768 wxRichTextParagraphStyleDefinition
* paraDef
= wxDynamicCast(def
, wxRichTextParagraphStyleDefinition
);
769 wxRichTextListStyleDefinition
* listDef
= wxDynamicCast(def
, wxRichTextListStyleDefinition
);
771 wxString baseStyle
= def
->GetBaseStyle();
772 wxString baseStyleProp
;
773 if (!baseStyle
.IsEmpty())
774 baseStyleProp
= wxT(" basestyle=\"") + baseStyle
+ wxT("\"");
778 OutputIndentation(stream
, level
);
779 OutputString(stream
, wxT("<characterstyle") + baseStyleProp
+ wxT(">"), convMem
, convFile
);
783 wxString style
= CreateStyle(def
->GetStyle(), false);
785 OutputIndentation(stream
, level
);
786 OutputString(stream
, wxT("<style ") + style
+ wxT(">"), convMem
, convFile
);
788 OutputIndentation(stream
, level
);
789 OutputString(stream
, wxT("</style>"), convMem
, convFile
);
793 OutputIndentation(stream
, level
);
794 OutputString(stream
, wxT("</characterstyle>"), convMem
, convFile
);
798 OutputIndentation(stream
, level
);
800 if (!listDef
->GetNextStyle().IsEmpty())
801 baseStyleProp
<< wxT(" basestyle=\"") << listDef
->GetNextStyle() << wxT("\"");
803 OutputString(stream
, wxT("<liststyle") + baseStyleProp
+ wxT(">"), convMem
, convFile
);
807 wxString style
= CreateStyle(def
->GetStyle(), false);
809 OutputIndentation(stream
, level
);
810 OutputString(stream
, wxT("<style ") + style
+ wxT(">"), convMem
, convFile
);
812 OutputIndentation(stream
, level
);
813 OutputString(stream
, wxT("</style>"), convMem
, convFile
);
816 for (i
= 0; i
< 10; i
++)
818 wxRichTextAttr
* levelAttr
= listDef
->GetLevelAttributes(i
);
821 wxString style
= CreateStyle(def
->GetStyle(), false);
822 wxString levelStr
= wxString::Format(wxT(" level=\"%d\" "), (i
+1));
824 OutputIndentation(stream
, level
);
825 OutputString(stream
, wxT("<style ") + levelStr
+ style
+ wxT(">"), convMem
, convFile
);
827 OutputIndentation(stream
, level
);
828 OutputString(stream
, wxT("</style>"), convMem
, convFile
);
834 OutputIndentation(stream
, level
);
835 OutputString(stream
, wxT("</liststyle>"), convMem
, convFile
);
839 OutputIndentation(stream
, level
);
841 if (!listDef
->GetNextStyle().IsEmpty())
842 baseStyleProp
<< wxT(" basestyle=\"") << listDef
->GetNextStyle() << wxT("\"");
844 OutputString(stream
, wxT("<paragraphstyle") + baseStyleProp
+ wxT(">"), convMem
, convFile
);
848 wxString style
= CreateStyle(def
->GetStyle(), false);
850 OutputIndentation(stream
, level
);
851 OutputString(stream
, wxT("<style ") + style
+ wxT(">"), convMem
, convFile
);
853 OutputIndentation(stream
, level
);
854 OutputString(stream
, wxT("</style>"), convMem
, convFile
);
858 OutputIndentation(stream
, level
);
859 OutputString(stream
, wxT("</paragraphstyle>"), convMem
, convFile
);
865 /// Create style parameters
866 wxString
wxRichTextXMLHandler::CreateStyle(const wxTextAttrEx
& attr
, bool isPara
)
869 if (attr
.HasTextColour() && attr
.GetTextColour().Ok())
871 str
<< wxT(" textcolor=\"#") << ColourToHexString(attr
.GetTextColour()) << wxT("\"");
873 if (attr
.HasBackgroundColour() && attr
.GetBackgroundColour().Ok())
875 str
<< wxT(" bgcolor=\"#") << ColourToHexString(attr
.GetBackgroundColour()) << wxT("\"");
878 if (attr
.GetFont().Ok())
881 str
<< wxT(" fontsize=\"") << attr
.GetFont().GetPointSize() << wxT("\"");
883 //if (attr.HasFamily())
884 // str << wxT(" fontfamily=\"") << attr.GetFont().GetFamily() << wxT("\"");
886 if (attr
.HasItalic())
887 str
<< wxT(" fontstyle=\"") << attr
.GetFont().GetStyle() << wxT("\"");
889 if (attr
.HasWeight())
890 str
<< wxT(" fontweight=\"") << attr
.GetFont().GetWeight() << wxT("\"");
892 if (attr
.HasUnderlined())
893 str
<< wxT(" fontunderlined=\"") << (int) attr
.GetFont().GetUnderlined() << wxT("\"");
895 if (attr
.HasFaceName())
896 str
<< wxT(" fontface=\"") << attr
.GetFont().GetFaceName() << wxT("\"");
899 if (!attr
.GetCharacterStyleName().empty())
900 str
<< wxT(" characterstyle=\"") << wxString(attr
.GetCharacterStyleName()) << wxT("\"");
904 if (attr
.HasAlignment())
905 str
<< wxT(" alignment=\"") << (int) attr
.GetAlignment() << wxT("\"");
907 if (attr
.HasLeftIndent())
909 str
<< wxT(" leftindent=\"") << (int) attr
.GetLeftIndent() << wxT("\"");
910 str
<< wxT(" leftsubindent=\"") << (int) attr
.GetLeftSubIndent() << wxT("\"");
913 if (attr
.HasRightIndent())
914 str
<< wxT(" rightindent=\"") << (int) attr
.GetRightIndent() << wxT("\"");
916 if (attr
.HasParagraphSpacingAfter())
917 str
<< wxT(" parspacingafter=\"") << (int) attr
.GetParagraphSpacingAfter() << wxT("\"");
919 if (attr
.HasParagraphSpacingBefore())
920 str
<< wxT(" parspacingbefore=\"") << (int) attr
.GetParagraphSpacingBefore() << wxT("\"");
922 if (attr
.HasLineSpacing())
923 str
<< wxT(" linespacing=\"") << (int) attr
.GetLineSpacing() << wxT("\"");
925 if (attr
.HasBulletStyle())
926 str
<< wxT(" bulletstyle=\"") << (int) attr
.GetBulletStyle() << wxT("\"");
928 if (attr
.HasBulletNumber())
929 str
<< wxT(" bulletnumber=\"") << (int) attr
.GetBulletNumber() << wxT("\"");
931 if (attr
.HasBulletText())
933 // If using a bullet symbol, convert to integer in case it's a non-XML-friendly character.
934 // Otherwise, assume it's XML-friendly text such as outline numbering, e.g. 1.2.3.1
935 if (!attr
.GetBulletText().IsEmpty() && (attr
.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL
))
936 str
<< wxT(" bulletsymbol=\"") << (int) (attr
.GetBulletText()[0]) << wxT("\"");
938 str
<< wxT(" bullettext=\"") << attr
.GetBulletText() << wxT("\"");
940 str
<< wxT(" bulletfont=\"") << attr
.GetBulletFont() << wxT("\"");
943 if (attr
.HasBulletName())
944 str
<< wxT(" bulletname=\"") << attr
.GetBulletName() << wxT("\"");
947 str
<< wxT(" url=\"") << attr
.GetURL() << wxT("\"");
949 if (!attr
.GetParagraphStyleName().empty())
950 str
<< wxT(" parstyle=\"") << wxString(attr
.GetParagraphStyleName()) << wxT("\"");
952 if (!attr
.GetListStyleName().empty())
953 str
<< wxT(" liststyle=\"") << wxString(attr
.GetListStyleName()) << wxT("\"");
957 str
<< wxT(" tabs=\"");
959 for (i
= 0; i
< attr
.GetTabs().GetCount(); i
++)
963 str
<< attr
.GetTabs()[i
];
972 /// Get style parameters
973 bool wxRichTextXMLHandler::GetStyle(wxTextAttrEx
& attr
, wxXmlNode
* node
, bool isPara
)
975 wxString fontFacename
;
977 int fontFamily
= wxDEFAULT
;
978 int fontWeight
= wxNORMAL
;
979 int fontStyle
= wxNORMAL
;
980 bool fontUnderlined
= false;
984 fontFacename
= node
->GetPropVal(wxT("fontface"), wxEmptyString
);
985 if (!fontFacename
.IsEmpty())
986 fontFlags
|= wxTEXT_ATTR_FONT_FACE
;
989 //value = node->GetPropVal(wxT("fontfamily"), wxEmptyString);
990 //if (!value.empty())
991 // fontFamily = wxAtoi(value);
993 value
= node
->GetPropVal(wxT("fontstyle"), wxEmptyString
);
996 fontStyle
= wxAtoi(value
);
997 fontFlags
|= wxTEXT_ATTR_FONT_ITALIC
;
1000 value
= node
->GetPropVal(wxT("fontsize"), wxEmptyString
);
1003 fontSize
= wxAtoi(value
);
1004 fontFlags
|= wxTEXT_ATTR_FONT_SIZE
;
1007 value
= node
->GetPropVal(wxT("fontweight"), wxEmptyString
);
1010 fontWeight
= wxAtoi(value
);
1011 fontFlags
|= wxTEXT_ATTR_FONT_WEIGHT
;
1014 value
= node
->GetPropVal(wxT("fontunderlined"), wxEmptyString
);
1017 fontUnderlined
= wxAtoi(value
) != 0;
1018 fontFlags
|= wxTEXT_ATTR_FONT_UNDERLINE
;
1021 attr
.SetFlags(fontFlags
);
1023 if (attr
.HasFlag(wxTEXT_ATTR_FONT
))
1024 attr
.SetFont(* wxTheFontList
->FindOrCreateFont(fontSize
, fontFamily
, fontStyle
, fontWeight
, fontUnderlined
, fontFacename
));
1026 // Restore correct font flags
1027 attr
.SetFlags(fontFlags
);
1029 value
= node
->GetPropVal(wxT("textcolor"), wxEmptyString
);
1032 if (value
[0] == wxT('#'))
1033 attr
.SetTextColour(HexStringToColour(value
.Mid(1)));
1035 attr
.SetTextColour(value
);
1038 value
= node
->GetPropVal(wxT("backgroundcolor"), wxEmptyString
);
1041 if (value
[0] == wxT('#'))
1042 attr
.SetBackgroundColour(HexStringToColour(value
.Mid(1)));
1044 attr
.SetBackgroundColour(value
);
1047 value
= node
->GetPropVal(wxT("characterstyle"), wxEmptyString
);
1049 attr
.SetCharacterStyleName(value
);
1051 // Set paragraph attributes
1054 value
= node
->GetPropVal(wxT("alignment"), wxEmptyString
);
1056 attr
.SetAlignment((wxTextAttrAlignment
) wxAtoi(value
));
1058 int leftSubIndent
= 0;
1060 bool hasLeftIndent
= false;
1062 value
= node
->GetPropVal(wxT("leftindent"), wxEmptyString
);
1065 leftIndent
= wxAtoi(value
);
1066 hasLeftIndent
= true;
1069 value
= node
->GetPropVal(wxT("leftsubindent"), wxEmptyString
);
1072 leftSubIndent
= wxAtoi(value
);
1073 hasLeftIndent
= true;
1077 attr
.SetLeftIndent(leftIndent
, leftSubIndent
);
1079 value
= node
->GetPropVal(wxT("rightindent"), wxEmptyString
);
1081 attr
.SetRightIndent(wxAtoi(value
));
1083 value
= node
->GetPropVal(wxT("parspacingbefore"), wxEmptyString
);
1085 attr
.SetParagraphSpacingBefore(wxAtoi(value
));
1087 value
= node
->GetPropVal(wxT("parspacingafter"), wxEmptyString
);
1089 attr
.SetParagraphSpacingAfter(wxAtoi(value
));
1091 value
= node
->GetPropVal(wxT("linespacing"), wxEmptyString
);
1093 attr
.SetLineSpacing(wxAtoi(value
));
1095 value
= node
->GetPropVal(wxT("bulletstyle"), wxEmptyString
);
1097 attr
.SetBulletStyle(wxAtoi(value
));
1099 value
= node
->GetPropVal(wxT("bulletnumber"), wxEmptyString
);
1101 attr
.SetBulletNumber(wxAtoi(value
));
1103 value
= node
->GetPropVal(wxT("bulletsymbol"), wxEmptyString
);
1106 wxChar ch
= wxAtoi(value
);
1109 attr
.SetBulletText(s
);
1112 value
= node
->GetPropVal(wxT("bullettext"), wxEmptyString
);
1114 attr
.SetBulletText(value
);
1116 value
= node
->GetPropVal(wxT("bulletfont"), wxEmptyString
);
1118 attr
.SetBulletFont(value
);
1120 value
= node
->GetPropVal(wxT("bulletname"), wxEmptyString
);
1122 attr
.SetBulletName(value
);
1124 value
= node
->GetPropVal(wxT("url"), wxEmptyString
);
1128 value
= node
->GetPropVal(wxT("parstyle"), wxEmptyString
);
1130 attr
.SetParagraphStyleName(value
);
1132 value
= node
->GetPropVal(wxT("liststyle"), wxEmptyString
);
1134 attr
.SetListStyleName(value
);
1136 value
= node
->GetPropVal(wxT("tabs"), wxEmptyString
);
1140 wxStringTokenizer
tkz(value
, wxT(","));
1141 while (tkz
.HasMoreTokens())
1143 wxString token
= tkz
.GetNextToken();
1144 tabs
.Add(wxAtoi(token
));
1157 // wxUSE_RICHTEXT && wxUSE_XML