From fa6585b2fc6847c2b242b226b163810c7a6366de Mon Sep 17 00:00:00 2001 From: Dimitri van Heesch Date: Sat, 8 Aug 2015 11:54:20 +0200 Subject: Improved handling of in tables for LaTeX output. --- src/commentscan.l | 61 +++++++++++++++++++++++++++------------ src/docparser.cpp | 57 ++++++++++++++++++++++++++++++++---- src/docparser.h | 23 +++++++++------ src/doctokenizer.l | 26 ++++++++++++++--- src/htmldocvisitor.cpp | 5 ++++ src/latexdocvisitor.cpp | 70 +++++++++++++++++++++++++-------------------- src/latexdocvisitor.h | 3 +- src/latexgen.cpp | 5 +++- src/printdocvisitor.h | 5 ++-- src/section.h | 3 +- templates/latex/doxygen.sty | 5 ++++ 11 files changed, 192 insertions(+), 71 deletions(-) diff --git a/src/commentscan.l b/src/commentscan.l index 406d966..9442ae3 100644 --- a/src/commentscan.l +++ b/src/commentscan.l @@ -834,6 +834,29 @@ static inline void setOutput(OutputContext ctx) } } + +static void addAnchor(const char *anchor) +{ + SectionInfo *si = Doxygen::sectionDict->find(anchor); + if (si) + { + if (si->lineNr != -1) + { + warn(yyFileName,yyLineNr,"multiple use of section label '%s' while adding anchor, (first occurrence: %s, line %d)",anchor,si->fileName.data(),si->lineNr); + } + else + { + warn(yyFileName,yyLineNr,"multiple use of section label '%s' while adding anchor, (first occurrence: %s)",anchor,si->fileName.data()); + } + } + else + { + si = new SectionInfo(yyFileName,yyLineNr,anchor,0,SectionInfo::Anchor,0); + Doxygen::sectionDict->append(anchor,si); + current->anchors->append(si); + } +} + // add a string to the output static inline void addOutput(const char *s) { @@ -905,6 +928,7 @@ IMG [iI][mM][gG] HR [hH][rR] PARA [pP][aA][rR][aA] CODE [cC][oO][dD][eE] +CAPTION [cC][aA][pP][tT][iI][oO][nN] DETAILEDHTML {PRE}|{UL}|{TABLE}|{OL}|{DL}|{P}|[Hh][1-6]|{IMG}|{HR}|{PARA} DETAILEDHTMLOPT {CODE} BN [ \t\n\r] @@ -1053,6 +1077,24 @@ RCSTAG "$"{ID}":"[^\n$]+"$" "" { // end of a brief or detailed description addOutput(yytext); } +"<"{CAPTION}{ATTR}">" { + QCString tag=yytext; + int s=tag.find("id="); + if (s!=-1) // command has id attribute + { + char c=tag[s+3]; + if (c=='\'' || c=='"') // valid start + { + int e=tag.find(c,s+4); + if (e!=-1) // found matching end + { + QCString id=tag.mid(s+4,e-s-4); // extract id + addAnchor(id); + } + } + } + addOutput(yytext); + } "<"{PRE}{ATTR}">" { insidePre=TRUE; addOutput(yytext); @@ -1745,24 +1787,7 @@ RCSTAG "$"{ID}":"[^\n$]+"$" /* ----- handle arguments of the anchor command ------- */ {LABELID} { // found argument - SectionInfo *si = Doxygen::sectionDict->find(yytext); - if (si) - { - if (si->lineNr != -1) - { - warn(yyFileName,yyLineNr,"multiple use of section label '%s' while adding anchor, (first occurrence: %s, line %d)",yytext,si->fileName.data(),si->lineNr); - } - else - { - warn(yyFileName,yyLineNr,"multiple use of section label '%s' while adding anchor, (first occurrence: %s)",yytext,si->fileName.data()); - } - } - else - { - si = new SectionInfo(yyFileName,yyLineNr,yytext,0,SectionInfo::Anchor,0); - Doxygen::sectionDict->append(yytext,si); - current->anchors->append(si); - } + addAnchor(yytext); addOutput(yytext); BEGIN( Comment ); } diff --git a/src/docparser.cpp b/src/docparser.cpp index f4261ac..468f862 100644 --- a/src/docparser.cpp +++ b/src/docparser.cpp @@ -2418,7 +2418,7 @@ void DocInternalRef::parse() //--------------------------------------------------------------------------- DocRef::DocRef(DocNode *parent,const QCString &target,const QCString &context) : - m_refToSection(FALSE), m_refToAnchor(FALSE), m_isSubPage(FALSE) + m_refType(Unknown), m_isSubPage(FALSE) { m_parent = parent; Definition *compound = 0; @@ -2444,8 +2444,18 @@ DocRef::DocRef(DocNode *parent,const QCString &target,const QCString &context) : m_ref = sec->ref; m_file = stripKnownExtensions(sec->fileName); - m_refToAnchor = sec->type==SectionInfo::Anchor; - m_refToSection = sec->type!=SectionInfo::Anchor; + if (sec->type==SectionInfo::Anchor) + { + m_refType = Anchor; + } + else if (sec->type==SectionInfo::Table) + { + m_refType = Table; + } + else + { + m_refType = Section; + } m_isSubPage = pd && pd->hasParentPage(); if (sec->type!=SectionInfo::Page || m_isSubPage) m_anchor = sec->label; //printf("m_text=%s,m_ref=%s,m_file=%s,m_refToAnchor=%d type=%d\n", @@ -3239,6 +3249,41 @@ endindexentry: //--------------------------------------------------------------------------- +DocHtmlCaption::DocHtmlCaption(DocNode *parent,const HtmlAttribList &attribs) +{ + m_hasCaptionId = FALSE; + HtmlAttribListIterator li(attribs); + HtmlAttrib *opt; + for (li.toFirst();(opt=li.current());++li) + { + if (opt->name=="id") // interpret id attribute as an anchor + { + SectionInfo *sec = Doxygen::sectionDict->find(opt->value); + if (sec) + { + //printf("Found anchor %s\n",id.data()); + m_file = sec->fileName; + m_anchor = sec->label; + m_hasCaptionId = TRUE; + if (g_sectionDict && g_sectionDict->find(opt->value)==0) + { + //printf("Inserting in dictionary!\n"); + g_sectionDict->append(opt->value,sec); + } + } + else + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Invalid caption id `%s'",qPrint(opt->value)); + } + } + else // copy attribute + { + m_attribs.append(new HtmlAttrib(*opt)); + } + } + m_parent = parent; +} + int DocHtmlCaption::parse() { int retval=0; @@ -3744,12 +3789,14 @@ void DocHtmlTable::accept(DocVisitor *v) { v->visitPre(this); // for HTML output we put the caption first - if (m_caption && v->id()==DocVisitor_Html) m_caption->accept(v); + //if (m_caption && v->id()==DocVisitor_Html) m_caption->accept(v); + // doxygen 1.8.11: always put the caption first + if (m_caption) m_caption->accept(v); QListIterator cli(m_children); DocNode *n; for (cli.toFirst();(n=cli.current());++cli) n->accept(v); // for other output formats we put the caption last - if (m_caption && v->id()!=DocVisitor_Html) m_caption->accept(v); + //if (m_caption && v->id()!=DocVisitor_Html) m_caption->accept(v); v->visitPost(this); } diff --git a/src/docparser.h b/src/docparser.h index 4984921..fcd18a4 100644 --- a/src/docparser.h +++ b/src/docparser.h @@ -822,15 +822,16 @@ class DocRef : public CompAccept, public DocNode QCString anchor() const { return m_anchor; } QCString targetTitle() const { return m_text; } bool hasLinkText() const { return !m_children.isEmpty(); } - bool refToAnchor() const { return m_refToAnchor; } - bool refToSection() const { return m_refToSection; } + bool refToAnchor() const { return m_refType==Anchor; } + bool refToSection() const { return m_refType==Section; } + bool refToTable() const { return m_refType==Table; } bool isSubPage() const { return m_isSubPage; } void accept(DocVisitor *v) { CompAccept::accept(this,v); } private: - bool m_refToSection; - bool m_refToAnchor; - bool m_isSubPage; + enum RefType { Unknown, Anchor, Section, Table }; + RefType m_refType; + bool m_isSubPage; QCString m_file; QCString m_relPath; QCString m_ref; @@ -1279,15 +1280,20 @@ class DocHtmlCell : public CompAccept, public DocNode class DocHtmlCaption : public CompAccept, public DocNode { public: - DocHtmlCaption(DocNode *parent,const HtmlAttribList &attribs) : - m_attribs(attribs) { m_parent = parent; } + DocHtmlCaption(DocNode *parent,const HtmlAttribList &attribs); Kind kind() const { return Kind_HtmlCaption; } void accept(DocVisitor *v) { CompAccept::accept(this,v); } const HtmlAttribList &attribs() const { return m_attribs; } int parse(); + bool hasCaptionId() const { return m_hasCaptionId; } + QCString file() const { return m_file; } + QCString anchor() const { return m_anchor; } private: HtmlAttribList m_attribs; + bool m_hasCaptionId; + QCString m_file; + QCString m_anchor; }; /** Node representing a HTML table row */ @@ -1342,7 +1348,8 @@ class DocHtmlTable : public CompAccept, public DocNode int parseXml(); uint numColumns() const { return m_numCols; } void accept(DocVisitor *v); - DocHtmlRow *firstRow() { + DocHtmlCaption *caption() const { return m_caption; } + DocHtmlRow *firstRow() const { DocNode *n = m_children.getFirst(); if (n && n->kind()==Kind_HtmlRow) return (DocHtmlRow*)n; return 0; diff --git a/src/doctokenizer.l b/src/doctokenizer.l index efc058a..8c58fb5 100644 --- a/src/doctokenizer.l +++ b/src/doctokenizer.l @@ -169,8 +169,7 @@ static void processSection() if ((si=Doxygen::sectionDict->find(g_secLabel))) { si->fileName = file; - //si = new SectionInfo(file,g_secLabel,g_secTitle,g_secType); - //Doxygen::sectionDict.insert(g_secLabel,si); + si->type = g_secType; } } @@ -389,6 +388,7 @@ WORD1 {ESCWORD}|{CHARWORDQ}+|"{"|"}"|"'\"'"|("\""[^"\n]*\n?[^"\n]*"\"") WORD2 "."|","|"("|")"|"["|"]"|":"|";"|"\?"|"="|"'" WORD1NQ {ESCWORD}|{CHARWORDQ}+|"{"|"}" WORD2NQ "."|","|"("|")"|"["|"]"|":"|";"|"\?"|"="|"'" +CAPTION [cC][aA][pP][tT][iI][oO][nN] HTMLTAG "<"(("/")?){ID}({WS}+{ATTRIB})*{WS}*(("/")?)">" HTMLKEYL "strong"|"center"|"table"|"caption"|"small"|"code"|"dfn"|"var"|"img"|"pre"|"sub"|"sup"|"tr"|"td"|"th"|"ol"|"ul"|"li"|"tt"|"kbd"|"em"|"hr"|"dl"|"dt"|"dd"|"br"|"i"|"a"|"b"|"p" HTMLKEYU "STRONG"|"CENTER"|"TABLE"|"CAPTION"|"SMALL"|"CODE"|"DFN"|"VAR"|"IMG"|"PRE"|"SUB"|"SUP"|"TR"|"TD"|"TH"|"OL"|"UL"|"LI"|"TT"|"KBD"|"EM"|"HR"|"DL"|"DT"|"DD"|"BR"|"I"|"A"|"B"|"P" @@ -1160,8 +1160,26 @@ REFWORD {LABELID}|{REFWORD2}|{REFWORD3}|{REFWORD4} /* State for the pass used to find the anchors and sections */ -[^\n@\\]+ -"@@"|"\\\\" +[^\n@\\<]+ +"@@"|"\\\\"|"@<"|"\\<" +"<"{CAPTION}({WS}+{ATTRIB})*">" { + QCString tag=yytext; + int s=tag.find("id="); + if (s!=-1) // command has id attribute + { + char c=tag[s+3]; + if (c=='\'' || c=='"') // valid start + { + int e=tag.find(c,s+4); + if (e!=-1) // found matching end + { + g_secType = SectionInfo::Table; + g_secLabel=tag.mid(s+4,e-s-4); // extract id + processSection(); + } + } + } + } {CMD}"anchor"{BLANK}+ { g_secType = SectionInfo::Anchor; BEGIN(St_SecLabel1); diff --git a/src/htmldocvisitor.cpp b/src/htmldocvisitor.cpp index 0ce4030..54dca32 100644 --- a/src/htmldocvisitor.cpp +++ b/src/htmldocvisitor.cpp @@ -1353,9 +1353,14 @@ void HtmlDocVisitor::visitPost(DocHtmlCell *c) void HtmlDocVisitor::visitPre(DocHtmlCaption *c) { if (m_hide) return; + if (c->hasCaptionId()) + { + m_t << "anchor() << "\">\n"; + } bool hasAlign = FALSE; HtmlAttribListIterator li(c->attribs()); HtmlAttrib *att; + QCString id; for (li.toFirst();(att=li.current());++li) { if (att->name=="align") hasAlign=TRUE; diff --git a/src/latexdocvisitor.cpp b/src/latexdocvisitor.cpp index b9e5839..22b3b32 100644 --- a/src/latexdocvisitor.cpp +++ b/src/latexdocvisitor.cpp @@ -171,7 +171,7 @@ QCString LatexDocVisitor::escapeMakeIndexChars(const char *s) LatexDocVisitor::LatexDocVisitor(FTextStream &t,CodeOutputInterface &ci, const char *langExt,bool insideTabbing) : DocVisitor(DocVisitor_Latex), m_t(t), m_ci(ci), m_insidePre(FALSE), - m_insideItem(FALSE), m_hide(FALSE), m_insideTabbing(insideTabbing), + m_insideItem(FALSE), m_hide(FALSE), m_hideCaption(FALSE), m_insideTabbing(insideTabbing), m_insideTable(FALSE), m_langExt(langExt), m_currentColumn(0), m_inRowspan(FALSE), m_inColspan(FALSE), m_firstRow(FALSE) { @@ -908,9 +908,28 @@ void LatexDocVisitor::visitPre(DocHtmlTable *t) if (m_hide) return; if (t->hasCaption()) { - m_t << "\\begin{table}[h]"; + DocHtmlCaption *c = t->caption(); + static bool pdfHyperLinks = Config_getBool("PDF_HYPERLINKS"); + if (!c->file().isEmpty() && pdfHyperLinks) + { + m_t << "\\hypertarget{" << stripPath(c->file()) << "_" << c->anchor() + << "}{}"; + } + m_t << endl; } + m_t << "\\begin{" << getTableName(t->parent()) << "}{" << t->numColumns() << "}\n"; + + if (t->hasCaption()) + { + DocHtmlCaption *c = t->caption(); + m_t << "\\caption{"; + visitCaption(this, c->children()); + m_t << "}"; + m_t << "\\label{" << stripPath(c->file()) << "_" << c->anchor() << "}"; + m_t << "\\\\\n"; + } + m_numCols = t->numColumns(); m_t << "\\hline\n"; @@ -930,26 +949,18 @@ void LatexDocVisitor::visitPost(DocHtmlTable *t) { m_insideTable=FALSE; if (m_hide) return; - if (t->hasCaption()) - { - m_t << "\\end{table}\n"; - } - else - { - m_t << "\\end{" << getTableName(t->parent()) << "}\n"; - } + m_t << "\\end{" << getTableName(t->parent()) << "}\n"; } void LatexDocVisitor::visitPre(DocHtmlCaption *c) { - if (m_hide) return; - m_t << "\\end{" << getTableName(c->parent()->parent()) << "}\n\\centering\n\\caption{"; + m_hideCaption = m_hide; + m_hide = TRUE; } -void LatexDocVisitor::visitPost(DocHtmlCaption *) +void LatexDocVisitor::visitPost(DocHtmlCaption *c) { - if (m_hide) return; - m_t << "}\n"; + m_hide = m_hideCaption; } void LatexDocVisitor::visitPre(DocHtmlRow *r) @@ -1029,6 +1040,8 @@ void LatexDocVisitor::visitPost(DocHtmlRow *row) { m_t << "\\endfirsthead" << endl; m_t << "\\hline" << endl; + m_t << "\\endfoot" << endl; + m_t << "\\hline" << endl; } else { @@ -1077,16 +1090,6 @@ void LatexDocVisitor::visitPre(DocHtmlCell *c) } } -#if 0 - QMap::Iterator it = m_rowspanIndices.find(m_currentColumn); - if (it!=m_rowspanIndices.end() && it.data()>0) - { - m_t << "&"; - m_currentColumn++; - it++; - } -#endif - int cs = c->colSpan(); if (cs>1 && row) { @@ -1106,7 +1109,6 @@ void LatexDocVisitor::visitPre(DocHtmlCell *c) if (rs>0) { m_inRowspan = TRUE; - //m_rowspanIndices[m_currentColumn] = rs; m_rowSpans.append(new ActiveRowSpan(c,rs,cs,m_currentColumn)); m_t << "\\multirow{" << rs << "}{\\linewidth}{"; } @@ -1282,7 +1284,7 @@ void LatexDocVisitor::visitPre(DocRef *ref) } else { - if (!ref->file().isEmpty()) startLink(ref->ref(),ref->file(),ref->anchor()); + if (!ref->file().isEmpty()) startLink(ref->ref(),ref->file(),ref->anchor(),ref->refToTable()); } if (!ref->hasLinkText()) filter(ref->targetTitle()); } @@ -1598,9 +1600,10 @@ void LatexDocVisitor::filter(const char *str) filterLatexString(m_t,str,m_insideTabbing,m_insidePre,m_insideItem); } -void LatexDocVisitor::startLink(const QCString &ref,const QCString &file,const QCString &anchor) +void LatexDocVisitor::startLink(const QCString &ref,const QCString &file,const QCString &anchor,bool refToTable) { - if (ref.isEmpty() && Config_getBool("PDF_HYPERLINKS")) // internal PDF link + static bool pdfHyperLinks = Config_getBool("PDF_HYPERLINKS"); + if (ref.isEmpty() && pdfHyperLinks) // internal PDF link { m_t << "\\hyperlink{"; if (!file.isEmpty()) m_t << stripPath(file); @@ -1608,6 +1611,10 @@ void LatexDocVisitor::startLink(const QCString &ref,const QCString &file,const Q if (!anchor.isEmpty()) m_t << anchor; m_t << "}{"; } + else if (refToTable) + { + m_t << "\\doxytableref{"; + } else if (ref.isEmpty()) // internal non-PDF link { m_t << "\\doxyref{"; @@ -1621,9 +1628,10 @@ void LatexDocVisitor::startLink(const QCString &ref,const QCString &file,const Q void LatexDocVisitor::endLink(const QCString &ref,const QCString &file,const QCString &anchor) { m_t << "}"; - if (ref.isEmpty() && !Config_getBool("PDF_HYPERLINKS")) + static bool pdfHyperLinks = Config_getBool("PDF_HYPERLINKS"); + if (ref.isEmpty() && !pdfHyperLinks) { - m_t << "{"; + m_t << "{"; filter(theTranslator->trPageAbbreviation()); m_t << "}{" << file; if (!file.isEmpty() && !anchor.isEmpty()) m_t << "_"; diff --git a/src/latexdocvisitor.h b/src/latexdocvisitor.h index e36e56c..de797ae 100644 --- a/src/latexdocvisitor.h +++ b/src/latexdocvisitor.h @@ -159,7 +159,7 @@ class LatexDocVisitor : public DocVisitor void filter(const char *str); void startLink(const QCString &ref,const QCString &file, - const QCString &anchor); + const QCString &anchor,bool refToTable=FALSE); void endLink(const QCString &ref,const QCString &file, const QCString &anchor); QCString escapeMakeIndexChars(const char *s); @@ -190,6 +190,7 @@ class LatexDocVisitor : public DocVisitor bool m_insidePre; bool m_insideItem; bool m_hide; + bool m_hideCaption; bool m_insideTabbing; bool m_insideTable; int m_numCols; diff --git a/src/latexgen.cpp b/src/latexgen.cpp index 30e28ec..2ed30e9 100644 --- a/src/latexgen.cpp +++ b/src/latexgen.cpp @@ -455,9 +455,12 @@ static void writeDefaultHeaderPart1(FTextStream &t) "\\newcommand{\\clearemptydoublepage}{%\n" " \\newpage{\\pagestyle{empty}\\cleardoublepage}%\n" "}\n" - "\n" "\n"; + // caption style definition + t << "\\usepackage{caption}\n" + << "\\captionsetup{labelsep=space,justification=centering,font={bf},singlelinecheck=off,skip=4pt,position=top}\n\n"; + // End of preamble, now comes the document contents t << "%===== C O N T E N T S =====\n" "\n" diff --git a/src/printdocvisitor.h b/src/printdocvisitor.h index 95e7e47..b86670a 100644 --- a/src/printdocvisitor.h +++ b/src/printdocvisitor.h @@ -536,10 +536,11 @@ class PrintDocVisitor : public DocVisitor indent_pre(); printf("\n", + " hasLinkText=\"%s\" refToAnchor=\"%s\" refToSection=\"%s\" refToTable=\"%s\">\n", ref->ref().data(),ref->file().data(),ref->anchor().data(), ref->targetTitle().data(),ref->hasLinkText()?"yes":"no", - ref->refToAnchor()?"yes":"no", ref->refToSection()?"yes":"no"); + ref->refToAnchor()?"yes":"no", ref->refToSection()?"yes":"no", + ref->refToTable()?"yes":"no"); } void visitPost(DocRef *) { diff --git a/src/section.h b/src/section.h index 51668a2..b6268a9 100644 --- a/src/section.h +++ b/src/section.h @@ -31,7 +31,8 @@ struct SectionInfo Subsection = 2, Subsubsection = 3, Paragraph = 4, - Anchor = 5 + Anchor = 5, + Table = 6 }; SectionInfo(const char *f,const int lin,const char *l,const char *t, SectionType st,int lev,const char *r=0) : diff --git a/templates/latex/doxygen.sty b/templates/latex/doxygen.sty index 66ffca3..64fb0f0 100644 --- a/templates/latex/doxygen.sty +++ b/templates/latex/doxygen.sty @@ -444,6 +444,11 @@ \textbf{#1} (\textnormal{#2}\,\pageref{#3})% } +% Used to link to a table when hyperlinks are turned off +\newcommand{\doxytableref}[3]{% + \ref{#3}% +} + % Used by @addindex \newcommand{\lcurly}{\{} \newcommand{\rcurly}{\}} -- cgit v0.12