]>
git.saurik.com Git - wxWidgets.git/blob - src/common/markupparser.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/markupparser.cpp
3 // Purpose: Implementation of wxMarkupParser.
4 // Author: Vadim Zeitlin
7 // Copyright: (c) 2011 Vadim Zeitlin <vadim@wxwidgets.org>
8 // Licence: wxWindows licence
9 ///////////////////////////////////////////////////////////////////////////////
11 // ============================================================================
13 // ============================================================================
15 // ----------------------------------------------------------------------------
17 // ----------------------------------------------------------------------------
19 // for compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
32 #include "wx/private/markupparser.h"
39 // ----------------------------------------------------------------------------
41 // ----------------------------------------------------------------------------
43 // Array containing the predefined XML 1.0 entities.
44 const struct XMLEntity
47 int len
; // == strlen(name)
58 // ----------------------------------------------------------------------------
60 // ----------------------------------------------------------------------------
63 ExtractUntil(char ch
, wxString::const_iterator
& it
, wxString::const_iterator end
)
66 for ( ; it
!= end
; ++it
)
74 // Return empty string to indicate that we didn't find ch at all.
78 } // anonymous namespace
80 // ============================================================================
81 // wxMarkupParser implementation
82 // ============================================================================
85 wxMarkupParser::ParseAttrs(wxString attrs
, TagAndAttrs
& tagAndAttrs
)
87 if ( tagAndAttrs
.name
.CmpNoCase("span") != 0 && !attrs
.empty() )
89 return wxString::Format("tag \"%s\" can't have attributes",
93 // TODO: Parse more attributes described at
94 // http://library.gnome.org/devel/pango/stable/PangoMarkupFormat.html
95 // and at least ignore them gracefully instead of giving errors (but
96 // quite a few of them could be supported as well, notable font_desc).
98 wxMarkupSpanAttributes
& spanAttrs
= tagAndAttrs
.attrs
;
100 while ( !attrs
.empty() )
103 const wxString attr
= attrs
.BeforeFirst(' ', &rest
);
106 // The "original" versions are used for error messages only.
108 const wxString nameOrig
= attr
.BeforeFirst('=', &valueOrig
);
110 const wxString name
= nameOrig
.Lower();
111 wxString value
= valueOrig
.Lower();
113 // All attributes values must be quoted.
114 if ( value
.length() < 2 ||
115 (value
[0] != value
.Last()) ||
116 (value
[0] != '"' && value
[0] != '\'') )
118 return wxString::Format("bad quoting for value of \"%s\"",
122 value
.assign(value
, 1, value
.length() - 2);
124 if ( name
== "foreground" || name
== "fgcolor" || name
== "color" )
126 spanAttrs
.m_fgCol
= value
;
128 else if ( name
== "background" || name
== "bgcolor" )
130 spanAttrs
.m_bgCol
= value
;
132 else if ( name
== "font_family" || name
== "face" )
134 spanAttrs
.m_fontFace
= value
;
136 else if ( name
== "font_weight" || name
== "weight" )
138 unsigned long weight
;
140 if ( value
== "ultralight" || value
== "light" || value
== "normal" )
141 spanAttrs
.m_isBold
= wxMarkupSpanAttributes::No
;
142 else if ( value
== "bold" || value
== "ultrabold" || value
== "heavy" )
143 spanAttrs
.m_isBold
= wxMarkupSpanAttributes::Yes
;
144 else if ( value
.ToULong(&weight
) )
145 spanAttrs
.m_isBold
= weight
>= 600 ? wxMarkupSpanAttributes::Yes
146 : wxMarkupSpanAttributes::No
;
148 return wxString::Format("invalid font weight \"%s\"", valueOrig
);
150 else if ( name
== "font_style" || name
== "style" )
152 if ( value
== "normal" )
153 spanAttrs
.m_isItalic
= wxMarkupSpanAttributes::No
;
154 else if ( value
== "oblique" || value
== "italic" )
155 spanAttrs
.m_isItalic
= wxMarkupSpanAttributes::Yes
;
157 return wxString::Format("invalid font style \"%s\"", valueOrig
);
159 else if ( name
== "size" )
162 if ( value
.ToULong(&size
) )
164 spanAttrs
.m_sizeKind
= wxMarkupSpanAttributes::Size_PointParts
;
165 spanAttrs
.m_fontSize
= size
;
167 else if ( value
== "smaller" || value
== "larger" )
169 spanAttrs
.m_sizeKind
= wxMarkupSpanAttributes::Size_Relative
;
170 spanAttrs
.m_fontSize
= value
== "smaller" ? -1 : +1;
172 else // Must be a CSS-like size specification
176 if ( value
.StartsWith("xx-", &rest
) )
178 else if ( value
.StartsWith("x-", &rest
) )
180 else if ( value
== "medium" )
187 if ( rest
== "small" )
189 else if ( rest
!= "large" )
190 return wxString::Format("invalid font size \"%s\"",
194 spanAttrs
.m_sizeKind
= wxMarkupSpanAttributes::Size_Symbolic
;
195 spanAttrs
.m_fontSize
= cssSize
;
203 bool wxMarkupParser::OutputTag(const TagAndAttrs
& tagAndAttrs
, bool start
)
205 if ( tagAndAttrs
.name
.CmpNoCase("span") == 0 )
208 m_output
.OnSpanStart(tagAndAttrs
.attrs
);
210 m_output
.OnSpanEnd(tagAndAttrs
.attrs
);
216 static const struct TagHandler
219 void (wxMarkupParserOutput::*startFunc
)();
220 void (wxMarkupParserOutput::*endFunc
)();
223 { "b", &wxMarkupParserOutput::OnBoldStart
,
224 &wxMarkupParserOutput::OnBoldEnd
},
225 { "i", &wxMarkupParserOutput::OnItalicStart
,
226 &wxMarkupParserOutput::OnItalicEnd
},
227 { "u", &wxMarkupParserOutput::OnUnderlinedStart
,
228 &wxMarkupParserOutput::OnUnderlinedEnd
},
229 { "s", &wxMarkupParserOutput::OnStrikethroughStart
,
230 &wxMarkupParserOutput::OnStrikethroughEnd
},
231 { "big", &wxMarkupParserOutput::OnBigStart
,
232 &wxMarkupParserOutput::OnBigEnd
},
233 { "small", &wxMarkupParserOutput::OnSmallStart
,
234 &wxMarkupParserOutput::OnSmallEnd
},
235 { "tt", &wxMarkupParserOutput::OnTeletypeStart
,
236 &wxMarkupParserOutput::OnTeletypeEnd
},
239 for ( unsigned n
= 0; n
< WXSIZEOF(tagHandlers
); n
++ )
241 const TagHandler
& h
= tagHandlers
[n
];
243 if ( tagAndAttrs
.name
.CmpNoCase(h
.name
) == 0 )
246 (m_output
.*(h
.startFunc
))();
248 (m_output
.*(h
.endFunc
))();
259 bool wxMarkupParser::Parse(const wxString
& text
)
261 // The stack containing the names and corresponding attributes (which are
262 // actually only used for <span> tags) of all of the currently opened tag
263 // or none if we're not inside any tag.
264 wxStack
<TagAndAttrs
> tags
;
266 // Current run of text.
269 const wxString::const_iterator end
= text
.end();
270 for ( wxString::const_iterator it
= text
.begin(); it
!= end
; ++it
)
272 switch ( (*it
).GetValue() )
276 // Flush the text preceding the tag, if any.
277 if ( !current
.empty() )
279 m_output
.OnText(current
);
283 // This variable is used only in the debugging messages
284 // and doesn't need to be defined if they're not compiled
285 // at all (it actually would result in unused variable
286 // messages in this case).
287 #if wxUSE_LOG_DEBUG || !defined(HAVE_VARIADIC_MACROS)
288 // Remember the tag starting position for the error
290 const size_t pos
= it
- text
.begin();
293 if ( ++it
!= end
&& *it
== '/' )
299 const wxString tag
= ExtractUntil('>', it
, end
);
302 wxLogDebug("%s at %lu.",
303 it
== end
? "Unclosed tag starting"
312 const wxString name
= tag
.BeforeFirst(' ', &attrs
);
314 TagAndAttrs
tagAndAttrs(name
);
315 const wxString err
= ParseAttrs(attrs
, tagAndAttrs
);
318 wxLogDebug("Bad attributes for \"%s\" "
324 tags
.push(tagAndAttrs
);
328 if ( tags
.empty() || tags
.top().name
!= tag
)
330 wxLogDebug("Unmatched closing tag \"%s\" at %lu.",
336 if ( !OutputTag(tags
.top(), start
) )
338 wxLogDebug("Unknown tag at %lu.", pos
);
348 wxLogDebug("'>' should be escaped as \">\"; at %lu.",
353 // Processing is somewhat complicated: we need to recognize at
354 // least the "<" entity to allow escaping left square
355 // brackets in the markup and, in fact, we recognize all of the
356 // standard XML entities for consistency with Pango markup
359 // However we also allow '&' to appear unescaped, i.e. directly
360 // and not as "&" when it is used to introduce the mnemonic
361 // for the label. In this case we simply leave it alone.
363 // Notice that this logic makes it impossible to have a label
364 // with "lt;" inside it and using "l" as mnemonic but hopefully
365 // this shouldn't be a problem in practice.
367 const size_t pos
= it
- text
.begin() + 1;
370 for ( n
= 0; n
< WXSIZEOF(xmlEntities
); n
++ )
372 const XMLEntity
& xmlEnt
= xmlEntities
[n
];
373 if ( text
.compare(pos
, xmlEnt
.len
, xmlEnt
.name
) == 0
374 && text
[pos
+ xmlEnt
.len
] == ';' )
376 // Escape the ampersands if needed to protect them
377 // from being interpreted as mnemonics indicators.
378 if ( xmlEnt
.value
== '&' )
381 current
+= xmlEnt
.value
;
383 it
+= xmlEnt
.len
+ 1; // +1 for '&' itself
389 if ( n
< WXSIZEOF(xmlEntities
) )
391 //else: fall through, '&' is not special
401 wxLogDebug("Missing closing tag for \"%s\"", tags
.top().name
);
405 if ( !current
.empty() )
406 m_output
.OnText(current
);
412 wxString
wxMarkupParser::Quote(const wxString
& text
)
415 quoted
.reserve(text
.length());
417 for ( wxString::const_iterator it
= text
.begin(); it
!= text
.end(); ++it
)
420 for ( n
= 0; n
< WXSIZEOF(xmlEntities
); n
++ )
422 const XMLEntity
& xmlEnt
= xmlEntities
[n
];
423 if ( *it
== xmlEnt
.value
)
425 quoted
<< '&' << xmlEnt
.name
<< ';';
430 if ( n
== WXSIZEOF(xmlEntities
) )
438 wxString
wxMarkupParser::Strip(const wxString
& text
)
440 class StripOutput
: public wxMarkupParserOutput
445 const wxString
& GetText() const { return m_text
; }
447 virtual void OnText(const wxString
& text
) { m_text
+= text
; }
449 virtual void OnBoldStart() { }
450 virtual void OnBoldEnd() { }
452 virtual void OnItalicStart() { }
453 virtual void OnItalicEnd() { }
455 virtual void OnUnderlinedStart() { }
456 virtual void OnUnderlinedEnd() { }
458 virtual void OnStrikethroughStart() { }
459 virtual void OnStrikethroughEnd() { }
461 virtual void OnBigStart() { }
462 virtual void OnBigEnd() { }
464 virtual void OnSmallStart() { }
465 virtual void OnSmallEnd() { }
467 virtual void OnTeletypeStart() { }
468 virtual void OnTeletypeEnd() { }
470 virtual void OnSpanStart(const wxMarkupSpanAttributes
& WXUNUSED(a
)) { }
471 virtual void OnSpanEnd(const wxMarkupSpanAttributes
& WXUNUSED(a
)) { }
478 wxMarkupParser
parser(output
);
479 if ( !parser
.Parse(text
) )
482 return output
.GetText();
485 #endif // wxUSE_MARKUP