]>
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
6 // Copyright: (c) 2011 Vadim Zeitlin <vadim@wxwidgets.org>
7 // Licence: wxWindows licence
8 ///////////////////////////////////////////////////////////////////////////////
10 // ============================================================================
12 // ============================================================================
14 // ----------------------------------------------------------------------------
16 // ----------------------------------------------------------------------------
18 // for compilers that support precompilation, includes "wx.h".
19 #include "wx/wxprec.h"
31 #include "wx/private/markupparser.h"
38 // ----------------------------------------------------------------------------
40 // ----------------------------------------------------------------------------
42 // Array containing the predefined XML 1.0 entities.
43 const struct XMLEntity
46 int len
; // == strlen(name)
57 // ----------------------------------------------------------------------------
59 // ----------------------------------------------------------------------------
62 ExtractUntil(char ch
, wxString::const_iterator
& it
, wxString::const_iterator end
)
65 for ( ; it
!= end
; ++it
)
73 // Return empty string to indicate that we didn't find ch at all.
77 } // anonymous namespace
79 // ============================================================================
80 // wxMarkupParser implementation
81 // ============================================================================
84 wxMarkupParser::ParseAttrs(wxString attrs
, TagAndAttrs
& tagAndAttrs
)
86 if ( tagAndAttrs
.name
.CmpNoCase("span") != 0 && !attrs
.empty() )
88 return wxString::Format("tag \"%s\" can't have attributes",
92 // TODO: Parse more attributes described at
93 // http://library.gnome.org/devel/pango/stable/PangoMarkupFormat.html
94 // and at least ignore them gracefully instead of giving errors (but
95 // quite a few of them could be supported as well, notable font_desc).
97 wxMarkupSpanAttributes
& spanAttrs
= tagAndAttrs
.attrs
;
99 while ( !attrs
.empty() )
102 const wxString attr
= attrs
.BeforeFirst(' ', &rest
);
105 // The "original" versions are used for error messages only.
107 const wxString nameOrig
= attr
.BeforeFirst('=', &valueOrig
);
109 const wxString name
= nameOrig
.Lower();
110 wxString value
= valueOrig
.Lower();
112 // All attributes values must be quoted.
113 if ( value
.length() < 2 ||
114 (value
[0] != value
.Last()) ||
115 (value
[0] != '"' && value
[0] != '\'') )
117 return wxString::Format("bad quoting for value of \"%s\"",
121 value
.assign(value
, 1, value
.length() - 2);
123 if ( name
== "foreground" || name
== "fgcolor" || name
== "color" )
125 spanAttrs
.m_fgCol
= value
;
127 else if ( name
== "background" || name
== "bgcolor" )
129 spanAttrs
.m_bgCol
= value
;
131 else if ( name
== "font_family" || name
== "face" )
133 spanAttrs
.m_fontFace
= value
;
135 else if ( name
== "font_weight" || name
== "weight" )
137 unsigned long weight
;
139 if ( value
== "ultralight" || value
== "light" || value
== "normal" )
140 spanAttrs
.m_isBold
= wxMarkupSpanAttributes::No
;
141 else if ( value
== "bold" || value
== "ultrabold" || value
== "heavy" )
142 spanAttrs
.m_isBold
= wxMarkupSpanAttributes::Yes
;
143 else if ( value
.ToULong(&weight
) )
144 spanAttrs
.m_isBold
= weight
>= 600 ? wxMarkupSpanAttributes::Yes
145 : wxMarkupSpanAttributes::No
;
147 return wxString::Format("invalid font weight \"%s\"", valueOrig
);
149 else if ( name
== "font_style" || name
== "style" )
151 if ( value
== "normal" )
152 spanAttrs
.m_isItalic
= wxMarkupSpanAttributes::No
;
153 else if ( value
== "oblique" || value
== "italic" )
154 spanAttrs
.m_isItalic
= wxMarkupSpanAttributes::Yes
;
156 return wxString::Format("invalid font style \"%s\"", valueOrig
);
158 else if ( name
== "size" )
161 if ( value
.ToULong(&size
) )
163 spanAttrs
.m_sizeKind
= wxMarkupSpanAttributes::Size_PointParts
;
164 spanAttrs
.m_fontSize
= size
;
166 else if ( value
== "smaller" || value
== "larger" )
168 spanAttrs
.m_sizeKind
= wxMarkupSpanAttributes::Size_Relative
;
169 spanAttrs
.m_fontSize
= value
== "smaller" ? -1 : +1;
171 else // Must be a CSS-like size specification
174 if ( value
.StartsWith("xx-", &rest
) )
176 else if ( value
.StartsWith("x-", &rest
) )
178 else if ( value
== "medium" )
185 if ( rest
== "small" )
187 else if ( rest
!= "large" )
188 return wxString::Format("invalid font size \"%s\"",
192 spanAttrs
.m_sizeKind
= wxMarkupSpanAttributes::Size_Symbolic
;
193 spanAttrs
.m_fontSize
= cssSize
;
201 bool wxMarkupParser::OutputTag(const TagAndAttrs
& tagAndAttrs
, bool start
)
203 if ( tagAndAttrs
.name
.CmpNoCase("span") == 0 )
206 m_output
.OnSpanStart(tagAndAttrs
.attrs
);
208 m_output
.OnSpanEnd(tagAndAttrs
.attrs
);
214 static const struct TagHandler
217 void (wxMarkupParserOutput::*startFunc
)();
218 void (wxMarkupParserOutput::*endFunc
)();
221 { "b", &wxMarkupParserOutput::OnBoldStart
,
222 &wxMarkupParserOutput::OnBoldEnd
},
223 { "i", &wxMarkupParserOutput::OnItalicStart
,
224 &wxMarkupParserOutput::OnItalicEnd
},
225 { "u", &wxMarkupParserOutput::OnUnderlinedStart
,
226 &wxMarkupParserOutput::OnUnderlinedEnd
},
227 { "s", &wxMarkupParserOutput::OnStrikethroughStart
,
228 &wxMarkupParserOutput::OnStrikethroughEnd
},
229 { "big", &wxMarkupParserOutput::OnBigStart
,
230 &wxMarkupParserOutput::OnBigEnd
},
231 { "small", &wxMarkupParserOutput::OnSmallStart
,
232 &wxMarkupParserOutput::OnSmallEnd
},
233 { "tt", &wxMarkupParserOutput::OnTeletypeStart
,
234 &wxMarkupParserOutput::OnTeletypeEnd
},
237 for ( unsigned n
= 0; n
< WXSIZEOF(tagHandlers
); n
++ )
239 const TagHandler
& h
= tagHandlers
[n
];
241 if ( tagAndAttrs
.name
.CmpNoCase(h
.name
) == 0 )
244 (m_output
.*(h
.startFunc
))();
246 (m_output
.*(h
.endFunc
))();
257 bool wxMarkupParser::Parse(const wxString
& text
)
259 // The stack containing the names and corresponding attributes (which are
260 // actually only used for <span> tags) of all of the currently opened tag
261 // or none if we're not inside any tag.
262 wxStack
<TagAndAttrs
> tags
;
264 // Current run of text.
267 const wxString::const_iterator end
= text
.end();
268 for ( wxString::const_iterator it
= text
.begin(); it
!= end
; ++it
)
270 switch ( (*it
).GetValue() )
274 // Flush the text preceding the tag, if any.
275 if ( !current
.empty() )
277 m_output
.OnText(current
);
281 // This variable is used only in the debugging messages
282 // and doesn't need to be defined if they're not compiled
283 // at all (it actually would result in unused variable
284 // messages in this case).
285 #if wxUSE_LOG_DEBUG || !defined(HAVE_VARIADIC_MACROS)
286 // Remember the tag starting position for the error
288 const size_t pos
= it
- text
.begin();
291 if ( ++it
!= end
&& *it
== '/' )
297 const wxString tag
= ExtractUntil('>', it
, end
);
300 wxLogDebug("%s at %lu.",
301 it
== end
? "Unclosed tag starting"
310 const wxString name
= tag
.BeforeFirst(' ', &attrs
);
312 TagAndAttrs
tagAndAttrs(name
);
313 const wxString err
= ParseAttrs(attrs
, tagAndAttrs
);
316 wxLogDebug("Bad attributes for \"%s\" "
322 tags
.push(tagAndAttrs
);
326 if ( tags
.empty() || tags
.top().name
!= tag
)
328 wxLogDebug("Unmatched closing tag \"%s\" at %lu.",
334 if ( !OutputTag(tags
.top(), start
) )
336 wxLogDebug("Unknown tag at %lu.", pos
);
346 wxLogDebug("'>' should be escaped as \">\"; at %lu.",
351 // Processing is somewhat complicated: we need to recognize at
352 // least the "<" entity to allow escaping left square
353 // brackets in the markup and, in fact, we recognize all of the
354 // standard XML entities for consistency with Pango markup
357 // However we also allow '&' to appear unescaped, i.e. directly
358 // and not as "&" when it is used to introduce the mnemonic
359 // for the label. In this case we simply leave it alone.
361 // Notice that this logic makes it impossible to have a label
362 // with "lt;" inside it and using "l" as mnemonic but hopefully
363 // this shouldn't be a problem in practice.
365 const size_t pos
= it
- text
.begin() + 1;
368 for ( n
= 0; n
< WXSIZEOF(xmlEntities
); n
++ )
370 const XMLEntity
& xmlEnt
= xmlEntities
[n
];
371 if ( text
.compare(pos
, xmlEnt
.len
, xmlEnt
.name
) == 0
372 && text
[pos
+ xmlEnt
.len
] == ';' )
374 // Escape the ampersands if needed to protect them
375 // from being interpreted as mnemonics indicators.
376 if ( xmlEnt
.value
== '&' )
379 current
+= xmlEnt
.value
;
381 it
+= xmlEnt
.len
+ 1; // +1 for '&' itself
387 if ( n
< WXSIZEOF(xmlEntities
) )
389 //else: fall through, '&' is not special
399 wxLogDebug("Missing closing tag for \"%s\"", tags
.top().name
);
403 if ( !current
.empty() )
404 m_output
.OnText(current
);
410 wxString
wxMarkupParser::Quote(const wxString
& text
)
413 quoted
.reserve(text
.length());
415 for ( wxString::const_iterator it
= text
.begin(); it
!= text
.end(); ++it
)
418 for ( n
= 0; n
< WXSIZEOF(xmlEntities
); n
++ )
420 const XMLEntity
& xmlEnt
= xmlEntities
[n
];
421 if ( *it
== xmlEnt
.value
)
423 quoted
<< '&' << xmlEnt
.name
<< ';';
428 if ( n
== WXSIZEOF(xmlEntities
) )
436 wxString
wxMarkupParser::Strip(const wxString
& text
)
438 class StripOutput
: public wxMarkupParserOutput
443 const wxString
& GetText() const { return m_text
; }
445 virtual void OnText(const wxString
& text
) { m_text
+= text
; }
447 virtual void OnBoldStart() { }
448 virtual void OnBoldEnd() { }
450 virtual void OnItalicStart() { }
451 virtual void OnItalicEnd() { }
453 virtual void OnUnderlinedStart() { }
454 virtual void OnUnderlinedEnd() { }
456 virtual void OnStrikethroughStart() { }
457 virtual void OnStrikethroughEnd() { }
459 virtual void OnBigStart() { }
460 virtual void OnBigEnd() { }
462 virtual void OnSmallStart() { }
463 virtual void OnSmallEnd() { }
465 virtual void OnTeletypeStart() { }
466 virtual void OnTeletypeEnd() { }
468 virtual void OnSpanStart(const wxMarkupSpanAttributes
& WXUNUSED(a
)) { }
469 virtual void OnSpanEnd(const wxMarkupSpanAttributes
& WXUNUSED(a
)) { }
476 wxMarkupParser
parser(output
);
477 if ( !parser
.Parse(text
) )
480 return output
.GetText();
483 #endif // wxUSE_MARKUP