1 /******************************************************************************
2 *
3 * Copyright (C) 1997-2020 by Dimitri van Heesch.
4 *
5 * Permission to use, copy, modify, and distribute this software and its
6 * documentation under the terms of the GNU General Public License is hereby
7 * granted. No representations are made about the suitability of this software
8 * for any purpose. It is provided "as is" without express or implied warranty.
9 * See the GNU General Public License for more details.
10 *
11 * Documents produced by Doxygen are derivative works derived from the
12 * input used in their production; they are not affected by this license.
13 *
14 */
15
16 #include "htmldocvisitor.h"
17 #include "docparser.h"
18 #include "language.h"
19 #include "doxygen.h"
20 #include "outputgen.h"
21 #include "dot.h"
22 #include "message.h"
23 #include "config.h"
24 #include "htmlgen.h"
25 #include "parserintf.h"
26 #include "msc.h"
27 #include "dia.h"
28 #include "util.h"
29 #include "vhdldocgen.h"
30 #include "filedef.h"
31 #include "memberdef.h"
32 #include "htmlentity.h"
33 #include "emoji.h"
34 #include "plantuml.h"
35 #include "formula.h"
36 #include "fileinfo.h"
37
38 static const int NUM_HTML_LIST_TYPES = 4;
39 static const char types[][NUM_HTML_LIST_TYPES] = {"1", "a", "i", "A"};
40 enum contexts_t
41 {
42 NONE, // 0
43 STARTLI, // 1
44 STARTDD, // 2
45 ENDLI, // 3
46 ENDDD, // 4
47 STARTTD, // 5
48 ENDTD, // 6
49 INTERLI, // 7
50 INTERDD, // 8
51 INTERTD // 9
52 };
53 static const char *contexts[10] =
54 { "", // 0
55 "startli", // 1
56 "startdd", // 2
57 "endli", // 3
58 "enddd", // 4
59 "starttd", // 5
60 "endtd", // 6
61 "interli", // 7
62 "interdd", // 8
63 "intertd" // 9
64 };
65 static const char *hex="0123456789ABCDEF";
66
convertIndexWordToAnchor(const QCString & word)67 static QCString convertIndexWordToAnchor(const QCString &word)
68 {
69 static int cnt = 0;
70 QCString result="a";
71 QCString cntStr;
72 result += cntStr.setNum(cnt);
73 result += "_";
74 cnt++;
75 const char *str = word.data();
76 unsigned char c;
77 if (str)
78 {
79 while ((c = *str++))
80 {
81 if ((c >= 'a' && c <= 'z') || // ALPHA
82 (c >= 'A' && c <= 'Z') || // ALPHA
83 (c >= '0' && c <= '9') || // DIGIT
84 c == '-' ||
85 c == '.' ||
86 c == '_'
87 )
88 {
89 result += c;
90 }
91 else
92 {
93 char enc[4];
94 enc[0] = ':';
95 enc[1] = hex[(c & 0xf0) >> 4];
96 enc[2] = hex[c & 0xf];
97 enc[3] = 0;
98 result += enc;
99 }
100 }
101 }
102 return result;
103 }
104
mustBeOutsideParagraph(const DocNode * n)105 static bool mustBeOutsideParagraph(const DocNode *n)
106 {
107 switch (n->kind())
108 {
109 /* <ul> */
110 case DocNode::Kind_HtmlList:
111 case DocNode::Kind_SimpleList:
112 case DocNode::Kind_AutoList:
113 /* <dl> */
114 case DocNode::Kind_SimpleSect:
115 case DocNode::Kind_ParamSect:
116 case DocNode::Kind_HtmlDescList:
117 case DocNode::Kind_XRefItem:
118 /* <table> */
119 case DocNode::Kind_HtmlTable:
120 /* <h?> */
121 case DocNode::Kind_Section:
122 case DocNode::Kind_HtmlHeader:
123 /* \internal */
124 case DocNode::Kind_Internal:
125 /* <div> */
126 case DocNode::Kind_Include:
127 case DocNode::Kind_SecRefList:
128 /* <hr> */
129 case DocNode::Kind_HorRuler:
130 /* CopyDoc gets paragraph markers from the wrapping DocPara node,
131 * but needs to insert them for all documentation being copied to
132 * preserve formatting.
133 */
134 case DocNode::Kind_Copy:
135 /* <blockquote> */
136 case DocNode::Kind_HtmlBlockQuote:
137 /* \parblock */
138 case DocNode::Kind_ParBlock:
139 case DocNode::Kind_IncOperator:
140 return TRUE;
141 case DocNode::Kind_Verbatim:
142 {
143 DocVerbatim *dv = (DocVerbatim*)n;
144 DocVerbatim::Type t = dv->type();
145 if (t == DocVerbatim::JavaDocCode || t == DocVerbatim::JavaDocLiteral) return FALSE;
146 return t!=DocVerbatim::HtmlOnly || dv->isBlock();
147 }
148 case DocNode::Kind_StyleChange:
149 return ((DocStyleChange*)n)->style()==DocStyleChange::Preformatted ||
150 ((DocStyleChange*)n)->style()==DocStyleChange::Div ||
151 ((DocStyleChange*)n)->style()==DocStyleChange::Center;
152 case DocNode::Kind_Formula:
153 return !((DocFormula*)n)->isInline();
154 case DocNode::Kind_Image:
155 return !((DocImage*)n)->isInlineImage();
156 default:
157 break;
158 }
159 return FALSE;
160 }
161
isDocVerbatimVisible(const DocVerbatim * s)162 static bool isDocVerbatimVisible(const DocVerbatim *s)
163 {
164 switch(s->type())
165 {
166 case DocVerbatim::ManOnly:
167 case DocVerbatim::LatexOnly:
168 case DocVerbatim::XmlOnly:
169 case DocVerbatim::RtfOnly:
170 case DocVerbatim::DocbookOnly:
171 return FALSE;
172 default:
173 return TRUE;
174 }
175 }
176
isDocIncludeVisible(const DocInclude * s)177 static bool isDocIncludeVisible(const DocInclude *s)
178 {
179 switch (s->type())
180 {
181 case DocInclude::DontInclude:
182 case DocInclude::LatexInclude:
183 case DocInclude::RtfInclude:
184 case DocInclude::ManInclude:
185 case DocInclude::XmlInclude:
186 case DocInclude::DocbookInclude:
187 return FALSE;
188 default:
189 return TRUE;
190 }
191 }
192
isDocIncOperatorVisible(const DocIncOperator * s)193 static bool isDocIncOperatorVisible(const DocIncOperator *s)
194 {
195 switch (s->type())
196 {
197 case DocIncOperator::Skip:
198 return FALSE;
199 default:
200 return TRUE;
201 }
202 }
203
isInvisibleNode(const DocNode * node)204 static bool isInvisibleNode(const DocNode *node)
205 {
206 return (node->kind()==DocNode::Kind_WhiteSpace)
207 || // skip over image nodes that are not for HTML output
208 (node->kind()==DocNode::Kind_Image && ((DocImage*)node)->type()!=DocImage::Html)
209 || // skip over verbatim nodes that are not visible in the HTML output
210 (node->kind()==DocNode::Kind_Verbatim && !isDocVerbatimVisible((DocVerbatim*)node))
211 || // skip over include nodes that are not visible in the HTML output
212 (node->kind()==DocNode::Kind_Include && !isDocIncludeVisible((DocInclude*)node))
213 || // skip over include operator nodes that are not visible in the HTML output
214 (node->kind()==DocNode::Kind_IncOperator && !isDocIncOperatorVisible((DocIncOperator*)node))
215 ;
216 }
217
mergeHtmlAttributes(const HtmlAttribList & attribs,HtmlAttribList & mergeInto)218 static void mergeHtmlAttributes(const HtmlAttribList &attribs, HtmlAttribList &mergeInto)
219 {
220 for (const auto &att : attribs)
221 {
222 auto it = std::find_if(mergeInto.begin(),mergeInto.end(),
223 [&att](const auto &opt) { return opt.name==att.name; });
224 if (it!=mergeInto.end()) // attribute name already in mergeInto
225 {
226 it->value = it->value + " " + att.value;
227 }
228 else // attribute name not yet in mergeInto
229 {
230 mergeInto.push_back(att);
231 }
232 }
233 }
234
htmlAttribsToString(const HtmlAttribList & attribs,QCString * pAltValue=0)235 static QCString htmlAttribsToString(const HtmlAttribList &attribs, QCString *pAltValue = 0)
236 {
237 QCString result;
238 for (const auto &att : attribs)
239 {
240 if (!att.value.isEmpty()) // ignore attribute without values as they
241 // are not XHTML compliant, with the exception
242 // of the alt attribute with the img tag
243 {
244 if (att.name=="alt" && pAltValue) // optionally return the value of alt separately
245 // need to convert <img> to <object> for SVG images,
246 // which do not support the alt attribute
247 {
248 *pAltValue = att.value;
249 }
250 else
251 {
252 result+=" ";
253 result+=att.name;
254 result+="=\""+convertToXML(att.value)+"\"";
255 }
256 }
257 else if (att.name=="open")
258 {
259 // The open attribute is a boolean attribute.
260 // Specifies that the details should be visible (open) to the user
261 // As it is a boolean attribute the initialisation value is of no interest
262 result+=" ";
263 result+=att.name;
264 result+="=\"true\"";
265 }
266 else if (att.name=="nowrap") // In XHTML, attribute minimization is forbidden, and the nowrap attribute must be defined as <td nowrap="nowrap">.
267 {
268 result+=" ";
269 result+=att.name;
270 result+="=\"nowrap\"";
271 }
272 }
273 return result;
274 }
275
276 //-------------------------------------------------------------------------
277
HtmlDocVisitor(TextStream & t,CodeOutputInterface & ci,const Definition * ctx)278 HtmlDocVisitor::HtmlDocVisitor(TextStream &t,CodeOutputInterface &ci,
279 const Definition *ctx)
280 : DocVisitor(DocVisitor_Html), m_t(t), m_ci(ci), m_ctx(ctx)
281 {
282 if (ctx) m_langExt=ctx->getDefFileExtension();
283 }
284
285 //--------------------------------------
286 // visitor functions for leaf nodes
287 //--------------------------------------
288
visit(DocWord * w)289 void HtmlDocVisitor::visit(DocWord *w)
290 {
291 //printf("word: %s\n",qPrint(w->word()));
292 if (m_hide) return;
293 filter(w->word());
294 }
295
visit(DocLinkedWord * w)296 void HtmlDocVisitor::visit(DocLinkedWord *w)
297 {
298 if (m_hide) return;
299 //printf("linked word: %s\n",qPrint(w->word()));
300 startLink(w->ref(),w->file(),w->relPath(),w->anchor(),w->tooltip());
301 filter(w->word());
302 endLink();
303 }
304
visit(DocWhiteSpace * w)305 void HtmlDocVisitor::visit(DocWhiteSpace *w)
306 {
307 if (m_hide) return;
308 if (m_insidePre)
309 {
310 m_t << w->chars();
311 }
312 else
313 {
314 m_t << " ";
315 }
316 }
317
visit(DocSymbol * s)318 void HtmlDocVisitor::visit(DocSymbol *s)
319 {
320 if (m_hide) return;
321 if (m_insideTitle &&
322 (s->symbol()==DocSymbol::Sym_Quot || s->symbol()==DocSymbol::Sym_quot)) // escape "'s inside title="..."
323 {
324 m_t << """;
325 }
326 else
327 {
328 const char *res = HtmlEntityMapper::instance()->html(s->symbol());
329 if (res)
330 {
331 m_t << res;
332 }
333 else
334 {
335 err("HTML: non supported HTML-entity found: %s\n",HtmlEntityMapper::instance()->html(s->symbol(),TRUE));
336 }
337 }
338 }
339
visit(DocEmoji * s)340 void HtmlDocVisitor::visit(DocEmoji *s)
341 {
342 if (m_hide) return;
343 const char *res = EmojiEntityMapper::instance()->unicode(s->index());
344 if (res)
345 {
346 m_t << "<span class=\"emoji\">"<<res<<"</span>";
347 }
348 else
349 {
350 m_t << s->name();
351 }
352 }
353
writeObfuscatedMailAddress(const QCString & url)354 void HtmlDocVisitor::writeObfuscatedMailAddress(const QCString &url)
355 {
356 if (!Config_getBool(OBFUSCATE_EMAILS))
357 {
358 m_t << "<a href=\"mailto:" << url << "\">";
359 }
360 else
361 {
362 m_t << "<a href=\"#\" onclick=\"location.href='mai'+'lto:'";
363 if (!url.isEmpty())
364 {
365 const char *p = url.data();
366 uint size=3;
367 while (*p)
368 {
369 m_t << "+'";
370 for (uint j=0;j<size && *p;j++)
371 {
372 p = writeUTF8Char(m_t,p);
373 }
374 m_t << "'";
375 if (size==3) size=2; else size=3;
376 }
377 }
378 m_t << "; return false;\">";
379 }
380 }
381
visit(DocURL * u)382 void HtmlDocVisitor::visit(DocURL *u)
383 {
384 if (m_hide) return;
385 if (u->isEmail()) // mail address
386 {
387 QCString url = u->url();
388 // obfuscate the mail address link
389 writeObfuscatedMailAddress(url);
390 if (!Config_getBool(OBFUSCATE_EMAILS))
391 {
392 m_t << url;
393 }
394 else
395 {
396 const char *p = url.data();
397 // also obfuscate the address as shown on the web page
398 uint size=5;
399 while (*p)
400 {
401 for (uint j=0;j<size && *p;j++)
402 {
403 p = writeUTF8Char(m_t,p);
404 }
405 if (*p) m_t << "<span class=\"obfuscator\">.nosp@m.</span>";
406 if (size==5) size=4; else size=5;
407 }
408 }
409 m_t << "</a>";
410 }
411 else // web address
412 {
413 m_t << "<a href=\"";
414 m_t << u->url() << "\">";
415 filter(u->url());
416 m_t << "</a>";
417 }
418 }
419
visit(DocLineBreak * br)420 void HtmlDocVisitor::visit(DocLineBreak *br)
421 {
422 if (m_hide) return;
423 m_t << "<br "<< htmlAttribsToString(br->attribs()) << " />\n";
424 }
425
visit(DocHorRuler * hr)426 void HtmlDocVisitor::visit(DocHorRuler *hr)
427 {
428 if (m_hide) return;
429 forceEndParagraph(hr);
430 m_t << "<hr "<< htmlAttribsToString(hr->attribs()) << " />\n";
431 forceStartParagraph(hr);
432 }
433
visit(DocStyleChange * s)434 void HtmlDocVisitor::visit(DocStyleChange *s)
435 {
436 if (m_hide) return;
437 switch (s->style())
438 {
439 case DocStyleChange::Bold:
440 if (s->enable()) m_t << "<b" << htmlAttribsToString(s->attribs()) << ">"; else m_t << "</b>";
441 break;
442 case DocStyleChange::S:
443 if (s->enable()) m_t << "<s" << htmlAttribsToString(s->attribs()) << ">"; else m_t << "</s>";
444 break;
445 case DocStyleChange::Strike:
446 if (s->enable()) m_t << "<strike" << htmlAttribsToString(s->attribs()) << ">"; else m_t << "</strike>";
447 break;
448 case DocStyleChange::Del:
449 if (s->enable()) m_t << "<del" << htmlAttribsToString(s->attribs()) << ">"; else m_t << "</del>";
450 break;
451 case DocStyleChange::Underline:
452 if (s->enable()) m_t << "<u" << htmlAttribsToString(s->attribs()) << ">"; else m_t << "</u>";
453 break;
454 case DocStyleChange::Ins:
455 if (s->enable()) m_t << "<ins" << htmlAttribsToString(s->attribs()) << ">"; else m_t << "</ins>";
456 break;
457 case DocStyleChange::Italic:
458 if (s->enable()) m_t << "<em" << htmlAttribsToString(s->attribs()) << ">"; else m_t << "</em>";
459 break;
460 case DocStyleChange::Code:
461 if (s->enable()) m_t << "<code" << htmlAttribsToString(s->attribs()) << ">"; else m_t << "</code>";
462 break;
463 case DocStyleChange::Subscript:
464 if (s->enable()) m_t << "<sub" << htmlAttribsToString(s->attribs()) << ">"; else m_t << "</sub>";
465 break;
466 case DocStyleChange::Superscript:
467 if (s->enable()) m_t << "<sup" << htmlAttribsToString(s->attribs()) << ">"; else m_t << "</sup>";
468 break;
469 case DocStyleChange::Center:
470 if (s->enable())
471 {
472 forceEndParagraph(s);
473 m_t << "<center" << htmlAttribsToString(s->attribs()) << ">";
474 }
475 else
476 {
477 m_t << "</center>";
478 forceStartParagraph(s);
479 }
480 break;
481 case DocStyleChange::Small:
482 if (s->enable()) m_t << "<small" << htmlAttribsToString(s->attribs()) << ">"; else m_t << "</small>";
483 break;
484 case DocStyleChange::Cite:
485 if (s->enable()) m_t << "<cite" << htmlAttribsToString(s->attribs()) << ">"; else m_t << "</cite>";
486 break;
487 case DocStyleChange::Preformatted:
488 if (s->enable())
489 {
490 forceEndParagraph(s);
491 m_t << "<pre" << htmlAttribsToString(s->attribs()) << ">";
492 m_insidePre=TRUE;
493 }
494 else
495 {
496 m_insidePre=FALSE;
497 m_t << "</pre>";
498 forceStartParagraph(s);
499 }
500 break;
501 case DocStyleChange::Div:
502 if (s->enable())
503 {
504 forceEndParagraph(s);
505 m_t << "<div" << htmlAttribsToString(s->attribs()) << ">";
506 }
507 else
508 {
509 m_t << "</div>";
510 forceStartParagraph(s);
511 }
512 break;
513 case DocStyleChange::Span:
514 if (s->enable()) m_t << "<span" << htmlAttribsToString(s->attribs()) << ">"; else m_t << "</span>";
515 break;
516 case DocStyleChange::Details:
517 if (s->enable()) m_t << "<details" << htmlAttribsToString(s->attribs()) << ">\n"; else m_t << "</details>\n";
518 break;
519 case DocStyleChange::Summary:
520 if (s->enable()) m_t << "<summary" << htmlAttribsToString(s->attribs()) << ">"; else m_t << "</summary>";
521 break;
522 }
523 }
524
525
visitPreCaption(TextStream & t,DocVerbatim * s)526 static void visitPreCaption(TextStream &t, DocVerbatim *s)
527 {
528 if (s->hasCaption())
529 {
530 t << "<div class=\"caption\">\n";
531 }
532 }
533
534
visitPostCaption(TextStream & t,DocVerbatim * s)535 static void visitPostCaption(TextStream &t, DocVerbatim *s)
536 {
537 if (s->hasCaption())
538 {
539 t << "</div>\n";
540 }
541 }
542
543
visitCaption(HtmlDocVisitor * parent,DocNodeList & children)544 static void visitCaption(HtmlDocVisitor *parent, DocNodeList &children)
545 {
546 for (const auto &n : children) n->accept(parent);
547 }
548
visit(DocVerbatim * s)549 void HtmlDocVisitor::visit(DocVerbatim *s)
550 {
551 if (m_hide) return;
552 QCString lang = m_langExt;
553 if (!s->language().isEmpty()) // explicit language setting
554 {
555 lang = s->language();
556 }
557 SrcLangExt langExt = getLanguageFromCodeLang(lang);
558 switch(s->type())
559 {
560 case DocVerbatim::Code:
561 forceEndParagraph(s);
562 m_ci.startCodeFragment("DoxyCode");
563 getCodeParser(lang).parseCode(m_ci,
564 s->context(),
565 s->text(),
566 langExt,
567 s->isExample(),
568 s->exampleFile(),
569 0, // fileDef
570 -1, // startLine
571 -1, // endLine
572 FALSE, // inlineFragment
573 0, // memberDef
574 TRUE, // show line numbers
575 m_ctx // search context
576 );
577 m_ci.endCodeFragment("DoxyCode");
578 forceStartParagraph(s);
579 break;
580 case DocVerbatim::Verbatim:
581 forceEndParagraph(s);
582 m_t << "<pre class=\"fragment\">";
583 filter(s->text());
584 m_t << "</pre>";
585 forceStartParagraph(s);
586 break;
587 case DocVerbatim::JavaDocLiteral:
588 filter(s->text(), true);
589 break;
590 case DocVerbatim::JavaDocCode:
591 m_t << "<code class=\"JavaDocCode\">";
592 filter(s->text(), true);
593 m_t << "</code>";
594 break;
595 case DocVerbatim::HtmlOnly:
596 {
597 if (s->isBlock()) forceEndParagraph(s);
598 m_t << s->text();
599 if (s->isBlock()) forceStartParagraph(s);
600 }
601 break;
602 case DocVerbatim::ManOnly:
603 case DocVerbatim::LatexOnly:
604 case DocVerbatim::XmlOnly:
605 case DocVerbatim::RtfOnly:
606 case DocVerbatim::DocbookOnly:
607 /* nothing */
608 break;
609
610 case DocVerbatim::Dot:
611 {
612 static int dotindex = 1;
613 QCString fileName(4096);
614
615 forceEndParagraph(s);
616 fileName.sprintf("%s%d%s",
617 qPrint(Config_getString(HTML_OUTPUT)+"/inline_dotgraph_"),
618 dotindex++,
619 ".dot"
620 );
621 std::ofstream file(fileName.str(),std::ofstream::out | std::ofstream::binary);
622 if (!file.is_open())
623 {
624 err("Could not open file %s for writing\n",qPrint(fileName));
625 }
626 else
627 {
628 QCString stext = s->text();
629 file.write( stext.data(), stext.length() );
630 file.close();
631
632 m_t << "<div class=\"dotgraph\">\n";
633 writeDotFile(fileName,s->relPath(),s->context(),s->srcFile(),s->srcLine());
634 visitPreCaption(m_t, s);
635 visitCaption(this, s->children());
636 visitPostCaption(m_t, s);
637 m_t << "</div>\n";
638
639 if (Config_getBool(DOT_CLEANUP)) Dir().remove(fileName.str());
640 }
641 forceStartParagraph(s);
642 }
643 break;
644 case DocVerbatim::Msc:
645 {
646 forceEndParagraph(s);
647
648 static int mscindex = 1;
649 QCString baseName(4096);
650
651 baseName.sprintf("%s%d",
652 qPrint(Config_getString(HTML_OUTPUT)+"/inline_mscgraph_"),
653 mscindex++
654 );
655 std::ofstream file(baseName.str()+".msc",std::ofstream::out | std::ofstream::binary);
656 if (!file.is_open())
657 {
658 err("Could not open file %s.msc for writing\n",qPrint(baseName));
659 }
660 else
661 {
662 QCString text = "msc {";
663 text+=s->text();
664 text+="}";
665
666 file.write( text.data(), text.length() );
667 file.close();
668
669 m_t << "<div class=\"mscgraph\">\n";
670 writeMscFile(baseName+".msc",s->relPath(),s->context(),s->srcFile(),s->srcLine());
671 visitPreCaption(m_t, s);
672 visitCaption(this, s->children());
673 visitPostCaption(m_t, s);
674 m_t << "</div>\n";
675
676 if (Config_getBool(DOT_CLEANUP)) Dir().remove(baseName.str()+".msc");
677 }
678 forceStartParagraph(s);
679 }
680 break;
681 case DocVerbatim::PlantUML:
682 {
683 forceEndParagraph(s);
684 static QCString htmlOutput = Config_getString(HTML_OUTPUT);
685 QCString imgExt = getDotImageExtension();
686 PlantumlManager::OutputFormat format = PlantumlManager::PUML_BITMAP; // default : PUML_BITMAP
687 if (imgExt=="svg")
688 {
689 format = PlantumlManager::PUML_SVG;
690 }
691 QCString baseName = PlantumlManager::instance().writePlantUMLSource(
692 htmlOutput,s->exampleFile(),
693 s->text(),format,s->engine(),s->srcFile(),s->srcLine());
694 m_t << "<div class=\"plantumlgraph\">\n";
695 writePlantUMLFile(baseName,s->relPath(),s->context(),s->srcFile(),s->srcLine());
696 visitPreCaption(m_t, s);
697 visitCaption(this, s->children());
698 visitPostCaption(m_t, s);
699 m_t << "</div>\n";
700 forceStartParagraph(s);
701 }
702 break;
703 }
704 }
705
visit(DocAnchor * anc)706 void HtmlDocVisitor::visit(DocAnchor *anc)
707 {
708 if (m_hide) return;
709 m_t << "<a class=\"anchor\" id=\"" << anc->anchor() << "\"" << htmlAttribsToString(anc->attribs()) << "></a>";
710 }
711
visit(DocInclude * inc)712 void HtmlDocVisitor::visit(DocInclude *inc)
713 {
714 if (m_hide) return;
715 SrcLangExt langExt = getLanguageFromFileName(inc->extension());
716 switch(inc->type())
717 {
718 case DocInclude::Include:
719 forceEndParagraph(inc);
720 m_ci.startCodeFragment("DoxyCode");
721 getCodeParser(inc->extension()).parseCode(m_ci,
722 inc->context(),
723 inc->text(),
724 langExt,
725 inc->isExample(),
726 inc->exampleFile(),
727 0, // fileDef
728 -1, // startLine
729 -1, // endLine
730 TRUE, // inlineFragment
731 0, // memberDef
732 FALSE, // show line numbers
733 m_ctx // search context
734 );
735 m_ci.endCodeFragment("DoxyCode");
736 forceStartParagraph(inc);
737 break;
738 case DocInclude::IncWithLines:
739 {
740 forceEndParagraph(inc);
741 m_ci.startCodeFragment("DoxyCode");
742 FileInfo cfi( inc->file().str() );
743 FileDef *fd = createFileDef( cfi.dirPath(), cfi.fileName() );
744 getCodeParser(inc->extension()).parseCode(m_ci,
745 inc->context(),
746 inc->text(),
747 langExt,
748 inc->isExample(),
749 inc->exampleFile(),
750 fd, // fileDef,
751 -1, // start line
752 -1, // end line
753 FALSE, // inline fragment
754 0, // memberDef
755 TRUE, // show line numbers
756 m_ctx // search context
757 );
758 delete fd;
759 m_ci.endCodeFragment("DoxyCode");
760 forceStartParagraph(inc);
761 }
762 break;
763 case DocInclude::DontInclude:
764 case DocInclude::LatexInclude:
765 case DocInclude::RtfInclude:
766 case DocInclude::ManInclude:
767 case DocInclude::XmlInclude:
768 case DocInclude::DocbookInclude:
769 case DocInclude::DontIncWithLines:
770 break;
771 case DocInclude::HtmlInclude:
772 {
773 if (inc->isBlock()) forceEndParagraph(inc);
774 m_t << inc->text();
775 if (inc->isBlock()) forceStartParagraph(inc);
776 }
777 break;
778 case DocInclude::VerbInclude:
779 forceEndParagraph(inc);
780 m_t << "<pre class=\"fragment\">";
781 filter(inc->text());
782 m_t << "</pre>";
783 forceStartParagraph(inc);
784 break;
785 case DocInclude::Snippet:
786 {
787 forceEndParagraph(inc);
788 m_ci.startCodeFragment("DoxyCode");
789 getCodeParser(inc->extension()).parseCode(m_ci,
790 inc->context(),
791 extractBlock(inc->text(),inc->blockId()),
792 langExt,
793 inc->isExample(),
794 inc->exampleFile(),
795 0,
796 -1, // startLine
797 -1, // endLine
798 TRUE, // inlineFragment
799 0, // memberDef
800 FALSE, // show line number
801 m_ctx // search context
802 );
803 m_ci.endCodeFragment("DoxyCode");
804 forceStartParagraph(inc);
805 }
806 break;
807 case DocInclude::SnipWithLines:
808 {
809 forceEndParagraph(inc);
810 m_ci.startCodeFragment("DoxyCode");
811 FileInfo cfi( inc->file().str() );
812 FileDef *fd = createFileDef( cfi.dirPath(), cfi.fileName() );
813 getCodeParser(inc->extension()).parseCode(m_ci,
814 inc->context(),
815 extractBlock(inc->text(),inc->blockId()),
816 langExt,
817 inc->isExample(),
818 inc->exampleFile(),
819 fd,
820 lineBlock(inc->text(),inc->blockId()),
821 -1, // endLine
822 FALSE, // inlineFragment
823 0, // memberDef
824 TRUE, // show line number
825 m_ctx // search context
826 );
827 delete fd;
828 m_ci.endCodeFragment("DoxyCode");
829 forceStartParagraph(inc);
830 }
831 break;
832 case DocInclude::SnippetDoc:
833 case DocInclude::IncludeDoc:
834 err("Internal inconsistency: found switch SnippetDoc / IncludeDoc in file: %s"
835 "Please create a bug report\n",__FILE__);
836 break;
837 }
838 }
839
visit(DocIncOperator * op)840 void HtmlDocVisitor::visit(DocIncOperator *op)
841 {
842 //printf("DocIncOperator: type=%d first=%d, last=%d text='%s'\n",
843 // op->type(),op->isFirst(),op->isLast(),qPrint(op->text()));
844 if (op->isFirst())
845 {
846 forceEndParagraph(op);
847 if (!m_hide) m_ci.startCodeFragment("DoxyCode");
848 pushHidden(m_hide);
849 m_hide=TRUE;
850 }
851 QCString locLangExt = getFileNameExtension(op->includeFileName());
852 if (locLangExt.isEmpty()) locLangExt = m_langExt;
853 SrcLangExt langExt = getLanguageFromFileName(locLangExt);
854 if (op->type()!=DocIncOperator::Skip)
855 {
856 m_hide = popHidden();
857 if (!m_hide)
858 {
859 FileDef *fd = 0;
860 if (!op->includeFileName().isEmpty())
861 {
862 FileInfo cfi( op->includeFileName().str() );
863 fd = createFileDef( cfi.dirPath(), cfi.fileName() );
864 }
865 getCodeParser(locLangExt).parseCode(
866 m_ci,
867 op->context(),
868 op->text(),
869 langExt,
870 op->isExample(),
871 op->exampleFile(),
872 fd, // fileDef
873 op->line(), // startLine
874 -1, // endLine
875 FALSE, // inline fragment
876 0, // memberDef
877 op->showLineNo(), // show line numbers
878 m_ctx // search context
879 );
880 if (fd) delete fd;
881 }
882 pushHidden(m_hide);
883 m_hide=TRUE;
884 }
885 if (op->isLast())
886 {
887 m_hide = popHidden();
888 if (!m_hide) m_ci.endCodeFragment("DoxyCode");
889 forceStartParagraph(op);
890 }
891 else
892 {
893 if (!m_hide) m_t << "\n";
894 }
895 }
896
visit(DocFormula * f)897 void HtmlDocVisitor::visit(DocFormula *f)
898 {
899 if (m_hide) return;
900 bool bDisplay = !f->isInline();
901 if (bDisplay)
902 {
903 forceEndParagraph(f);
904 m_t << "<p class=\"formulaDsp\">\n";
905 }
906
907 if (Config_getBool(USE_MATHJAX))
908 {
909 QCString text = f->text();
910 bool closeInline = FALSE;
911 if (!bDisplay && !text.isEmpty() && text.at(0)=='$' &&
912 text.at(text.length()-1)=='$')
913 {
914 closeInline=TRUE;
915 text = text.mid(1,text.length()-2);
916 m_t << "\\(";
917 }
918 else if (!bDisplay && !text.isEmpty())
919 {
920 closeInline=TRUE;
921 m_t << "\\(";
922 }
923 m_t << convertToHtml(text);
924 if (closeInline)
925 {
926 m_t << "\\)";
927 }
928 }
929 else
930 {
931 m_t << "<img class=\"formula"
932 << (bDisplay ? "Dsp" : "Inl");
933 m_t << "\" alt=\"";
934 filterQuotedCdataAttr(f->text());
935 m_t << "\"";
936 m_t << " src=\"" << f->relPath() << f->name();
937 if (Config_getEnum(HTML_FORMULA_FORMAT)==HTML_FORMULA_FORMAT_t::svg)
938 {
939 m_t << ".svg";
940 }
941 else
942 {
943 m_t << ".png";
944 }
945 FormulaManager::DisplaySize size = FormulaManager::instance().displaySize(f->id());
946 if (size.width!=-1)
947 {
948 m_t << "\" width=\"" << size.width;
949 }
950 if (size.height!=-1)
951 {
952 m_t << "\" height=\"" << size.height;
953 }
954 m_t << "\"/>";
955 }
956 if (bDisplay)
957 {
958 m_t << "\n</p>\n";
959 forceStartParagraph(f);
960 }
961 }
962
visit(DocIndexEntry * e)963 void HtmlDocVisitor::visit(DocIndexEntry *e)
964 {
965 QCString anchor = convertIndexWordToAnchor(e->entry());
966 if (e->member())
967 {
968 anchor.prepend(e->member()->anchor()+"_");
969 }
970 m_t << "<a id=\"" << anchor << "\" name=\"" << anchor << "\"></a>";
971 //printf("*** DocIndexEntry: word='%s' scope='%s' member='%s'\n",
972 // qPrint(e->entry()),
973 // e->scope() ? qPrint(e->scope()->name()) : "<null>",
974 // e->member() ? qPrint(e->member()->name()) : "<null>"
975 // );
976 Doxygen::indexList->addIndexItem(e->scope(),e->member(),anchor,e->entry());
977 }
978
visit(DocSimpleSectSep *)979 void HtmlDocVisitor::visit(DocSimpleSectSep *)
980 {
981 m_t << "</dd>\n";
982 m_t << "<dd>\n";
983 }
984
visit(DocCite * cite)985 void HtmlDocVisitor::visit(DocCite *cite)
986 {
987 if (m_hide) return;
988 if (!cite->file().isEmpty())
989 {
990 startLink(cite->ref(),cite->file(),cite->relPath(),cite->anchor());
991 }
992 else
993 {
994 m_t << "<b>[";
995 }
996 filter(cite->text());
997 if (!cite->file().isEmpty())
998 {
999 endLink();
1000 }
1001 else
1002 {
1003 m_t << "]</b>";
1004 }
1005 }
1006
1007
1008 //--------------------------------------
1009 // visitor functions for compound nodes
1010 //--------------------------------------
1011
1012
visitPre(DocAutoList * l)1013 void HtmlDocVisitor::visitPre(DocAutoList *l)
1014 {
1015 //printf("DocAutoList::visitPre\n");
1016 if (m_hide) return;
1017 forceEndParagraph(l);
1018 if (l->isEnumList())
1019 {
1020 //
1021 // Do list type based on depth:
1022 // 1.
1023 // a.
1024 // i.
1025 // A.
1026 // 1. (repeat)...
1027 //
1028 m_t << "<ol type=\"" << types[l->depth() % NUM_HTML_LIST_TYPES] << "\">";
1029 }
1030 else
1031 {
1032 m_t << "<ul>";
1033 }
1034 if (!l->isPreformatted()) m_t << "\n";
1035 }
1036
visitPost(DocAutoList * l)1037 void HtmlDocVisitor::visitPost(DocAutoList *l)
1038 {
1039 //printf("DocAutoList::visitPost\n");
1040 if (m_hide) return;
1041 if (l->isEnumList())
1042 {
1043 m_t << "</ol>";
1044 }
1045 else
1046 {
1047 m_t << "</ul>";
1048 }
1049 if (!l->isPreformatted()) m_t << "\n";
1050 forceStartParagraph(l);
1051 }
1052
visitPre(DocAutoListItem *)1053 void HtmlDocVisitor::visitPre(DocAutoListItem *)
1054 {
1055 if (m_hide) return;
1056 m_t << "<li>";
1057 }
1058
visitPost(DocAutoListItem * li)1059 void HtmlDocVisitor::visitPost(DocAutoListItem *li)
1060 {
1061 if (m_hide) return;
1062 m_t << "</li>";
1063 if (!li->isPreformatted()) m_t << "\n";
1064 }
1065
1066 template<class T>
isFirstChildNode(T * parent,DocNode * node)1067 bool isFirstChildNode(T *parent, DocNode *node)
1068 {
1069 return !parent->children().empty() && parent->children().front().get()==node;
1070 }
1071
1072 template<class T>
isLastChildNode(T * parent,DocNode * node)1073 bool isLastChildNode(T *parent, DocNode *node)
1074 {
1075 return !parent->children().empty() && parent->children().back().get()==node;
1076 }
1077
isSeparatedParagraph(DocSimpleSect * parent,DocPara * par)1078 bool isSeparatedParagraph(DocSimpleSect *parent,DocPara *par)
1079 {
1080 const DocNodeList &nodes = parent->children();
1081 auto it = std::find_if(nodes.begin(),nodes.end(),[par](const auto &n) { return n.get()==par; });
1082 if (it==nodes.end()) return FALSE;
1083 size_t i = it - nodes.begin();
1084 size_t count = parent->children().size();
1085 if (count>1 && i==0) // first node
1086 {
1087 if (nodes.at(i+1)->kind()==DocNode::Kind_SimpleSectSep)
1088 {
1089 return TRUE;
1090 }
1091 }
1092 else if (count>1 && i==count-1) // last node
1093 {
1094 if (nodes.at(i-1)->kind()==DocNode::Kind_SimpleSectSep)
1095 {
1096 return TRUE;
1097 }
1098 }
1099 else if (count>2 && i>0 && i<count-1) // intermediate node
1100 {
1101 if (nodes.at(i-1)->kind()==DocNode::Kind_SimpleSectSep &&
1102 nodes.at(i+1)->kind()==DocNode::Kind_SimpleSectSep)
1103 {
1104 return TRUE;
1105 }
1106 }
1107 return FALSE;
1108 }
1109
getParagraphContext(DocPara * p,bool & isFirst,bool & isLast)1110 static int getParagraphContext(DocPara *p,bool &isFirst,bool &isLast)
1111 {
1112 int t=0;
1113 isFirst=FALSE;
1114 isLast=FALSE;
1115 if (p && p->parent())
1116 {
1117 switch (p->parent()->kind())
1118 {
1119 case DocNode::Kind_ParBlock:
1120 { // hierarchy: node N -> para -> parblock -> para
1121 // adapt return value to kind of N
1122 DocNode::Kind kind = DocNode::Kind_Para;
1123 if ( p->parent()->parent() && p->parent()->parent()->parent() )
1124 {
1125 kind = p->parent()->parent()->parent()->kind();
1126 }
1127 isFirst=isFirstChildNode((DocParBlock*)p->parent(),p);
1128 isLast =isLastChildNode ((DocParBlock*)p->parent(),p);
1129 t=NONE;
1130 if (isFirst)
1131 {
1132 if (kind==DocNode::Kind_HtmlListItem ||
1133 kind==DocNode::Kind_SecRefItem)
1134 {
1135 t=STARTLI;
1136 }
1137 else if (kind==DocNode::Kind_HtmlDescData ||
1138 kind==DocNode::Kind_XRefItem ||
1139 kind==DocNode::Kind_SimpleSect)
1140 {
1141 t=STARTDD;
1142 }
1143 else if (kind==DocNode::Kind_HtmlCell ||
1144 kind==DocNode::Kind_ParamList)
1145 {
1146 t=STARTTD;
1147 }
1148 }
1149 if (isLast)
1150 {
1151 if (kind==DocNode::Kind_HtmlListItem ||
1152 kind==DocNode::Kind_SecRefItem)
1153 {
1154 t=ENDLI;
1155 }
1156 else if (kind==DocNode::Kind_HtmlDescData ||
1157 kind==DocNode::Kind_XRefItem ||
1158 kind==DocNode::Kind_SimpleSect)
1159 {
1160 t=ENDDD;
1161 }
1162 else if (kind==DocNode::Kind_HtmlCell ||
1163 kind==DocNode::Kind_ParamList)
1164 {
1165 t=ENDTD;
1166 }
1167 }
1168 if (!isFirst && !isLast)
1169 {
1170 if (kind==DocNode::Kind_HtmlListItem ||
1171 kind==DocNode::Kind_SecRefItem)
1172 {
1173 t=INTERLI;
1174 }
1175 else if (kind==DocNode::Kind_HtmlDescData ||
1176 kind==DocNode::Kind_XRefItem ||
1177 kind==DocNode::Kind_SimpleSect)
1178 {
1179 t=INTERDD;
1180 }
1181 else if (kind==DocNode::Kind_HtmlCell ||
1182 kind==DocNode::Kind_ParamList)
1183 {
1184 t=INTERTD;
1185 }
1186 }
1187 break;
1188 }
1189 case DocNode::Kind_AutoListItem:
1190 isFirst=isFirstChildNode((DocAutoListItem*)p->parent(),p);
1191 isLast =isLastChildNode ((DocAutoListItem*)p->parent(),p);
1192 t=STARTLI; // not used
1193 break;
1194 case DocNode::Kind_SimpleListItem:
1195 isFirst=TRUE;
1196 isLast =TRUE;
1197 t=STARTLI; // not used
1198 break;
1199 case DocNode::Kind_ParamList:
1200 isFirst=TRUE;
1201 isLast =TRUE;
1202 t=STARTLI; // not used
1203 break;
1204 case DocNode::Kind_HtmlListItem:
1205 isFirst=isFirstChildNode((DocHtmlListItem*)p->parent(),p);
1206 isLast =isLastChildNode ((DocHtmlListItem*)p->parent(),p);
1207 if (isFirst) t=STARTLI;
1208 if (isLast) t=ENDLI;
1209 if (!isFirst && !isLast) t = INTERLI;
1210 break;
1211 case DocNode::Kind_SecRefItem:
1212 isFirst=isFirstChildNode((DocSecRefItem*)p->parent(),p);
1213 isLast =isLastChildNode ((DocSecRefItem*)p->parent(),p);
1214 if (isFirst) t=STARTLI;
1215 if (isLast) t=ENDLI;
1216 if (!isFirst && !isLast) t = INTERLI;
1217 break;
1218 case DocNode::Kind_HtmlDescData:
1219 isFirst=isFirstChildNode((DocHtmlDescData*)p->parent(),p);
1220 isLast =isLastChildNode ((DocHtmlDescData*)p->parent(),p);
1221 if (isFirst) t=STARTDD;
1222 if (isLast) t=ENDDD;
1223 if (!isFirst && !isLast) t = INTERDD;
1224 break;
1225 case DocNode::Kind_XRefItem:
1226 isFirst=isFirstChildNode((DocXRefItem*)p->parent(),p);
1227 isLast =isLastChildNode ((DocXRefItem*)p->parent(),p);
1228 if (isFirst) t=STARTDD;
1229 if (isLast) t=ENDDD;
1230 if (!isFirst && !isLast) t = INTERDD;
1231 break;
1232 case DocNode::Kind_SimpleSect:
1233 isFirst=isFirstChildNode((DocSimpleSect*)p->parent(),p);
1234 isLast =isLastChildNode ((DocSimpleSect*)p->parent(),p);
1235 if (isFirst) t=STARTDD;
1236 if (isLast) t=ENDDD;
1237 if (isSeparatedParagraph((DocSimpleSect*)p->parent(),p))
1238 // if the paragraph is enclosed with separators it will
1239 // be included in <dd>..</dd> so avoid addition paragraph
1240 // markers
1241 {
1242 isFirst=isLast=TRUE;
1243 }
1244 if (!isFirst && !isLast) t = INTERDD;
1245 break;
1246 case DocNode::Kind_HtmlCell:
1247 isFirst=isFirstChildNode((DocHtmlCell*)p->parent(),p);
1248 isLast =isLastChildNode ((DocHtmlCell*)p->parent(),p);
1249 if (isFirst) t=STARTTD;
1250 if (isLast) t=ENDTD;
1251 if (!isFirst && !isLast) t = INTERTD;
1252 break;
1253 default:
1254 break;
1255 }
1256 //printf("para=%p parent()->kind=%d isFirst=%d isLast=%d t=%d\n",
1257 // p,p->parent()->kind(),isFirst,isLast,t);
1258 }
1259 return t;
1260 }
1261
visitPre(DocPara * p)1262 void HtmlDocVisitor::visitPre(DocPara *p)
1263 {
1264 if (m_hide) return;
1265
1266 //printf("DocPara::visitPre: parent of kind %d ",
1267 // p->parent() ? p->parent()->kind() : -1);
1268
1269 bool needsTag = FALSE;
1270 if (p && p->parent())
1271 {
1272 switch (p->parent()->kind())
1273 {
1274 case DocNode::Kind_Section:
1275 case DocNode::Kind_Internal:
1276 case DocNode::Kind_HtmlListItem:
1277 case DocNode::Kind_HtmlDescData:
1278 case DocNode::Kind_HtmlCell:
1279 case DocNode::Kind_SimpleListItem:
1280 case DocNode::Kind_AutoListItem:
1281 case DocNode::Kind_SimpleSect:
1282 case DocNode::Kind_XRefItem:
1283 case DocNode::Kind_Copy:
1284 case DocNode::Kind_HtmlBlockQuote:
1285 case DocNode::Kind_ParBlock:
1286 needsTag = TRUE;
1287 break;
1288 case DocNode::Kind_Root:
1289 needsTag = !((DocRoot*)p->parent())->singleLine();
1290 break;
1291 default:
1292 needsTag = FALSE;
1293 }
1294 }
1295
1296 // if the first element of a paragraph is something that should be outside of
1297 // the paragraph (<ul>,<dl>,<table>,..) then that will already started the
1298 // paragraph and we don't need to do it here
1299 size_t nodeIndex = 0;
1300 if (p && nodeIndex<p->children().size())
1301 {
1302 while (nodeIndex<p->children().size() && isInvisibleNode(p->children().at(nodeIndex).get()))
1303 {
1304 nodeIndex++;
1305 }
1306 if (nodeIndex<p->children().size())
1307 {
1308 const DocNode *n = p->children().at(nodeIndex).get();
1309 if (mustBeOutsideParagraph(n))
1310 {
1311 needsTag = FALSE;
1312 }
1313 }
1314 }
1315
1316 // check if this paragraph is the first or last or intermediate child of a <li> or <dd>.
1317 // this allows us to mark the tag with a special class so we can
1318 // fix the otherwise ugly spacing.
1319 int t;
1320 bool isFirst;
1321 bool isLast;
1322 t = getParagraphContext(p,isFirst,isLast);
1323 //printf("startPara first=%d last=%d\n",isFirst,isLast);
1324 if (isFirst && isLast) needsTag=FALSE;
1325
1326 //printf(" needsTag=%d\n",needsTag);
1327 // write the paragraph tag (if needed)
1328 if (needsTag)
1329 {
1330 if (strlen(contexts[t]))
1331 m_t << "<p class=\"" << contexts[t] << "\"" << htmlAttribsToString(p->attribs()) << ">";
1332 else
1333 m_t << "<p " << htmlAttribsToString(p->attribs()) << ">";
1334 }
1335 }
1336
visitPost(DocPara * p)1337 void HtmlDocVisitor::visitPost(DocPara *p)
1338 {
1339
1340 //printf("DocPara::visitPost: parent of kind %d ",
1341 // p->parent() ? p->parent()->kind() : -1);
1342
1343 bool needsTag = FALSE;
1344 if (p->parent())
1345 {
1346 switch (p->parent()->kind())
1347 {
1348 case DocNode::Kind_Section:
1349 case DocNode::Kind_Internal:
1350 case DocNode::Kind_HtmlListItem:
1351 case DocNode::Kind_HtmlDescData:
1352 case DocNode::Kind_HtmlCell:
1353 case DocNode::Kind_SimpleListItem:
1354 case DocNode::Kind_AutoListItem:
1355 case DocNode::Kind_SimpleSect:
1356 case DocNode::Kind_XRefItem:
1357 case DocNode::Kind_Copy:
1358 case DocNode::Kind_HtmlBlockQuote:
1359 case DocNode::Kind_ParBlock:
1360 needsTag = TRUE;
1361 break;
1362 case DocNode::Kind_Root:
1363 needsTag = !((DocRoot*)p->parent())->singleLine();
1364 break;
1365 default:
1366 needsTag = FALSE;
1367 }
1368 }
1369
1370 // if the last element of a paragraph is something that should be outside of
1371 // the paragraph (<ul>,<dl>,<table>) then that will already have ended the
1372 // paragraph and we don't need to do it here
1373 if (!p->children().empty())
1374 {
1375 int nodeIndex = static_cast<int>(p->children().size()-1);
1376 while (nodeIndex>=0 && isInvisibleNode(p->children().at(nodeIndex).get()))
1377 {
1378 nodeIndex--;
1379 }
1380 if (nodeIndex>=0)
1381 {
1382 const DocNode *n = p->children().at(nodeIndex).get();
1383 if (mustBeOutsideParagraph(n))
1384 {
1385 needsTag = FALSE;
1386 }
1387 }
1388 }
1389
1390 bool isFirst;
1391 bool isLast;
1392 getParagraphContext(p,isFirst,isLast);
1393 //printf("endPara first=%d last=%d\n",isFirst,isLast);
1394 if (isFirst && isLast) needsTag=FALSE;
1395
1396 //printf("DocPara::visitPost needsTag=%d\n",needsTag);
1397
1398 if (needsTag) m_t << "</p>\n";
1399
1400 }
1401
visitPre(DocRoot *)1402 void HtmlDocVisitor::visitPre(DocRoot *)
1403 {
1404 }
1405
visitPost(DocRoot *)1406 void HtmlDocVisitor::visitPost(DocRoot *)
1407 {
1408 }
1409
visitPre(DocSimpleSect * s)1410 void HtmlDocVisitor::visitPre(DocSimpleSect *s)
1411 {
1412 if (m_hide) return;
1413 forceEndParagraph(s);
1414 m_t << "<dl class=\"section " << s->typeString() << "\"><dt>";
1415 switch(s->type())
1416 {
1417 case DocSimpleSect::See:
1418 m_t << theTranslator->trSeeAlso(); break;
1419 case DocSimpleSect::Return:
1420 m_t << theTranslator->trReturns(); break;
1421 case DocSimpleSect::Author:
1422 m_t << theTranslator->trAuthor(TRUE,TRUE); break;
1423 case DocSimpleSect::Authors:
1424 m_t << theTranslator->trAuthor(TRUE,FALSE); break;
1425 case DocSimpleSect::Version:
1426 m_t << theTranslator->trVersion(); break;
1427 case DocSimpleSect::Since:
1428 m_t << theTranslator->trSince(); break;
1429 case DocSimpleSect::Date:
1430 m_t << theTranslator->trDate(); break;
1431 case DocSimpleSect::Note:
1432 m_t << theTranslator->trNote(); break;
1433 case DocSimpleSect::Warning:
1434 m_t << theTranslator->trWarning(); break;
1435 case DocSimpleSect::Pre:
1436 m_t << theTranslator->trPrecondition(); break;
1437 case DocSimpleSect::Post:
1438 m_t << theTranslator->trPostcondition(); break;
1439 case DocSimpleSect::Copyright:
1440 m_t << theTranslator->trCopyright(); break;
1441 case DocSimpleSect::Invar:
1442 m_t << theTranslator->trInvariant(); break;
1443 case DocSimpleSect::Remark:
1444 m_t << theTranslator->trRemarks(); break;
1445 case DocSimpleSect::Attention:
1446 m_t << theTranslator->trAttention(); break;
1447 case DocSimpleSect::User: break;
1448 case DocSimpleSect::Rcs: break;
1449 case DocSimpleSect::Unknown: break;
1450 }
1451
1452 // special case 1: user defined title
1453 if (s->type()!=DocSimpleSect::User && s->type()!=DocSimpleSect::Rcs)
1454 {
1455 m_t << "</dt><dd>";
1456 }
1457 }
1458
visitPost(DocSimpleSect * s)1459 void HtmlDocVisitor::visitPost(DocSimpleSect *s)
1460 {
1461 if (m_hide) return;
1462 m_t << "</dd></dl>\n";
1463 forceStartParagraph(s);
1464 }
1465
visitPre(DocTitle *)1466 void HtmlDocVisitor::visitPre(DocTitle *)
1467 {
1468 }
1469
visitPost(DocTitle *)1470 void HtmlDocVisitor::visitPost(DocTitle *)
1471 {
1472 if (m_hide) return;
1473 m_t << "</dt><dd>";
1474 }
1475
visitPre(DocSimpleList * sl)1476 void HtmlDocVisitor::visitPre(DocSimpleList *sl)
1477 {
1478 if (m_hide) return;
1479 forceEndParagraph(sl);
1480 m_t << "<ul>";
1481 if (!sl->isPreformatted()) m_t << "\n";
1482
1483 }
1484
visitPost(DocSimpleList * sl)1485 void HtmlDocVisitor::visitPost(DocSimpleList *sl)
1486 {
1487 if (m_hide) return;
1488 m_t << "</ul>";
1489 if (!sl->isPreformatted()) m_t << "\n";
1490 forceStartParagraph(sl);
1491 }
1492
visitPre(DocSimpleListItem *)1493 void HtmlDocVisitor::visitPre(DocSimpleListItem *)
1494 {
1495 if (m_hide) return;
1496 m_t << "<li>";
1497 }
1498
visitPost(DocSimpleListItem * li)1499 void HtmlDocVisitor::visitPost(DocSimpleListItem *li)
1500 {
1501 if (m_hide) return;
1502 m_t << "</li>";
1503 if (!li->isPreformatted()) m_t << "\n";
1504 }
1505
visitPre(DocSection * s)1506 void HtmlDocVisitor::visitPre(DocSection *s)
1507 {
1508 if (m_hide) return;
1509 forceEndParagraph(s);
1510 m_t << "<h" << s->level() << ">";
1511 m_t << "<a class=\"anchor\" id=\"" << s->anchor();
1512 m_t << "\"></a>\n";
1513 filter(convertCharEntitiesToUTF8(s->title()));
1514 m_t << "</h" << s->level() << ">\n";
1515 }
1516
visitPost(DocSection * s)1517 void HtmlDocVisitor::visitPost(DocSection *s)
1518 {
1519 forceStartParagraph(s);
1520 }
1521
visitPre(DocHtmlList * s)1522 void HtmlDocVisitor::visitPre(DocHtmlList *s)
1523 {
1524 if (m_hide) return;
1525 forceEndParagraph(s);
1526 if (s->type()==DocHtmlList::Ordered)
1527 {
1528 m_t << "<ol" << htmlAttribsToString(s->attribs());
1529 }
1530 else
1531 {
1532 m_t << "<ul" << htmlAttribsToString(s->attribs());
1533 }
1534 m_t << ">\n";
1535 }
1536
visitPost(DocHtmlList * s)1537 void HtmlDocVisitor::visitPost(DocHtmlList *s)
1538 {
1539 if (m_hide) return;
1540 if (s->type()==DocHtmlList::Ordered)
1541 {
1542 m_t << "</ol>";
1543 }
1544 else
1545 {
1546 m_t << "</ul>";
1547 }
1548 if (!s->isPreformatted()) m_t << "\n";
1549 forceStartParagraph(s);
1550 }
1551
visitPre(DocHtmlListItem * i)1552 void HtmlDocVisitor::visitPre(DocHtmlListItem *i)
1553 {
1554 if (m_hide) return;
1555 m_t << "<li" << htmlAttribsToString(i->attribs()) << ">";
1556 if (!i->isPreformatted()) m_t << "\n";
1557 }
1558
visitPost(DocHtmlListItem *)1559 void HtmlDocVisitor::visitPost(DocHtmlListItem *)
1560 {
1561 if (m_hide) return;
1562 m_t << "</li>\n";
1563 }
1564
visitPre(DocHtmlDescList * dl)1565 void HtmlDocVisitor::visitPre(DocHtmlDescList *dl)
1566 {
1567 if (m_hide) return;
1568 forceEndParagraph(dl);
1569 m_t << "<dl" << htmlAttribsToString(dl->attribs()) << ">\n";
1570 }
1571
visitPost(DocHtmlDescList * dl)1572 void HtmlDocVisitor::visitPost(DocHtmlDescList *dl)
1573 {
1574 if (m_hide) return;
1575 m_t << "</dl>\n";
1576 forceStartParagraph(dl);
1577 }
1578
visitPre(DocHtmlDescTitle * dt)1579 void HtmlDocVisitor::visitPre(DocHtmlDescTitle *dt)
1580 {
1581 if (m_hide) return;
1582 m_t << "<dt" << htmlAttribsToString(dt->attribs()) << ">";
1583 }
1584
visitPost(DocHtmlDescTitle *)1585 void HtmlDocVisitor::visitPost(DocHtmlDescTitle *)
1586 {
1587 if (m_hide) return;
1588 m_t << "</dt>\n";
1589 }
1590
visitPre(DocHtmlDescData * dd)1591 void HtmlDocVisitor::visitPre(DocHtmlDescData *dd)
1592 {
1593 if (m_hide) return;
1594 m_t << "<dd" << htmlAttribsToString(dd->attribs()) << ">";
1595 }
1596
visitPost(DocHtmlDescData *)1597 void HtmlDocVisitor::visitPost(DocHtmlDescData *)
1598 {
1599 if (m_hide) return;
1600 m_t << "</dd>\n";
1601 }
1602
visitPre(DocHtmlTable * t)1603 void HtmlDocVisitor::visitPre(DocHtmlTable *t)
1604 {
1605 if (m_hide) return;
1606
1607 forceEndParagraph(t);
1608
1609 if (t->hasCaption())
1610 {
1611 QCString anc = t->caption()->anchor();
1612 if (!anc.isEmpty())
1613 {
1614 m_t << "<a class=\"anchor\" id=\"" << anc << "\"></a>\n";
1615 }
1616 }
1617
1618 QCString attrs = htmlAttribsToString(t->attribs());
1619 if (attrs.isEmpty())
1620 {
1621 m_t << "<table class=\"doxtable\">\n";
1622 }
1623 else
1624 {
1625 m_t << "<table" << htmlAttribsToString(t->attribs()) << ">\n";
1626 }
1627 }
1628
visitPost(DocHtmlTable * t)1629 void HtmlDocVisitor::visitPost(DocHtmlTable *t)
1630 {
1631 if (m_hide) return;
1632 m_t << "</table>\n";
1633 forceStartParagraph(t);
1634 }
1635
visitPre(DocHtmlRow * tr)1636 void HtmlDocVisitor::visitPre(DocHtmlRow *tr)
1637 {
1638 if (m_hide) return;
1639 m_t << "<tr" << htmlAttribsToString(tr->attribs()) << ">\n";
1640 }
1641
visitPost(DocHtmlRow *)1642 void HtmlDocVisitor::visitPost(DocHtmlRow *)
1643 {
1644 if (m_hide) return;
1645 m_t << "</tr>\n";
1646 }
1647
visitPre(DocHtmlCell * c)1648 void HtmlDocVisitor::visitPre(DocHtmlCell *c)
1649 {
1650 if (m_hide) return;
1651 if (c->isHeading())
1652 {
1653 m_t << "<th" << htmlAttribsToString(c->attribs()) << ">";
1654 }
1655 else
1656 {
1657 m_t << "<td" << htmlAttribsToString(c->attribs()) << ">";
1658 }
1659 }
1660
visitPost(DocHtmlCell * c)1661 void HtmlDocVisitor::visitPost(DocHtmlCell *c)
1662 {
1663 if (m_hide) return;
1664 if (c->isHeading()) m_t << "</th>"; else m_t << "</td>";
1665 }
1666
visitPre(DocHtmlCaption * c)1667 void HtmlDocVisitor::visitPre(DocHtmlCaption *c)
1668 {
1669 if (m_hide) return;
1670 m_t << "<caption" << htmlAttribsToString(c->attribs()) << ">";
1671 }
1672
visitPost(DocHtmlCaption *)1673 void HtmlDocVisitor::visitPost(DocHtmlCaption *)
1674 {
1675 if (m_hide) return;
1676 m_t << "</caption>\n";
1677 }
1678
visitPre(DocInternal *)1679 void HtmlDocVisitor::visitPre(DocInternal *)
1680 {
1681 if (m_hide) return;
1682 //forceEndParagraph(i);
1683 //m_t << "<p><b>" << theTranslator->trForInternalUseOnly() << "</b></p>\n";
1684 }
1685
visitPost(DocInternal *)1686 void HtmlDocVisitor::visitPost(DocInternal *)
1687 {
1688 if (m_hide) return;
1689 //forceStartParagraph(i);
1690 }
1691
visitPre(DocHRef * href)1692 void HtmlDocVisitor::visitPre(DocHRef *href)
1693 {
1694 if (m_hide) return;
1695 if (href->url().left(7)=="mailto:")
1696 {
1697 writeObfuscatedMailAddress(href->url().mid(7));
1698 }
1699 else
1700 {
1701 QCString url = correctURL(href->url(),href->relPath());
1702 m_t << "<a href=\"" << convertToHtml(url) << "\""
1703 << htmlAttribsToString(href->attribs()) << ">";
1704 }
1705 }
1706
visitPost(DocHRef *)1707 void HtmlDocVisitor::visitPost(DocHRef *)
1708 {
1709 if (m_hide) return;
1710 m_t << "</a>";
1711 }
1712
visitPre(DocHtmlHeader * header)1713 void HtmlDocVisitor::visitPre(DocHtmlHeader *header)
1714 {
1715 if (m_hide) return;
1716 forceEndParagraph(header);
1717 m_t << "<h" << header->level() << htmlAttribsToString(header->attribs()) << ">";
1718 }
1719
visitPost(DocHtmlHeader * header)1720 void HtmlDocVisitor::visitPost(DocHtmlHeader *header)
1721 {
1722 if (m_hide) return;
1723 m_t << "</h" << header->level() << ">\n";
1724 forceStartParagraph(header);
1725 }
1726
visitPre(DocImage * img)1727 void HtmlDocVisitor::visitPre(DocImage *img)
1728 {
1729 if (img->type()==DocImage::Html)
1730 {
1731 bool inlineImage = img->isInlineImage();
1732 bool typeSVG = img->isSVG();
1733 QCString url = img->url();
1734
1735 if (!inlineImage)
1736 {
1737 forceEndParagraph(img);
1738 }
1739 if (m_hide) return;
1740 QCString baseName=img->name();
1741 int i;
1742 if ((i=baseName.findRev('/'))!=-1 || (i=baseName.findRev('\\'))!=-1)
1743 {
1744 baseName=baseName.right(baseName.length()-i-1);
1745 }
1746 if (!inlineImage) m_t << "<div class=\"image\">\n";
1747 QCString sizeAttribs;
1748 if (!img->width().isEmpty())
1749 {
1750 sizeAttribs+=" width=\""+img->width()+"\"";
1751 }
1752 if (!img->height().isEmpty()) // link to local file
1753 {
1754 sizeAttribs+=" height=\""+img->height()+"\"";
1755 }
1756 // 16 cases: url.isEmpty() | typeSVG | inlineImage | img->hasCaption()
1757
1758 HtmlAttribList extraAttribs;
1759 if (typeSVG)
1760 {
1761 HtmlAttrib opt;
1762 opt.name = "style";
1763 opt.value = "pointer-events: none;";
1764 extraAttribs.push_back(opt);
1765 }
1766 QCString alt;
1767 mergeHtmlAttributes(img->attribs(),extraAttribs);
1768 QCString attrs = htmlAttribsToString(extraAttribs,&alt);
1769 QCString src;
1770 if (url.isEmpty())
1771 {
1772 src = img->relPath()+img->name();
1773 }
1774 else
1775 {
1776 src = correctURL(url,img->relPath());
1777 }
1778 if (typeSVG && !inlineImage)
1779 {
1780 m_t << "<object type=\"image/svg+xml\" data=\"" << convertToHtml(src)
1781 << "\"" << sizeAttribs << attrs;
1782 if (inlineImage)
1783 {
1784 // skip closing tag
1785 }
1786 else
1787 {
1788 m_t << ">" << alt << "</object>\n";
1789 }
1790 }
1791 else
1792 {
1793 m_t << "<img src=\"" << convertToHtml(src) << "\" alt=\"" << alt << "\"" << sizeAttribs << attrs;
1794 if (inlineImage)
1795 {
1796 m_t << " class=\"inline\"";
1797 }
1798 else
1799 {
1800 m_t << "/>\n";
1801 }
1802 }
1803 if (img->hasCaption())
1804 {
1805 if (inlineImage)
1806 {
1807 m_t << " title=\"";
1808 m_insideTitle=true;
1809 }
1810 else
1811 {
1812 m_t << "<div class=\"caption\">\n";
1813 }
1814 }
1815 else if (inlineImage)
1816 {
1817 m_t << "/>";
1818 }
1819 }
1820 else // other format -> skip
1821 {
1822 pushHidden(m_hide);
1823 m_hide=TRUE;
1824 }
1825 }
1826
visitPost(DocImage * img)1827 void HtmlDocVisitor::visitPost(DocImage *img)
1828 {
1829 if (img->type()==DocImage::Html)
1830 {
1831 if (m_hide) return;
1832 bool inlineImage = img->isInlineImage();
1833 if (img->hasCaption())
1834 {
1835 if (inlineImage)
1836 {
1837 m_t << "\"/>";
1838 m_insideTitle=false;
1839 }
1840 else // end <div class="caption">
1841 {
1842 m_t << "</div>";
1843 }
1844 }
1845 if (!inlineImage) // end <div class="image">
1846 {
1847 m_t << "</div>\n";
1848 forceStartParagraph(img);
1849 }
1850 }
1851 else // other format
1852 {
1853 m_hide = popHidden();
1854 }
1855 }
1856
visitPre(DocDotFile * df)1857 void HtmlDocVisitor::visitPre(DocDotFile *df)
1858 {
1859 if (m_hide) return;
1860 if (!Config_getBool(DOT_CLEANUP)) copyFile(df->file(),Config_getString(HTML_OUTPUT)+"/"+stripPath(df->file()));
1861 m_t << "<div class=\"dotgraph\">\n";
1862 writeDotFile(df->file(),df->relPath(),df->context(),df->srcFile(),df->srcLine());
1863 if (df->hasCaption())
1864 {
1865 m_t << "<div class=\"caption\">\n";
1866 }
1867 }
1868
visitPost(DocDotFile * df)1869 void HtmlDocVisitor::visitPost(DocDotFile *df)
1870 {
1871 if (m_hide) return;
1872 if (df->hasCaption())
1873 {
1874 m_t << "</div>\n";
1875 }
1876 m_t << "</div>\n";
1877 }
1878
visitPre(DocMscFile * df)1879 void HtmlDocVisitor::visitPre(DocMscFile *df)
1880 {
1881 if (m_hide) return;
1882 if (!Config_getBool(DOT_CLEANUP)) copyFile(df->file(),Config_getString(HTML_OUTPUT)+"/"+stripPath(df->file()));
1883 m_t << "<div class=\"mscgraph\">\n";
1884 writeMscFile(df->file(),df->relPath(),df->context(),df->srcFile(),df->srcLine());
1885 if (df->hasCaption())
1886 {
1887 m_t << "<div class=\"caption\">\n";
1888 }
1889 }
visitPost(DocMscFile * df)1890 void HtmlDocVisitor::visitPost(DocMscFile *df)
1891 {
1892 if (m_hide) return;
1893 if (df->hasCaption())
1894 {
1895 m_t << "</div>\n";
1896 }
1897 m_t << "</div>\n";
1898 }
1899
visitPre(DocDiaFile * df)1900 void HtmlDocVisitor::visitPre(DocDiaFile *df)
1901 {
1902 if (m_hide) return;
1903 if (!Config_getBool(DOT_CLEANUP)) copyFile(df->file(),Config_getString(HTML_OUTPUT)+"/"+stripPath(df->file()));
1904 m_t << "<div class=\"diagraph\">\n";
1905 writeDiaFile(df->file(),df->relPath(),df->context(),df->srcFile(),df->srcLine());
1906 if (df->hasCaption())
1907 {
1908 m_t << "<div class=\"caption\">\n";
1909 }
1910 }
visitPost(DocDiaFile * df)1911 void HtmlDocVisitor::visitPost(DocDiaFile *df)
1912 {
1913 if (m_hide) return;
1914 if (df->hasCaption())
1915 {
1916 m_t << "</div>\n";
1917 }
1918 m_t << "</div>\n";
1919 }
1920
visitPre(DocLink * lnk)1921 void HtmlDocVisitor::visitPre(DocLink *lnk)
1922 {
1923 if (m_hide) return;
1924 startLink(lnk->ref(),lnk->file(),lnk->relPath(),lnk->anchor());
1925 }
1926
visitPost(DocLink *)1927 void HtmlDocVisitor::visitPost(DocLink *)
1928 {
1929 if (m_hide) return;
1930 endLink();
1931 }
1932
visitPre(DocRef * ref)1933 void HtmlDocVisitor::visitPre(DocRef *ref)
1934 {
1935 if (m_hide) return;
1936 if (!ref->file().isEmpty())
1937 {
1938 // when ref->isSubPage()==TRUE we use ref->file() for HTML and
1939 // ref->anchor() for LaTeX/RTF
1940 startLink(ref->ref(),ref->file(),ref->relPath(),ref->isSubPage() ? QCString() : ref->anchor());
1941 }
1942 if (!ref->hasLinkText()) filter(ref->targetTitle());
1943 }
1944
visitPost(DocRef * ref)1945 void HtmlDocVisitor::visitPost(DocRef *ref)
1946 {
1947 if (m_hide) return;
1948 if (!ref->file().isEmpty()) endLink();
1949 //m_t << " ";
1950 }
1951
visitPre(DocSecRefItem * ref)1952 void HtmlDocVisitor::visitPre(DocSecRefItem *ref)
1953 {
1954 if (m_hide) return;
1955 if (!ref->file().isEmpty())
1956 {
1957 m_t << "<li>";
1958 startLink(ref->ref(),ref->file(),ref->relPath(),ref->isSubPage() ? QCString() : ref->anchor());
1959 }
1960 }
1961
visitPost(DocSecRefItem * ref)1962 void HtmlDocVisitor::visitPost(DocSecRefItem *ref)
1963 {
1964 if (m_hide) return;
1965 if (!ref->file().isEmpty())
1966 {
1967 endLink();
1968 m_t << "</li>\n";
1969 }
1970 }
1971
visitPre(DocSecRefList * s)1972 void HtmlDocVisitor::visitPre(DocSecRefList *s)
1973 {
1974 if (m_hide) return;
1975 forceEndParagraph(s);
1976 m_t << "<div>\n";
1977 m_t << "<ul class=\"multicol\">\n";
1978 }
1979
visitPost(DocSecRefList * s)1980 void HtmlDocVisitor::visitPost(DocSecRefList *s)
1981 {
1982 if (m_hide) return;
1983 m_t << "</ul>\n";
1984 m_t << "</div>\n";
1985 forceStartParagraph(s);
1986 }
1987
visitPre(DocParamSect * s)1988 void HtmlDocVisitor::visitPre(DocParamSect *s)
1989 {
1990 if (m_hide) return;
1991 forceEndParagraph(s);
1992 QCString className;
1993 QCString heading;
1994 switch(s->type())
1995 {
1996 case DocParamSect::Param:
1997 heading=theTranslator->trParameters();
1998 className="params";
1999 break;
2000 case DocParamSect::RetVal:
2001 heading=theTranslator->trReturnValues();
2002 className="retval";
2003 break;
2004 case DocParamSect::Exception:
2005 heading=theTranslator->trExceptions();
2006 className="exception";
2007 break;
2008 case DocParamSect::TemplateParam:
2009 heading=theTranslator->trTemplateParameters();
2010 className="tparams";
2011 break;
2012 default:
2013 ASSERT(0);
2014 }
2015 m_t << "<dl class=\"" << className << "\"><dt>";
2016 m_t << heading;
2017 m_t << "</dt><dd>\n";
2018 m_t << " <table class=\"" << className << "\">\n";
2019 }
2020
visitPost(DocParamSect * s)2021 void HtmlDocVisitor::visitPost(DocParamSect *s)
2022 {
2023 if (m_hide) return;
2024 m_t << " </table>\n";
2025 m_t << " </dd>\n";
2026 m_t << "</dl>\n";
2027 forceStartParagraph(s);
2028 }
2029
visitPre(DocParamList * pl)2030 void HtmlDocVisitor::visitPre(DocParamList *pl)
2031 {
2032 //printf("DocParamList::visitPre\n");
2033 if (m_hide) return;
2034 m_t << " <tr>";
2035 DocParamSect *sect = 0;
2036 if (pl->parent()->kind()==DocNode::Kind_ParamSect)
2037 {
2038 sect=(DocParamSect*)pl->parent();
2039 }
2040 if (sect && sect->hasInOutSpecifier())
2041 {
2042 m_t << "<td class=\"paramdir\">";
2043 if (pl->direction()!=DocParamSect::Unspecified)
2044 {
2045 m_t << "[";
2046 if (pl->direction()==DocParamSect::In)
2047 {
2048 m_t << "in";
2049 }
2050 else if (pl->direction()==DocParamSect::Out)
2051 {
2052 m_t << "out";
2053 }
2054 else if (pl->direction()==DocParamSect::InOut)
2055 {
2056 m_t << "in,out";
2057 }
2058 m_t << "]";
2059 }
2060 m_t << "</td>";
2061 }
2062 if (sect && sect->hasTypeSpecifier())
2063 {
2064 m_t << "<td class=\"paramtype\">";
2065 for (const auto &type : pl->paramTypes())
2066 {
2067 if (type->kind()==DocNode::Kind_Word)
2068 {
2069 visit((DocWord*)type.get());
2070 }
2071 else if (type->kind()==DocNode::Kind_LinkedWord)
2072 {
2073 visit((DocLinkedWord*)type.get());
2074 }
2075 else if (type->kind()==DocNode::Kind_Sep)
2076 {
2077 m_t << " " << ((DocSeparator *)type.get())->chars() << " ";
2078 }
2079 }
2080 m_t << "</td>";
2081 }
2082 m_t << "<td class=\"paramname\">";
2083 bool first=TRUE;
2084 for (const auto ¶m : pl->parameters())
2085 {
2086 if (!first) m_t << ","; else first=FALSE;
2087 if (param->kind()==DocNode::Kind_Word)
2088 {
2089 visit((DocWord*)param.get());
2090 }
2091 else if (param->kind()==DocNode::Kind_LinkedWord)
2092 {
2093 visit((DocLinkedWord*)param.get());
2094 }
2095 }
2096 m_t << "</td><td>";
2097 }
2098
visitPost(DocParamList *)2099 void HtmlDocVisitor::visitPost(DocParamList *)
2100 {
2101 //printf("DocParamList::visitPost\n");
2102 if (m_hide) return;
2103 m_t << "</td></tr>\n";
2104 }
2105
visitPre(DocXRefItem * x)2106 void HtmlDocVisitor::visitPre(DocXRefItem *x)
2107 {
2108 if (m_hide) return;
2109 if (x->title().isEmpty()) return;
2110
2111 forceEndParagraph(x);
2112 bool anonymousEnum = x->file()=="@";
2113 if (!anonymousEnum)
2114 {
2115 m_t << "<dl class=\"" << x->key() << "\"><dt><b><a class=\"el\" href=\""
2116 << x->relPath() << addHtmlExtensionIfMissing(x->file())
2117 << "#" << x->anchor() << "\">";
2118 }
2119 else
2120 {
2121 m_t << "<dl class=\"" << x->key() << "\"><dt><b>";
2122 }
2123 filter(x->title());
2124 m_t << ":";
2125 if (!anonymousEnum) m_t << "</a>";
2126 m_t << "</b></dt><dd>";
2127 }
2128
visitPost(DocXRefItem * x)2129 void HtmlDocVisitor::visitPost(DocXRefItem *x)
2130 {
2131 if (m_hide) return;
2132 if (x->title().isEmpty()) return;
2133 m_t << "</dd></dl>\n";
2134 forceStartParagraph(x);
2135 }
2136
visitPre(DocInternalRef * ref)2137 void HtmlDocVisitor::visitPre(DocInternalRef *ref)
2138 {
2139 if (m_hide) return;
2140 startLink(QCString(),ref->file(),ref->relPath(),ref->anchor());
2141 }
2142
visitPost(DocInternalRef *)2143 void HtmlDocVisitor::visitPost(DocInternalRef *)
2144 {
2145 if (m_hide) return;
2146 endLink();
2147 m_t << " ";
2148 }
2149
visitPre(DocText *)2150 void HtmlDocVisitor::visitPre(DocText *)
2151 {
2152 }
2153
visitPost(DocText *)2154 void HtmlDocVisitor::visitPost(DocText *)
2155 {
2156 }
2157
visitPre(DocHtmlBlockQuote * b)2158 void HtmlDocVisitor::visitPre(DocHtmlBlockQuote *b)
2159 {
2160 if (m_hide) return;
2161 forceEndParagraph(b);
2162 QCString attrs = htmlAttribsToString(b->attribs());
2163 m_t << "<blockquote class=\"doxtable\"" << htmlAttribsToString(b->attribs()) << ">\n";
2164 }
2165
visitPost(DocHtmlBlockQuote * b)2166 void HtmlDocVisitor::visitPost(DocHtmlBlockQuote *b)
2167 {
2168 if (m_hide) return;
2169 m_t << "</blockquote>\n";
2170 forceStartParagraph(b);
2171 }
2172
visitPre(DocVhdlFlow * vf)2173 void HtmlDocVisitor::visitPre(DocVhdlFlow *vf)
2174 {
2175 if (m_hide) return;
2176 if (VhdlDocGen::getFlowMember()) // use VHDL flow chart creator
2177 {
2178 forceEndParagraph(vf);
2179 QCString fname=FlowChart::convertNameToFileName();
2180 m_t << "<p>";
2181 m_t << "flowchart: " ; // TODO: translate me
2182 m_t << "<a href=\"";
2183 m_t << fname;
2184 m_t << ".svg\">";
2185 m_t << VhdlDocGen::getFlowMember()->name();
2186 m_t << "</a>";
2187 if (vf->hasCaption())
2188 {
2189 m_t << "<br />";
2190 }
2191 }
2192 }
2193
visitPost(DocVhdlFlow * vf)2194 void HtmlDocVisitor::visitPost(DocVhdlFlow *vf)
2195 {
2196 if (m_hide) return;
2197 if (VhdlDocGen::getFlowMember()) // use VHDL flow chart creator
2198 {
2199 m_t << "</p>";
2200 forceStartParagraph(vf);
2201 }
2202 }
2203
visitPre(DocParBlock *)2204 void HtmlDocVisitor::visitPre(DocParBlock *)
2205 {
2206 if (m_hide) return;
2207 }
2208
visitPost(DocParBlock *)2209 void HtmlDocVisitor::visitPost(DocParBlock *)
2210 {
2211 if (m_hide) return;
2212 }
2213
2214
2215
filter(const QCString & str,const bool retainNewline)2216 void HtmlDocVisitor::filter(const QCString &str, const bool retainNewline)
2217 {
2218 if (str.isEmpty()) return;
2219 const char *p=str.data();
2220 char c;
2221 while (*p)
2222 {
2223 c=*p++;
2224 switch(c)
2225 {
2226 case '\n': if(retainNewline) m_t << "<br/>"; m_t << c; break;
2227 case '<': m_t << "<"; break;
2228 case '>': m_t << ">"; break;
2229 case '&': m_t << "&"; break;
2230 case '\\': if ((*p == '(') || (*p == ')'))
2231 m_t << "\\‍" << *p++;
2232 else
2233 m_t << c;
2234 break;
2235 default:
2236 {
2237 uchar uc = static_cast<uchar>(c);
2238 if (uc<32 && !isspace(c)) // non-printable control characters
2239 {
2240 m_t << "$" << hex[uc>>4] << hex[uc&0xF] << ";";
2241 }
2242 else
2243 {
2244 m_t << c;
2245 }
2246 }
2247 break;
2248 }
2249 }
2250 }
2251
2252 /// Escape basic entities to produce a valid CDATA attribute value,
2253 /// assume that the outer quoting will be using the double quote "
filterQuotedCdataAttr(const QCString & str)2254 void HtmlDocVisitor::filterQuotedCdataAttr(const QCString &str)
2255 {
2256 if (str.isEmpty()) return;
2257 const char *p=str.data();
2258 char c;
2259 while (*p)
2260 {
2261 c=*p++;
2262 switch(c)
2263 {
2264 case '&': m_t << "&"; break;
2265 case '"': m_t << """; break;
2266 case '<': m_t << "<"; break;
2267 case '>': m_t << ">"; break;
2268 case '\\': if ((*p == '(') || (*p == ')'))
2269 m_t << "\\‍" << *p++;
2270 else
2271 m_t << c;
2272 break;
2273 default:
2274 {
2275 uchar uc = static_cast<uchar>(c);
2276 if (uc<32 && !isspace(c)) // non-printable control characters
2277 {
2278 m_t << "$" << hex[uc>>4] << hex[uc&0xF] << ";";
2279 }
2280 else
2281 {
2282 m_t << c;
2283 }
2284 }
2285 break;
2286 }
2287 }
2288 }
2289
startLink(const QCString & ref,const QCString & file,const QCString & relPath,const QCString & anchor,const QCString & tooltip)2290 void HtmlDocVisitor::startLink(const QCString &ref,const QCString &file,
2291 const QCString &relPath,const QCString &anchor,
2292 const QCString &tooltip)
2293 {
2294 //printf("HtmlDocVisitor: file=%s anchor=%s\n",qPrint(file),qPrint(anchor));
2295 if (!ref.isEmpty()) // link to entity imported via tag file
2296 {
2297 m_t << "<a class=\"elRef\" ";
2298 m_t << externalLinkTarget();
2299 }
2300 else // local link
2301 {
2302 m_t << "<a class=\"el\" ";
2303 }
2304 m_t << "href=\"";
2305 m_t << externalRef(relPath,ref,TRUE);
2306 if (!file.isEmpty())
2307 {
2308 m_t << addHtmlExtensionIfMissing(file);
2309 }
2310 if (!anchor.isEmpty()) m_t << "#" << anchor;
2311 m_t << "\"";
2312 if (!tooltip.isEmpty()) m_t << " title=\"" << convertToHtml(tooltip) << "\"";
2313 m_t << ">";
2314 }
2315
endLink()2316 void HtmlDocVisitor::endLink()
2317 {
2318 m_t << "</a>";
2319 }
2320
writeDotFile(const QCString & fn,const QCString & relPath,const QCString & context,const QCString & srcFile,int srcLine)2321 void HtmlDocVisitor::writeDotFile(const QCString &fn,const QCString &relPath,
2322 const QCString &context,const QCString &srcFile,int srcLine)
2323 {
2324 QCString baseName=fn;
2325 int i;
2326 if ((i=baseName.findRev('/'))!=-1)
2327 {
2328 baseName=baseName.right(baseName.length()-i-1);
2329 }
2330 if ((i=baseName.find('.'))!=-1) // strip extension
2331 {
2332 baseName=baseName.left(i);
2333 }
2334 baseName.prepend("dot_");
2335 QCString outDir = Config_getString(HTML_OUTPUT);
2336 writeDotGraphFromFile(fn,outDir,baseName,GOF_BITMAP,srcFile,srcLine);
2337 writeDotImageMapFromFile(m_t,fn,outDir,relPath,baseName,context,-1,srcFile,srcLine);
2338 }
2339
writeMscFile(const QCString & fileName,const QCString & relPath,const QCString & context,const QCString & srcFile,int srcLine)2340 void HtmlDocVisitor::writeMscFile(const QCString &fileName,const QCString &relPath,
2341 const QCString &context,const QCString &srcFile,int srcLine)
2342 {
2343 QCString baseName=fileName;
2344 int i;
2345 if ((i=baseName.findRev('/'))!=-1) // strip path
2346 {
2347 baseName=baseName.right(baseName.length()-i-1);
2348 }
2349 if ((i=baseName.find('.'))!=-1) // strip extension
2350 {
2351 baseName=baseName.left(i);
2352 }
2353 baseName.prepend("msc_");
2354 QCString outDir = Config_getString(HTML_OUTPUT);
2355 QCString imgExt = getDotImageExtension();
2356 MscOutputFormat mscFormat = MSC_BITMAP;
2357 if ("svg" == imgExt)
2358 mscFormat = MSC_SVG;
2359 writeMscGraphFromFile(fileName,outDir,baseName,mscFormat,srcFile,srcLine);
2360 writeMscImageMapFromFile(m_t,fileName,outDir,relPath,baseName,context,mscFormat,srcFile,srcLine);
2361 }
2362
writeDiaFile(const QCString & fileName,const QCString & relPath,const QCString &,const QCString & srcFile,int srcLine)2363 void HtmlDocVisitor::writeDiaFile(const QCString &fileName, const QCString &relPath,
2364 const QCString &,const QCString &srcFile,int srcLine)
2365 {
2366 QCString baseName=fileName;
2367 int i;
2368 if ((i=baseName.findRev('/'))!=-1) // strip path
2369 {
2370 baseName=baseName.right(baseName.length()-i-1);
2371 }
2372 if ((i=baseName.find('.'))!=-1) // strip extension
2373 {
2374 baseName=baseName.left(i);
2375 }
2376 baseName.prepend("dia_");
2377 QCString outDir = Config_getString(HTML_OUTPUT);
2378 writeDiaGraphFromFile(fileName,outDir,baseName,DIA_BITMAP,srcFile,srcLine);
2379
2380 m_t << "<img src=\"" << relPath << baseName << ".png" << "\" />\n";
2381 }
2382
writePlantUMLFile(const QCString & fileName,const QCString & relPath,const QCString &,const QCString & srcFile,int srcLine)2383 void HtmlDocVisitor::writePlantUMLFile(const QCString &fileName, const QCString &relPath,
2384 const QCString &,const QCString &srcFile,int srcLine)
2385 {
2386 QCString baseName=fileName;
2387 int i;
2388 if ((i=baseName.findRev('/'))!=-1) // strip path
2389 {
2390 baseName=baseName.right(baseName.length()-i-1);
2391 }
2392 if ((i=baseName.findRev('.'))!=-1) // strip extension
2393 {
2394 baseName=baseName.left(i);
2395 }
2396 static QCString outDir = Config_getString(HTML_OUTPUT);
2397 QCString imgExt = getDotImageExtension();
2398 if (imgExt=="svg")
2399 {
2400 PlantumlManager::instance().generatePlantUMLOutput(fileName,outDir,PlantumlManager::PUML_SVG);
2401 //m_t << "<iframe scrolling=\"no\" frameborder=\"0\" src=\"" << relPath << baseName << ".svg" << "\" />\n";
2402 //m_t << "<p><b>This browser is not able to show SVG: try Firefox, Chrome, Safari, or Opera instead.</b></p>";
2403 //m_t << "</iframe>\n";
2404 m_t << "<object type=\"image/svg+xml\" data=\"" << relPath << baseName << ".svg\"></object>\n";
2405 }
2406 else
2407 {
2408 PlantumlManager::instance().generatePlantUMLOutput(fileName,outDir,PlantumlManager::PUML_BITMAP);
2409 m_t << "<img src=\"" << relPath << baseName << ".png" << "\" />\n";
2410 }
2411 }
2412
2413 /** Returns TRUE if the child nodes in paragraph \a para until \a nodeIndex
2414 contain a style change node that is still active and that style change is one that
2415 must be located outside of a paragraph, i.e. it is a center, div, or pre tag.
2416 See also bug746162.
2417 */
insideStyleChangeThatIsOutsideParagraph(DocPara * para,int nodeIndex)2418 static bool insideStyleChangeThatIsOutsideParagraph(DocPara *para,int nodeIndex)
2419 {
2420 //printf("insideStyleChangeThatIsOutputParagraph(index=%d)\n",nodeIndex);
2421 int styleMask=0;
2422 bool styleOutsideParagraph=FALSE;
2423 while (nodeIndex>=0 && !styleOutsideParagraph)
2424 {
2425 DocNode *n = para->children().at(nodeIndex).get();
2426 if (n->kind()==DocNode::Kind_StyleChange)
2427 {
2428 DocStyleChange *sc = (DocStyleChange*)n;
2429 if (!sc->enable()) // remember styles that has been closed already
2430 {
2431 styleMask|=(int)sc->style();
2432 }
2433 bool paraStyle = sc->style()==DocStyleChange::Center ||
2434 sc->style()==DocStyleChange::Div ||
2435 sc->style()==DocStyleChange::Preformatted;
2436 //printf("Found style change %s enabled=%d\n",sc->styleString(),sc->enable());
2437 if (sc->enable() && (styleMask&(int)sc->style())==0 && // style change that is still active
2438 paraStyle
2439 )
2440 {
2441 styleOutsideParagraph=TRUE;
2442 }
2443 }
2444 nodeIndex--;
2445 }
2446 return styleOutsideParagraph;
2447 }
2448
2449 /** Used for items found inside a paragraph, which due to XHTML restrictions
2450 * have to be outside of the paragraph. This method will forcefully end
2451 * the current paragraph and forceStartParagraph() will restart it.
2452 */
forceEndParagraph(DocNode * n)2453 void HtmlDocVisitor::forceEndParagraph(DocNode *n)
2454 {
2455 //printf("forceEndParagraph(%p) %d\n",n,n->kind());
2456 if (n->parent() && n->parent()->kind()==DocNode::Kind_Para)
2457 {
2458 DocPara *para = (DocPara*)n->parent();
2459 const DocNodeList &children = para->children();
2460 auto it = std::find_if(children.begin(),children.end(),[n](const auto &np) { return np.get()==n; });
2461 if (it==children.end()) return;
2462 int nodeIndex = static_cast<int>(it - children.begin());
2463 nodeIndex--;
2464 if (nodeIndex<0) return; // first node in paragraph
2465 while (nodeIndex>=0 && isInvisibleNode(children.at(nodeIndex).get()))
2466 {
2467 nodeIndex--;
2468 }
2469 if (nodeIndex<0) return; // first visible node in paragraph
2470 n = children.at(nodeIndex).get();
2471 if (mustBeOutsideParagraph(n)) return; // previous node already outside paragraph context
2472 nodeIndex--;
2473 bool styleOutsideParagraph=insideStyleChangeThatIsOutsideParagraph(para,nodeIndex);
2474 bool isFirst;
2475 bool isLast;
2476 getParagraphContext(para,isFirst,isLast);
2477 //printf("forceEnd first=%d last=%d styleOutsideParagraph=%d\n",isFirst,isLast,styleOutsideParagraph);
2478 if (isFirst && isLast) return;
2479 if (styleOutsideParagraph) return;
2480
2481 m_t << "</p>";
2482 }
2483 }
2484
2485 /** Used for items found inside a paragraph, which due to XHTML restrictions
2486 * have to be outside of the paragraph. This method will forcefully start
2487 * the paragraph, that was previously ended by forceEndParagraph().
2488 */
forceStartParagraph(DocNode * n)2489 void HtmlDocVisitor::forceStartParagraph(DocNode *n)
2490 {
2491 //printf("forceStartParagraph(%p) %d\n",n,n->kind());
2492 if (n->parent() && n->parent()->kind()==DocNode::Kind_Para) // if we are inside a paragraph
2493 {
2494 DocPara *para = (DocPara*)n->parent();
2495 const DocNodeList &children = para->children();
2496 auto it = std::find_if(children.begin(),children.end(),[n](const auto &np) { return np.get()==n; });
2497 if (it==children.end()) return;
2498 int nodeIndex = static_cast<int>(it - children.begin());
2499 int numNodes = static_cast<int>(para->children().size());
2500 bool styleOutsideParagraph=insideStyleChangeThatIsOutsideParagraph(para,nodeIndex);
2501 if (styleOutsideParagraph) return;
2502 nodeIndex++;
2503 if (nodeIndex==numNodes) return; // last node
2504 while (nodeIndex<numNodes && isInvisibleNode(para->children().at(nodeIndex).get()))
2505 {
2506 nodeIndex++;
2507 }
2508 if (nodeIndex<numNodes)
2509 {
2510 n = para->children().at(nodeIndex).get();
2511 if (mustBeOutsideParagraph(n)) return; // next element also outside paragraph
2512 }
2513 else
2514 {
2515 return; // only whitespace at the end!
2516 }
2517
2518 bool needsTag = TRUE;
2519 bool isFirst;
2520 bool isLast;
2521 getParagraphContext(para,isFirst,isLast);
2522 if (isFirst && isLast) needsTag = FALSE;
2523 //printf("forceStart first=%d last=%d needsTag=%d\n",isFirst,isLast,needsTag);
2524
2525 if (needsTag) m_t << "<p>";
2526 }
2527 }
2528
2529