]>
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
175 if ( value
.StartsWith("xx-", &rest
) )
177 else if ( value
.StartsWith("x-", &rest
) )
179 else if ( value
== "medium" )
186 if ( rest
== "small" )
188 else if ( rest
!= "large" )
189 return wxString::Format("invalid font size \"%s\"",
193 spanAttrs
.m_sizeKind
= wxMarkupSpanAttributes::Size_Symbolic
;
194 spanAttrs
.m_fontSize
= cssSize
;
202 bool wxMarkupParser::OutputTag(const TagAndAttrs
& tagAndAttrs
, bool start
)
204 if ( tagAndAttrs
.name
.CmpNoCase("span") == 0 )
207 m_output
.OnSpanStart(tagAndAttrs
.attrs
);
209 m_output
.OnSpanEnd(tagAndAttrs
.attrs
);
215 static const struct TagHandler
218 void (wxMarkupParserOutput::*startFunc
)();
219 void (wxMarkupParserOutput::*endFunc
)();
222 { "b", &wxMarkupParserOutput::OnBoldStart
,
223 &wxMarkupParserOutput::OnBoldEnd
},
224 { "i", &wxMarkupParserOutput::OnItalicStart
,
225 &wxMarkupParserOutput::OnItalicEnd
},
226 { "u", &wxMarkupParserOutput::OnUnderlinedStart
,
227 &wxMarkupParserOutput::OnUnderlinedEnd
},
228 { "s", &wxMarkupParserOutput::OnStrikethroughStart
,
229 &wxMarkupParserOutput::OnStrikethroughEnd
},
230 { "big", &wxMarkupParserOutput::OnBigStart
,
231 &wxMarkupParserOutput::OnBigEnd
},
232 { "small", &wxMarkupParserOutput::OnSmallStart
,
233 &wxMarkupParserOutput::OnSmallEnd
},
234 { "tt", &wxMarkupParserOutput::OnTeletypeStart
,
235 &wxMarkupParserOutput::OnTeletypeEnd
},
238 for ( unsigned n
= 0; n
< WXSIZEOF(tagHandlers
); n
++ )
240 const TagHandler
& h
= tagHandlers
[n
];
242 if ( tagAndAttrs
.name
.CmpNoCase(h
.name
) == 0 )
245 (m_output
.*(h
.startFunc
))();
247 (m_output
.*(h
.endFunc
))();
258 bool wxMarkupParser::Parse(const wxString
& text
)
260 // The stack containing the names and corresponding attributes (which are
261 // actually only used for <span> tags) of all of the currently opened tag
262 // or none if we're not inside any tag.
263 wxStack
<TagAndAttrs
> tags
;
265 // Current run of text.
268 const wxString::const_iterator end
= text
.end();
269 for ( wxString::const_iterator it
= text
.begin(); it
!= end
; ++it
)
271 switch ( (*it
).GetValue() )
275 // Flush the text preceding the tag, if any.
276 if ( !current
.empty() )
278 m_output
.OnText(current
);
282 // This variable is used only in the debugging messages
283 // and doesn't need to be defined if they're not compiled
284 // at all (it actually would result in unused variable
285 // messages in this case).
286 #if wxUSE_LOG_DEBUG || !defined(HAVE_VARIADIC_MACROS)
287 // Remember the tag starting position for the error
289 const size_t pos
= it
- text
.begin();
292 if ( ++it
!= end
&& *it
== '/' )
298 const wxString tag
= ExtractUntil('>', it
, end
);
301 wxLogDebug("%s at %lu.",
302 it
== end
? "Unclosed tag starting"
311 const wxString name
= tag
.BeforeFirst(' ', &attrs
);
313 TagAndAttrs
tagAndAttrs(name
);
314 const wxString err
= ParseAttrs(attrs
, tagAndAttrs
);
317 wxLogDebug("Bad attributes for \"%s\" "
323 tags
.push(tagAndAttrs
);
327 if ( tags
.empty() || tags
.top().name
!= tag
)
329 wxLogDebug("Unmatched closing tag \"%s\" at %lu.",
335 if ( !OutputTag(tags
.top(), start
) )
337 wxLogDebug("Unknown tag at %lu.", pos
);
347 wxLogDebug("'>' should be escaped as \">\"; at %lu.",
352 // Processing is somewhat complicated: we need to recognize at
353 // least the "<" entity to allow escaping left square
354 // brackets in the markup and, in fact, we recognize all of the
355 // standard XML entities for consistency with Pango markup
358 // However we also allow '&' to appear unescaped, i.e. directly
359 // and not as "&" when it is used to introduce the mnemonic
360 // for the label. In this case we simply leave it alone.
362 // Notice that this logic makes it impossible to have a label
363 // with "lt;" inside it and using "l" as mnemonic but hopefully
364 // this shouldn't be a problem in practice.
366 const size_t pos
= it
- text
.begin() + 1;
369 for ( n
= 0; n
< WXSIZEOF(xmlEntities
); n
++ )
371 const XMLEntity
& xmlEnt
= xmlEntities
[n
];
372 if ( text
.compare(pos
, xmlEnt
.len
, xmlEnt
.name
) == 0
373 && text
[pos
+ xmlEnt
.len
] == ';' )
375 // Escape the ampersands if needed to protect them
376 // from being interpreted as mnemonics indicators.
377 if ( xmlEnt
.value
== '&' )
380 current
+= xmlEnt
.value
;
382 it
+= xmlEnt
.len
+ 1; // +1 for '&' itself
388 if ( n
< WXSIZEOF(xmlEntities
) )
390 //else: fall through, '&' is not special
400 wxLogDebug("Missing closing tag for \"%s\"", tags
.top().name
);
404 if ( !current
.empty() )
405 m_output
.OnText(current
);
411 wxString
wxMarkupParser::Quote(const wxString
& text
)
414 quoted
.reserve(text
.length());
416 for ( wxString::const_iterator it
= text
.begin(); it
!= text
.end(); ++it
)
419 for ( n
= 0; n
< WXSIZEOF(xmlEntities
); n
++ )
421 const XMLEntity
& xmlEnt
= xmlEntities
[n
];
422 if ( *it
== xmlEnt
.value
)
424 quoted
<< '&' << xmlEnt
.name
<< ';';
429 if ( n
== WXSIZEOF(xmlEntities
) )
437 wxString
wxMarkupParser::Strip(const wxString
& text
)
439 class StripOutput
: public wxMarkupParserOutput
444 const wxString
& GetText() const { return m_text
; }
446 virtual void OnText(const wxString
& text
) { m_text
+= text
; }
448 virtual void OnBoldStart() { }
449 virtual void OnBoldEnd() { }
451 virtual void OnItalicStart() { }
452 virtual void OnItalicEnd() { }
454 virtual void OnUnderlinedStart() { }
455 virtual void OnUnderlinedEnd() { }
457 virtual void OnStrikethroughStart() { }
458 virtual void OnStrikethroughEnd() { }
460 virtual void OnBigStart() { }
461 virtual void OnBigEnd() { }
463 virtual void OnSmallStart() { }
464 virtual void OnSmallEnd() { }
466 virtual void OnTeletypeStart() { }
467 virtual void OnTeletypeEnd() { }
469 virtual void OnSpanStart(const wxMarkupSpanAttributes
& WXUNUSED(a
)) { }
470 virtual void OnSpanEnd(const wxMarkupSpanAttributes
& WXUNUSED(a
)) { }
477 wxMarkupParser
parser(output
);
478 if ( !parser
.Parse(text
) )
481 return output
.GetText();
484 #endif // wxUSE_MARKUP