Line data Source code
1 : // Snap Websites Servers -- handle calls to QXmlQuery
2 : // Copyright (c) 2014-2019 Made to Order Software Corp. All Rights Reserved
3 : //
4 : // This program is free software; you can redistribute it and/or modify
5 : // it under the terms of the GNU General Public License as published by
6 : // the Free Software Foundation; either version 2 of the License, or
7 : // (at your option) any later version.
8 : //
9 : // This program is distributed in the hope that it will be useful,
10 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : // GNU General Public License for more details.
13 : //
14 : // You should have received a copy of the GNU General Public License
15 : // along with this program; if not, write to the Free Software
16 : // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 :
18 :
19 : // self
20 : //
21 : #include "snapwebsites/xslt.h"
22 :
23 :
24 : // snapwebsites lib
25 : //
26 : #include "snapwebsites/snapwebsites.h"
27 : #include "snapwebsites/log.h"
28 : #include "snapwebsites/qdomreceiver.h"
29 : #include "snapwebsites/qhtmlserializer.h"
30 : #include "snapwebsites/qxmlmessagehandler.h"
31 :
32 :
33 : // Qt lib
34 : //
35 : #include <QXmlQuery>
36 :
37 :
38 : // last include
39 : //
40 : #include <snapdev/poison.h>
41 :
42 :
43 :
44 :
45 : namespace snap
46 : {
47 :
48 :
49 : /** \brief Save the XSLT parser.
50 : *
51 : * This function receives a copy of the XSLT parser in the form of a string.
52 : * It gets saved in the class and reused by the evaluation functions
53 : * until changed.
54 : *
55 : * \param[in] xsl The XSLT parser document.
56 : */
57 0 : void xslt::set_xsl(QString const & xsl)
58 : {
59 0 : f_xsl = xsl;
60 0 : }
61 :
62 :
63 : /** \brief Save the XSLT parser.
64 : *
65 : * This function receives a copy of the XSLT parser in the form of a string.
66 : * It gets saved in the class and reused by the evaluation functions
67 : * until changed.
68 : *
69 : * \note
70 : * At this the XML document is immediately transformed to a string. At
71 : * some point we may be able to use the XML document as such with
72 : * QXmlQuery...
73 : *
74 : * \param[in] xsl The XSLT parser document.
75 : */
76 0 : void xslt::set_xsl(QDomDocument const & xsl)
77 : {
78 0 : f_xsl = xsl.toString(-1);
79 0 : }
80 :
81 :
82 : /** \brief Set the XSLT parser from the content of a file.
83 : *
84 : * When you have a filename you may bypass loading the file yourselves
85 : * and call this function.
86 : *
87 : * The loading is done using the load_file() signal.
88 : *
89 : * \warning
90 : * This function assumes that you have a valid running server with
91 : * all the plugins loaded (i.e. running in snap_child or snap_backend
92 : * or any plugin.)
93 : *
94 : * \exception xslt_initialization_error
95 : * This exception is raised if the named file cannot be loaded since
96 : * that means we will not be able to transform the input.
97 : *
98 : * \param[in] filename The name of the file to load.
99 : */
100 0 : void xslt::set_xsl_from_file(QString const & filename)
101 : {
102 : // make sure we have access to a valid server
103 0 : server::pointer_t server(server::instance());
104 0 : if(!server)
105 : {
106 0 : throw snap_logic_exception("server pointer is nullptr");
107 : }
108 :
109 : // whether he file was found
110 0 : bool found(false);
111 :
112 : // setup the file
113 0 : snap::snap_child::post_file_t file;
114 0 : file.set_filename(filename);
115 :
116 : // try to load the data
117 0 : server->load_file(file, found);
118 :
119 : // if not found, we have a problem
120 0 : if(!found)
121 : {
122 0 : throw xslt_initialization_error(QString("xslt::set_xsl_from_file() could not load file \"%1\".").arg(filename));
123 : }
124 :
125 : // okay, it got loaded, save the resulting file in here
126 0 : f_xsl = QString::fromUtf8(file.get_data(), file.get_size());
127 0 : }
128 :
129 :
130 : /** \brief Set the document to be transformed.
131 : *
132 : * This function saves a copy of the specified input document in
133 : * this object. This is the document that is going to be transformed
134 : * using the XSLT script.
135 : *
136 : * In most cases this document is expected to be HTML or XHTML.
137 : *
138 : * \note
139 : * If you have a DOM document, you may call the other
140 : * set_document(QDomDocument &) to get the same effect.
141 : *
142 : * \param[in] input The document to be parsed.
143 : *
144 : * \sa set_document(QDomDocument & doc);
145 : */
146 0 : void xslt::set_document(QString const & input)
147 : {
148 0 : f_doc.clear();
149 0 : f_input = input;
150 0 : }
151 :
152 :
153 : /** \brief Set the document to be transformed.
154 : *
155 : * This function saves a copy of the specified input document in
156 : * this object. This is the document that is going to be transformed
157 : * using the XSLT script.
158 : *
159 : * \note
160 : * If you have a string, you may call the other
161 : * set_document(QString &) function which is going
162 : * to be must faster than generating a document
163 : * and saving it back to a string.
164 : *
165 : * \param[in] doc The document to be parsed.
166 : *
167 : * \sa set_document(QDomDocument & doc);
168 : */
169 0 : void xslt::set_document(QDomDocument & doc)
170 : {
171 0 : f_input.clear();
172 0 : f_doc = doc;
173 0 : }
174 :
175 :
176 : /** \brief Add a variable which will be used with the parser.
177 : *
178 : * This function can be used to add variables to the parser.
179 : * These are added in the evaluation function just before
180 : * the evaluation takes place.
181 : *
182 : * In most cases, though, you add your data to the input
183 : * XML document and not here as variables.
184 : *
185 : * \param[in] name The name of the variable.
186 : * \param[in] value The value of the variable.
187 : *
188 : * \sa clear_variables()
189 : */
190 0 : void xslt::add_variable(QString const & name, QVariant const & value)
191 : {
192 0 : f_variables[name] = value;
193 0 : }
194 :
195 :
196 : /** \brief Clear all the variables.
197 : *
198 : * This function can be used to clear all the variables of an XSLT
199 : * object. Any variable that were added with the add_variable()
200 : * function are removed.
201 : *
202 : * \sa add_variable()
203 : */
204 0 : void xslt::clear_variables()
205 : {
206 0 : f_variables.clear();
207 0 : }
208 :
209 :
210 : /** \brief Run parser and return the result as a string.
211 : *
212 : * This function runs the parser to apply the XSLT 2.0 transformations
213 : * against the input document and returns the result as a string.
214 : *
215 : * \return The transformation result as a string.
216 : */
217 0 : QString xslt::evaluate_to_string()
218 : {
219 0 : QString result;
220 0 : evaluate(&result, nullptr);
221 0 : return result;
222 : }
223 :
224 :
225 : /** \brief Run parser and return the result in \p output.
226 : *
227 : * This function runs the parser to apply the XSLT 2.0 transformations
228 : * against the input document and returns the result as an XML document
229 : * in the specified output object. Whether the output DOM is currently
230 : * empty has no bearing to the process although things may not work
231 : * as expected if the output is not just one document root element,
232 : * though.
233 : *
234 : * \param[in,out] output The output QDomDocument where the parsing gets saved.
235 : *
236 : * \return The transformation result as a string.
237 : */
238 0 : void xslt::evaluate_to_document(QDomDocument & output)
239 : {
240 0 : evaluate(nullptr, &output);
241 0 : }
242 :
243 :
244 : /** \brief Evaluate the transformation.
245 : *
246 : * This function is the internal function that is used to evaluate the
247 : * output. The functions calling parameters is "complicated" and hence
248 : * the function was made internal and two public functions created
249 : * so the outside world can make easy calls instead.
250 : *
251 : * \todo
252 : * Look into getting the XML sitemap version to here? Actually, the
253 : * best would be to look into making use of XML data as input since
254 : * that would allow us to avoid some (many?) conversions to and from
255 : * string.
256 : *
257 : * \param[out] output_string The output string if evaluate_to_string()
258 : * was called.
259 : * \param[in,out] output_document The output document if
260 : * evaluate_to_document() was called.
261 : */
262 0 : void xslt::evaluate(QString * output_string, QDomDocument * output_document)
263 : {
264 0 : bool first_attempt(true);
265 :
266 : for(;;)
267 : {
268 0 : QXmlQuery q(QXmlQuery::XSLT20);
269 :
270 0 : QString doc_str(f_input);
271 0 : if(doc_str.isEmpty())
272 : {
273 0 : if(f_doc.isNull())
274 : {
275 0 : throw snap_logic_exception("xslt::evaluate_to_string(): f_input and f_doc are both empty.");
276 : }
277 :
278 0 : doc_str = f_doc.toString(-1);
279 : }
280 :
281 : // if it is our second attempt, then we need to transform the
282 : // entities to Unicode characters
283 0 : if(!first_attempt)
284 : {
285 : // this function is rather slow so we try to not run it
286 : // here since we are likely live when running this...
287 : // a filter will be created to run this function on
288 : // all fields in the database that may include "incorrect"
289 : // (as per QXmlQuery) HTML code.
290 : //
291 0 : doc_str = filter_entities_out(doc_str);
292 : }
293 :
294 : // setup our message handler
295 0 : QMessageHandler msg;
296 0 : msg.set_xsl(f_xsl);
297 0 : msg.set_doc(doc_str);
298 :
299 : #ifdef _DEBUG
300 : //SNAP_LOG_DEBUG("!!!! f_xsl=" )(f_xsl);
301 : //SNAP_LOG_DEBUG("!!!! doc_str=")(doc_str);
302 : #endif
303 :
304 : // setup the XML query object
305 0 : q.setMessageHandler(&msg);
306 0 : q.setFocus(doc_str);
307 :
308 : // set variables
309 : // WARNING: variables MUST be set before you call the setQuery()
310 : // function since it may start making use of them
311 : //
312 : // TBD: I think variables can cause problems, although it is used by
313 : // the form and editor implementations...
314 : //
315 0 : for(auto p(f_variables.begin());
316 0 : p != f_variables.end();
317 : ++p)
318 : {
319 0 : q.bindVariable(p.key(), p.value());
320 : }
321 :
322 : // setup the transformation data
323 0 : q.setQuery(f_xsl);
324 0 : if(!q.isValid())
325 : {
326 0 : if(first_attempt)
327 : {
328 0 : goto try_again;
329 : }
330 0 : throw xslt_evaluation_error("Invalid XSLT query detected by Qt.");
331 : }
332 :
333 0 : if(output_document != nullptr)
334 : {
335 : // this should be faster since we keep the data in a DOM
336 0 : QDomReceiver receiver(q.namePool(), *output_document);
337 0 : q.evaluateTo(&receiver);
338 :
339 0 : if(!msg.has_entities())
340 : {
341 0 : if(msg.had_msg())
342 : {
343 : // do something? but what then?
344 : }
345 0 : return;
346 : }
347 : }
348 0 : else if(output_string != nullptr)
349 : {
350 : // request the evalutation in a QBuffer
351 0 : QBuffer output;
352 0 : output.open(QBuffer::ReadWrite);
353 0 : QHtmlSerializer html(q.namePool(), &output);
354 0 : q.evaluateTo(&html);
355 :
356 0 : if(!msg.has_entities())
357 : {
358 0 : if(!msg.had_msg())
359 : {
360 0 : *output_string = QString::fromUtf8(output.data());
361 : }
362 : else
363 : {
364 : // return a default so we know something went wrong
365 0 : *output_string = "[QXmlParser failed to transform your data]";
366 : }
367 0 : return;
368 : }
369 : }
370 : else
371 : {
372 0 : throw xslt_evaluation_error("incorrect use of evaluate() with both output_document and output_string");
373 : }
374 :
375 : // if we reach here then the input data includes entities
376 0 : if(!first_attempt)
377 : {
378 0 : throw xslt_evaluation_error("your input document could not be converted by QXmlQuery.");
379 : }
380 0 : try_again:
381 0 : first_attempt = false;
382 0 : }
383 :
384 : // The following is for history record.
385 : // I first tried to implement the transformation from XML to XML so avoid
386 : // wasting time converting to a string first and then back to XML
387 : //
388 : // QString const doc_str(doc.toString(-1));
389 : // if(doc_str.isEmpty())
390 : // {
391 : // throw snap_logic_exception("somehow the memory XML document for the body XSLT is empty");
392 : // }
393 : // QXmlQuery q(QXmlQuery::XSLT20);
394 : // QMessageHandler msg;
395 : // msg.set_xsl(xsl);
396 : // msg.set_doc(doc_str);
397 : // q.setMessageHandler(&msg);
398 : // #if 0
399 : // QDomNodeModel m(q.namePool(), doc);
400 : // QXmlNodeModelIndex x(m.fromDomNode(doc.documentElement()));
401 : // QXmlItem i(x);
402 : // q.setFocus(i);
403 : // #else
404 : // q.setFocus(doc_str);
405 : // #endif
406 : // q.setQuery(xsl);
407 : // if(!q.isValid())
408 : // {
409 : // throw layout_exception_invalid_xslt_data(QString("invalid XSLT query for BODY \"%1\" detected by Qt").arg(ipath.get_key()));
410 : // }
411 : // #if 0
412 : // QXmlResultItems results;
413 : // q.evaluateTo(&results);
414 : //
415 : // QXmlItem item(results.next());
416 : // while(!item.isNull())
417 : // {
418 : // if(item.isNode())
419 : // {
420 : // //printf("Got a node!\n");
421 : // QXmlNodeModelIndex node_index(item.toNodeModelIndex());
422 : // QDomNode node(m.toDomNode(node_index));
423 : // printf("Got a node! [%s]\n", node.localName()/*ownerDocument().toString(-1)*/.toUtf8().data());
424 : // }
425 : // item = results.next();
426 : // }
427 : // #elif 1
428 : // // this should be faster since we keep the data in a DOM
429 : // QDomDocument doc_output("body");
430 : // QDomReceiver receiver(q.namePool(), doc_output);
431 : // q.evaluateTo(&receiver);
432 : //
433 : // extract_js_and_css(doc, doc_output);
434 : // body.appendChild(doc.importNode(doc_output.documentElement(), true));
435 : // //std::cout << "Body HTML is [" << doc_output.toString(-1) << "]\n";
436 : // #else
437 : // //QDomDocument doc_body("body");
438 : // //doc_body.setContent(get_content_parameter(path, get_name(name_t::SNAP_NAME_CONTENT_BODY) <<-- that would be wrong now).stringValue(), true, nullptr, nullptr, nullptr);
439 : // //QDomElement content_tag(doc.createElement("content"));
440 : // //body.appendChild(content_tag);
441 : // //content_tag.appendChild(doc.importNode(doc_body.documentElement(), true));
442 : //
443 : // // TODO: look into getting XML as output
444 : // QString out;
445 : // q.evaluateTo(&out);
446 : // //QDomElement output(doc.createElement("output"));
447 : // //body.appendChild(output);
448 : // //QDomText text(doc.createTextNode(out));
449 : // //output.appendChild(text);
450 : // QDomDocument doc_output("body");
451 : // doc_output.setContent(out, true, nullptr, nullptr, nullptr);
452 : // body.appendChild(doc.importNode(doc_output.documentElement(), true));
453 : // #endif
454 : }
455 :
456 :
457 : /** \brief Filter an HTML document and replace entities by their characters.
458 : *
459 : * This function goes through the entire HTML document and makes sure that
460 : * all entities get transformed from the HTML syntax (\&name;)
461 : * to the corresponding Unicode character.
462 : *
463 : * This is important because Browsers may send us HTML with entities and
464 : * unfortunately the QXmlQuery system does not support them. (Actually
465 : * the Qt XML loaders are expected to transform entities to Unicode
466 : * characters on the fly, but in our case only the <, >, and &
467 : * are recognized.)
468 : *
469 : * \param[in] html The HTML to be transformed.
470 : *
471 : * \return The transformed HTML.
472 : */
473 0 : QString xslt::filter_entities_out(QString const & html)
474 : {
475 0 : QString result;
476 :
477 0 : int pos(0);
478 : for(;;)
479 : {
480 0 : int amp(html.indexOf('&', pos));
481 0 : if(amp == -1)
482 : {
483 : // no more '&', return immediately
484 0 : result += html.mid(pos);
485 0 : return result;
486 : }
487 0 : ++amp;
488 :
489 : // TODO: check whether we find a '<', '>', or '&' before the ';'
490 0 : int const semi_colon(html.indexOf(';', amp));
491 0 : if(semi_colon == -1)
492 : {
493 : // found '&' without ';', return it as is
494 0 : result += html.mid(pos);
495 0 : return result;
496 : }
497 :
498 0 : if(amp >= html.length())
499 : {
500 : // this is not possible since we found a ';' after the '&'
501 0 : throw snap_logic_exception("xslt::filter_entities_out(): amp >= html.length() is just not possible here.");
502 : }
503 :
504 : // make sure that the first character represents a possible
505 : // entity otherwise we ignore it altogether
506 0 : QChar const c(html[amp]);
507 0 : if(c == '#')
508 : {
509 : // no need to convert numeric entities
510 0 : result += html.mid(pos, semi_colon + 1 - pos);
511 : }
512 0 : else if(semi_colon - amp < 100 // if the name is more than 100 characters, we probably got a problem...
513 0 : || (c >= 'a' && c <= 'z')
514 0 : || (c >= 'A' && c <= 'Z'))
515 : {
516 : // keep whatever happened before the ampersand
517 0 : result += html.mid(pos, amp - 1 - pos);
518 :
519 : // retrieve the entity reference name
520 0 : QString const entity(html.mid(amp, semi_colon - amp));
521 0 : result += convert_entity(entity);
522 : }
523 : else
524 : {
525 : // in this case we replace the entity with a starting &
526 0 : result += "&";
527 0 : result += html.mid(pos, semi_colon - pos);
528 : }
529 :
530 0 : pos = semi_colon + 1;
531 0 : }
532 : }
533 :
534 :
535 : /** \brief Convert the named entity to a character or even a few characters.
536 : *
537 : * This function "manually" (so very quickly) converts the named entity
538 : * to a name.
539 : *
540 : * If the entity is not known, then we return "&<name>;" so that way
541 : * we can continue to parse the data and make the problematic character
542 : * known.
543 : *
544 : * \note
545 : * The total number of entities is enormous and found on this page:
546 : * http://www.w3.org/TR/xml-entity-names/
547 : *
548 : * \todo
549 : * The idea, at some point, will be to write a parser of source entity
550 : * files and come up with a function that checks characters one by one
551 : * very quickly (similar to having a switch at all levels.)
552 : *
553 : * \param[in] entity_name The name of the entity to be converted.
554 : *
555 : * \return The corresponding Unicode character.
556 : */
557 0 : QString xslt::convert_entity(QString const & entity_name)
558 : {
559 0 : if(entity_name.isEmpty())
560 : {
561 0 : return "";
562 : }
563 :
564 0 : switch(entity_name[0].unicode())
565 : {
566 0 : case 'A':
567 0 : if(entity_name == "Aacute")
568 : {
569 0 : return QString("%1").arg(QChar(193));
570 : }
571 0 : if(entity_name == "Acirc")
572 : {
573 0 : return QString("%1").arg(QChar(194));
574 : }
575 0 : if(entity_name == "AElig")
576 : {
577 0 : return QString("%1").arg(QChar(198));
578 : }
579 0 : if(entity_name == "Agrave")
580 : {
581 0 : return QString("%1").arg(QChar(192));
582 : }
583 0 : if(entity_name == "Aring")
584 : {
585 0 : return QString("%1").arg(QChar(197));
586 : }
587 0 : if(entity_name == "Atilde")
588 : {
589 0 : return QString("%1").arg(QChar(195));
590 : }
591 0 : if(entity_name == "Auml")
592 : {
593 0 : return QString("%1").arg(QChar(196));
594 : }
595 0 : break;
596 :
597 0 : case 'a':
598 0 : if(entity_name == "aacute")
599 : {
600 0 : return QString("%1").arg(QChar(225));
601 : }
602 0 : if(entity_name == "acute")
603 : {
604 0 : return QString("%1").arg(QChar(180));
605 : }
606 0 : if(entity_name == "acirc")
607 : {
608 0 : return QString("%1").arg(QChar(226));
609 : }
610 0 : if(entity_name == "aelig")
611 : {
612 0 : return QString("%1").arg(QChar(230));
613 : }
614 0 : if(entity_name == "agrave")
615 : {
616 0 : return QString("%1").arg(QChar(224));
617 : }
618 0 : if(entity_name == "amp")
619 : {
620 0 : return "&";
621 : }
622 0 : if(entity_name == "aring")
623 : {
624 0 : return QString("%1").arg(QChar(229));
625 : }
626 0 : if(entity_name == "atilde")
627 : {
628 0 : return QString("%1").arg(QChar(227));
629 : }
630 0 : if(entity_name == "auml")
631 : {
632 0 : return QString("%1").arg(QChar(228));
633 : }
634 0 : break;
635 :
636 0 : case 'b':
637 0 : if(entity_name == "brvbar")
638 : {
639 0 : return QString("%1").arg(QChar(166));
640 : }
641 0 : break;
642 :
643 0 : case 'C':
644 0 : if(entity_name == "Ccedil")
645 : {
646 0 : return QString("%1").arg(QChar(199));
647 : }
648 0 : break;
649 :
650 0 : case 'c':
651 0 : if(entity_name == "ccedil")
652 : {
653 0 : return QString("%1").arg(QChar(231));
654 : }
655 0 : if(entity_name == "cedil")
656 : {
657 0 : return QString("%1").arg(QChar(184));
658 : }
659 0 : if(entity_name == "cent")
660 : {
661 0 : return QString("%1").arg(QChar(162));
662 : }
663 0 : if(entity_name == "copy")
664 : {
665 0 : return QString("%1").arg(QChar(169));
666 : }
667 0 : if(entity_name == "curren")
668 : {
669 0 : return QString("%1").arg(QChar(164));
670 : }
671 0 : break;
672 :
673 0 : case 'd':
674 0 : if(entity_name == "deg")
675 : {
676 0 : return QString("%1").arg(QChar(176));
677 : }
678 0 : if(entity_name == "divide")
679 : {
680 0 : return QString("%1").arg(QChar(247));
681 : }
682 0 : break;
683 :
684 0 : case 'E':
685 0 : if(entity_name == "Eacute")
686 : {
687 0 : return QString("%1").arg(QChar(201));
688 : }
689 0 : if(entity_name == "Ecirc")
690 : {
691 0 : return QString("%1").arg(QChar(202));
692 : }
693 0 : if(entity_name == "Egrave")
694 : {
695 0 : return QString("%1").arg(QChar(200));
696 : }
697 0 : if(entity_name == "ETH")
698 : {
699 0 : return QString("%1").arg(QChar(208));
700 : }
701 0 : if(entity_name == "Euml")
702 : {
703 0 : return QString("%1").arg(QChar(203));
704 : }
705 0 : break;
706 :
707 0 : case 'e':
708 0 : if(entity_name == "eacute")
709 : {
710 0 : return QString("%1").arg(QChar(233));
711 : }
712 0 : if(entity_name == "ecirc")
713 : {
714 0 : return QString("%1").arg(QChar(234));
715 : }
716 0 : if(entity_name == "egrave")
717 : {
718 0 : return QString("%1").arg(QChar(232));
719 : }
720 0 : if(entity_name == "eth")
721 : {
722 0 : return QString("%1").arg(QChar(240));
723 : }
724 0 : if(entity_name == "euml")
725 : {
726 0 : return QString("%1").arg(QChar(235));
727 : }
728 0 : break;
729 :
730 0 : case 'f':
731 0 : if(entity_name == "frac12")
732 : {
733 0 : return QString("%1").arg(QChar(189));
734 : }
735 0 : if(entity_name == "frac14")
736 : {
737 0 : return QString("%1").arg(QChar(188));
738 : }
739 0 : if(entity_name == "frac34")
740 : {
741 0 : return QString("%1").arg(QChar(190));
742 : }
743 0 : break;
744 :
745 0 : case 'g':
746 0 : if(entity_name == "gt")
747 : {
748 0 : return ">";
749 : }
750 0 : break;
751 :
752 0 : case 'I':
753 0 : if(entity_name == "Iacute")
754 : {
755 0 : return QString("%1").arg(QChar(205));
756 : }
757 0 : if(entity_name == "Icirc")
758 : {
759 0 : return QString("%1").arg(QChar(206));
760 : }
761 0 : if(entity_name == "Igrave")
762 : {
763 0 : return QString("%1").arg(QChar(204));
764 : }
765 0 : if(entity_name == "Iuml")
766 : {
767 0 : return QString("%1").arg(QChar(207));
768 : }
769 0 : break;
770 :
771 0 : case 'i':
772 0 : if(entity_name == "iacute")
773 : {
774 0 : return QString("%1").arg(QChar(237));
775 : }
776 0 : if(entity_name == "icirc")
777 : {
778 0 : return QString("%1").arg(QChar(238));
779 : }
780 0 : if(entity_name == "iexcl")
781 : {
782 0 : return QString("%1").arg(QChar(161));
783 : }
784 0 : if(entity_name == "igrave")
785 : {
786 0 : return QString("%1").arg(QChar(236));
787 : }
788 0 : if(entity_name == "iquest")
789 : {
790 0 : return QString("%1").arg(QChar(191));
791 : }
792 0 : if(entity_name == "iuml")
793 : {
794 0 : return QString("%1").arg(QChar(239));
795 : }
796 0 : break;
797 :
798 0 : case 'l':
799 0 : if(entity_name == "laquo")
800 : {
801 0 : return QString("%1").arg(QChar(171));
802 : }
803 0 : if(entity_name == "lt")
804 : {
805 0 : return "<";
806 : }
807 0 : break;
808 :
809 0 : case 'm':
810 0 : if(entity_name == "macr")
811 : {
812 0 : return QString("%1").arg(QChar(175));
813 : }
814 0 : if(entity_name == "micro")
815 : {
816 0 : return QString("%1").arg(QChar(181));
817 : }
818 0 : if(entity_name == "middot")
819 : {
820 0 : return QString("%1").arg(QChar(183));
821 : }
822 0 : break;
823 :
824 0 : case 'N':
825 0 : if(entity_name == "Ntilde")
826 : {
827 0 : return QString("%1").arg(QChar(209));
828 : }
829 0 : break;
830 :
831 0 : case 'n':
832 0 : if(entity_name == "nbsp")
833 : {
834 0 : return QString("%1").arg(QChar(160));
835 : }
836 0 : if(entity_name == "not")
837 : {
838 0 : return QString("%1").arg(QChar(172));
839 : }
840 0 : if(entity_name == "ntilde")
841 : {
842 0 : return QString("%1").arg(QChar(241));
843 : }
844 0 : break;
845 :
846 0 : case 'O':
847 0 : if(entity_name == "Oacute")
848 : {
849 0 : return QString("%1").arg(QChar(211));
850 : }
851 0 : if(entity_name == "Ocirc")
852 : {
853 0 : return QString("%1").arg(QChar(212));
854 : }
855 0 : if(entity_name == "Ograve")
856 : {
857 0 : return QString("%1").arg(QChar(210));
858 : }
859 0 : if(entity_name == "Oslash")
860 : {
861 0 : return QString("%1").arg(QChar(216));
862 : }
863 0 : if(entity_name == "Otilde")
864 : {
865 0 : return QString("%1").arg(QChar(213));
866 : }
867 0 : if(entity_name == "Ouml")
868 : {
869 0 : return QString("%1").arg(QChar(214));
870 : }
871 0 : break;
872 :
873 0 : case 'o':
874 0 : if(entity_name == "oacute")
875 : {
876 0 : return QString("%1").arg(QChar(243));
877 : }
878 0 : if(entity_name == "ocirc")
879 : {
880 0 : return QString("%1").arg(QChar(244));
881 : }
882 0 : if(entity_name == "ograve")
883 : {
884 0 : return QString("%1").arg(QChar(242));
885 : }
886 0 : if(entity_name == "ordf")
887 : {
888 0 : return QString("%1").arg(QChar(170));
889 : }
890 0 : if(entity_name == "ordm")
891 : {
892 0 : return QString("%1").arg(QChar(186));
893 : }
894 0 : if(entity_name == "oslash")
895 : {
896 0 : return QString("%1").arg(QChar(248));
897 : }
898 0 : if(entity_name == "otilde")
899 : {
900 0 : return QString("%1").arg(QChar(245));
901 : }
902 0 : if(entity_name == "ouml")
903 : {
904 0 : return QString("%1").arg(QChar(246));
905 : }
906 0 : break;
907 :
908 0 : case 'p':
909 0 : if(entity_name == "para")
910 : {
911 0 : return QString("%1").arg(QChar(182));
912 : }
913 0 : if(entity_name == "plusmn")
914 : {
915 0 : return QString("%1").arg(QChar(177));
916 : }
917 0 : if(entity_name == "pound")
918 : {
919 0 : return QString("%1").arg(QChar(163));
920 : }
921 0 : break;
922 :
923 0 : case 'r':
924 0 : if(entity_name == "raquo")
925 : {
926 0 : return QString("%1").arg(QChar(187));
927 : }
928 0 : if(entity_name == "reg")
929 : {
930 0 : return QString("%1").arg(QChar(174));
931 : }
932 0 : break;
933 :
934 0 : case 's':
935 0 : if(entity_name == "sect")
936 : {
937 0 : return QString("%1").arg(QChar(167));
938 : }
939 0 : if(entity_name == "shy")
940 : {
941 0 : return QString("%1").arg(QChar(173));
942 : }
943 0 : if(entity_name == "sup1")
944 : {
945 0 : return QString("%1").arg(QChar(185));
946 : }
947 0 : if(entity_name == "sup2")
948 : {
949 0 : return QString("%1").arg(QChar(178));
950 : }
951 0 : if(entity_name == "sup3")
952 : {
953 0 : return QString("%1").arg(QChar(179));
954 : }
955 0 : if(entity_name == "szlig")
956 : {
957 0 : return QString("%1").arg(QChar(223));
958 : }
959 0 : break;
960 :
961 0 : case 'T':
962 0 : if(entity_name == "THORN")
963 : {
964 0 : return QString("%1").arg(QChar(222));
965 : }
966 0 : break;
967 :
968 0 : case 't':
969 0 : if(entity_name == "thorn")
970 : {
971 0 : return QString("%1").arg(QChar(254));
972 : }
973 0 : if(entity_name == "times")
974 : {
975 0 : return QString("%1").arg(QChar(215));
976 : }
977 0 : break;
978 :
979 0 : case 'U':
980 0 : if(entity_name == "Uacute")
981 : {
982 0 : return QString("%1").arg(QChar(218));
983 : }
984 0 : if(entity_name == "Ucirc")
985 : {
986 0 : return QString("%1").arg(QChar(219));
987 : }
988 0 : if(entity_name == "Ugrave")
989 : {
990 0 : return QString("%1").arg(QChar(217));
991 : }
992 0 : if(entity_name == "Uuml")
993 : {
994 0 : return QString("%1").arg(QChar(220));
995 : }
996 0 : break;
997 :
998 0 : case 'u':
999 0 : if(entity_name == "uacute")
1000 : {
1001 0 : return QString("%1").arg(QChar(250));
1002 : }
1003 0 : if(entity_name == "ucirc")
1004 : {
1005 0 : return QString("%1").arg(QChar(251));
1006 : }
1007 0 : if(entity_name == "ugrave")
1008 : {
1009 0 : return QString("%1").arg(QChar(249));
1010 : }
1011 0 : if(entity_name == "uml")
1012 : {
1013 0 : return QString("%1").arg(QChar(168));
1014 : }
1015 0 : if(entity_name == "uuml")
1016 : {
1017 0 : return QString("%1").arg(QChar(252));
1018 : }
1019 0 : break;
1020 :
1021 0 : case 'Y':
1022 0 : if(entity_name == "Yacute")
1023 : {
1024 0 : return QString("%1").arg(QChar(221));
1025 : }
1026 0 : break;
1027 :
1028 0 : case 'y':
1029 0 : if(entity_name == "yacute")
1030 : {
1031 0 : return QString("%1").arg(QChar(253));
1032 : }
1033 0 : if(entity_name == "yen")
1034 : {
1035 0 : return QString("%1").arg(QChar(165));
1036 : }
1037 0 : if(entity_name == "yuml")
1038 : {
1039 0 : return QString("%1").arg(QChar(255));
1040 : }
1041 0 : break;
1042 :
1043 : }
1044 :
1045 : // if we reach here then it was not found...
1046 0 : return QString("&%1;").arg(entity_name);
1047 : }
1048 :
1049 :
1050 6 : } // namespace snap
1051 : // vim: ts=4 sw=4 et
|