summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authoralbert-github <albert.tests@gmail.com>2019-08-27 14:55:47 (GMT)
committeralbert-github <albert.tests@gmail.com>2019-08-27 14:55:47 (GMT)
commitfd3b60caa8bb99bec81b74d74f394c6043091c76 (patch)
tree1558814875abeb800fbf4bb92cfa146680460a9a /src
parent3d4eb56c5fc8135129e7dd748e2df7a4757195a7 (diff)
downloadDoxygen-fd3b60caa8bb99bec81b74d74f394c6043091c76.zip
Doxygen-fd3b60caa8bb99bec81b74d74f394c6043091c76.tar.gz
Doxygen-fd3b60caa8bb99bec81b74d74f394c6043091c76.tar.bz2
Create possibility to define LaTeX commands for formulas
To be able to have building bocks for formulas one can create a `\newcommand` (or when one wants to change a command `\renewcommand`). Due to the different handling of LaTeX commands in pure LaTeX code (latex output and formulas converted to images) and MathJax it is necessary to transform LaTeX commands to the MathJax equivalent. This is done in a transparent way by providing the new commands in a file and add this verbatim to the pure LaTeX code and to translate the `\newcommand` and `\renewcomamnd` to MathJax macros.
Diffstat (limited to 'src')
-rw-r--r--src/config.xml10
-rwxr-xr-xsrc/configgen.py2
-rw-r--r--src/formula.cpp14
-rw-r--r--src/htmlgen.cpp185
-rw-r--r--src/latexgen.cpp10
5 files changed, 220 insertions, 1 deletions
diff --git a/src/config.xml b/src/config.xml
index e68b3d7..a226863 100644
--- a/src/config.xml
+++ b/src/config.xml
@@ -2377,6 +2377,16 @@ The \c DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
]]>
</docs>
</option>
+ <option type='string' id='FORMULA_MACROFILE' format='file' defval=''>
+ <docs>
+<![CDATA[
+ The \c FORMULA_MACROFILE can contain \f$\mbox{\LaTeX}\f$ `\newcommand` and
+ `\renewcommand` commands to create new \f$\mbox{\LaTeX}\f$ commands to be used
+ in formulas as building blocks.
+ See the section \ref formulas for details.
+]]>
+ </docs>
+ </option>
<option type='bool' id='USE_MATHJAX' defval='0' depends='GENERATE_HTML'>
<docs>
<![CDATA[
diff --git a/src/configgen.py b/src/configgen.py
index dbba264..6720116 100755
--- a/src/configgen.py
+++ b/src/configgen.py
@@ -21,6 +21,7 @@ from xml.dom import minidom, Node
def transformDocs(doc):
# join lines, unless it is an empty line
# remove doxygen layout constructs
+ # Note: also look at expert.cpp of doxywizard for doxywizard parts
doc = doc.strip()
doc = doc.replace("\n", " ")
doc = doc.replace("\r", " ")
@@ -57,6 +58,7 @@ def transformDocs(doc):
doc)
doc = re.sub('\\\\ref +external', '"Linking to external documentation"',
doc)
+ doc = re.sub('\\\\ref +formulas', '"Including formulas"', doc)
# fallback for not handled
doc = re.sub('\\\\ref', '', doc)
#<a href="address">description</a> -> description (see: address)
diff --git a/src/formula.cpp b/src/formula.cpp
index 1c5042e..f79bd47 100644
--- a/src/formula.cpp
+++ b/src/formula.cpp
@@ -53,6 +53,15 @@ void FormulaList::generateBitmaps(const char *path)
// store the original directory
if (!d.exists()) { err("Output dir %s does not exist!\n",path); exit(1); }
QCString oldDir = QDir::currentDirPath().utf8();
+ QCString macroFile = Config_getString(FORMULA_MACROFILE);
+ QCString stripMacroFile;
+ if (!macroFile.isEmpty())
+ {
+ QFileInfo fi(macroFile);
+ macroFile=fi.absFilePath().utf8();
+ stripMacroFile = fi.fileName().data();
+ }
+
// go to the html output directory (i.e. path)
QDir::setCurrent(d.absPath());
QDir thisDir;
@@ -74,6 +83,11 @@ void FormulaList::generateBitmaps(const char *path)
t << "\\usepackage[utf8]{inputenc}" << endl; // looks like some older distributions with newunicode package 1.1 need this option.
writeExtraLatexPackages(t);
writeLatexSpecialFormulaChars(t);
+ if (!macroFile.isEmpty())
+ {
+ copyFile(macroFile,stripMacroFile);
+ t << "\\input{" << stripMacroFile << "}" << endl;
+ }
t << "\\pagestyle{empty}" << endl;
t << "\\begin{document}" << endl;
int page=0;
diff --git a/src/htmlgen.cpp b/src/htmlgen.cpp
index 402b4e4..5d2945a 100644
--- a/src/htmlgen.cpp
+++ b/src/htmlgen.cpp
@@ -49,6 +49,7 @@
#include "bufstr.h"
#include "resourcemgr.h"
#include "tooltip.h"
+#include "growbuf.h"
//#define DBG_HTML(x) x;
#define DBG_HTML(x)
@@ -56,6 +57,7 @@
static QCString g_header;
static QCString g_footer;
static QCString g_mathjax_code;
+static QCString g_latex_macro;
static bool DoxyCodeLineOpen = FALSE;
@@ -112,6 +114,174 @@ static void writeServerSearchBox(FTextStream &t,const char *relPath,bool highlig
}
//------------------------------------------------------------------------
+/// Convert a set of LaTeX `\(re)newcommand` to a form readable by MathJax
+/// LaTeX syntax:
+/// \newcommand{\cmd}{replacement}
+/// or
+/// \renewcommand{\cmd}{replacement}
+/// MathJax syntax:
+/// cmd: "{replacement}"
+///
+/// LaTeX syntax:
+/// \newcommand{\cmd}[nr]{replacement}
+/// or
+/// \renewcommand{\cmd}[nr]{replacement}
+/// MathJax syntax:
+/// cmd: ["{replacement}",nr]
+static QCString getConvertLatexMacro()
+{
+ QCString macrofile = Config_getString(FORMULA_MACROFILE);
+ if (macrofile.isEmpty()) return "";
+ QCString s = fileToString(macrofile);
+ macrofile = QFileInfo(macrofile).absFilePath().utf8();
+ int size = s.length();
+ GrowBuf out(size);
+ const char *data = s.data();
+ int line = 1;
+ int cnt = 0;
+ int i = 0;
+ QCString nr;
+ while (i < size)
+ {
+ nr = "";
+ // skip initial white space, but count lines
+ while (i < size && (data[i] == ' ' || data[i] == '\t' || data[i] == '\n'))
+ {
+ if (data[i] == '\n') line++;
+ i++;
+ }
+ if (i >= size) break;
+ // check for \newcommand or \renewcommand
+ if (data[i] != '\\')
+ {
+ warn(macrofile,line, "file contains non valid code, expected '\\' got '%c'\n",data[i]);
+ return "";
+ }
+ i++;
+ if (!qstrncmp(data + i, "newcommand", strlen("newcommand"))) i += strlen("newcommand");
+ else if (!qstrncmp(data + i, "renewcommand", strlen("renewcommand"))) i += strlen("renewcommand");
+ else
+ {
+ warn(macrofile,line, "file contains non valid code, expected 'newcommand' or 'renewcommand'");
+ return "";
+ }
+ // handle {cmd}
+ if (data[i] != '{')
+ {
+ warn(macrofile,line, "file contains non valid code, expected '{' got '%c'\n",data[i]);
+ return "";
+ }
+ i++;
+ if (data[i] != '\\')
+ {
+ warn(macrofile,line, "file contains non valid code, expected '\\' got '%c'\n",data[i]);
+ return "";
+ }
+ i++;
+ // run till }, i.e. cmd
+ out.addStr(" ");
+ while (i < size && (data[i] != '}')) out.addChar(data[i++]);
+ if (i >= size)
+ {
+ warn(macrofile,line, "file contains non valid code, no closing '}' for command");
+ return "";
+ }
+ out.addChar(':');
+ out.addChar(' ');
+ i++;
+
+ if (data[i] == '[')
+ {
+ // handle [nr]
+ // run till ]
+ out.addChar('[');
+ i++;
+ while (i < size && (data[i] != ']')) nr += data[i++];
+ if (i >= size)
+ {
+ warn(macrofile,line, "file contains non valid code, no closing ']'");
+ return "";
+ }
+ i++;
+ }
+ else if (data[i] != '{')
+ {
+ warn(macrofile,line, "file contains non valid code, expected '[' or '{' got '%c'\n",data[i]);
+ return "";
+ }
+ // handle {replacement}
+ // retest as the '[' part might have advanced so we can have a new '{'
+ if (data[i] != '{')
+ {
+ warn(macrofile,line, "file contains non valid code, expected '{' got '%c'\n",data[i]);
+ return "";
+ }
+ out.addChar('"');
+ out.addChar('{');
+ i++;
+ // run till }
+ cnt = 1;
+ while (i < size && cnt)
+ {
+ switch(data[i])
+ {
+ case '\\':
+ out.addChar('\\'); // need to escape it for MathJax js code
+ out.addChar('\\');
+ i++;
+ if (data[i] == '\\') // we have an escaped backslash
+ {
+ out.addChar('\\');
+ out.addChar('\\');
+ i++;
+ }
+ else if (data[i] != '"') out.addChar(data[i++]); // double quote handled separately
+ break;
+ case '{':
+ cnt++;
+ out.addChar(data[i++]);
+ break;
+ case '}':
+ cnt--;
+ if (cnt) out.addChar(data[i]);
+ i++;
+ break;
+ case '"':
+ out.addChar('\\'); // need to escape it for MathJax js code
+ out.addChar(data[i++]);
+ break;
+ case '\n':
+ line++;
+ out.addChar(data[i++]);
+ break;
+ default:
+ out.addChar(data[i++]);
+ break;
+ }
+ }
+ if (i > size)
+ {
+ warn(macrofile,line, "file contains non valid code, no closing '}' for replacement");
+ return "";
+ }
+ out.addChar('}');
+ out.addChar('"');
+ if (!nr.isEmpty())
+ {
+ out.addChar(',');
+ out.addStr(nr);
+ }
+ if (!nr.isEmpty())
+ {
+ out.addChar(']');
+ }
+ out.addChar(',');
+ out.addChar('\n');
+ }
+ out.addChar(0);
+ return out.get();
+}
+//------------------------------------------------------------------------
/// Clear a text block \a s from \a begin to \a end markers
QCString clearBlock(const char *s,const char *begin,const char *end)
@@ -381,7 +551,18 @@ static QCString substituteHtmlKeywords(const QCString &s,
mathJaxJs += g_mathjax_code;
mathJaxJs += "\n";
}
- mathJaxJs += "</script>";
+ mathJaxJs += "</script>\n";
+ if (!g_latex_macro.isEmpty())
+ {
+ mathJaxJs += "<script type=\"text/x-mathjax-config\">\n"
+ " MathJax.Hub.Config({\n"
+ " TeX: { Macros: {\n";
+ mathJaxJs += g_latex_macro;
+ mathJaxJs += "\n"
+ " } }\n"
+ "});\n"
+ "</script>\n";
+ }
mathJaxJs += "<script type=\"text/javascript\" async=\"async\" src=\"" + path + "MathJax.js\"></script>\n";
}
@@ -763,6 +944,8 @@ void HtmlGenerator::init()
g_mathjax_code=fileToString(Config_getString(MATHJAX_CODEFILE));
//printf("g_mathjax_code='%s'\n",g_mathjax_code.data());
}
+ g_latex_macro=getConvertLatexMacro();
+ //printf("converted g_latex_macro='%s'\n",g_latex_macro.data());
}
createSubDirs(d);
diff --git a/src/latexgen.cpp b/src/latexgen.cpp
index e6c6861..27d0c47 100644
--- a/src/latexgen.cpp
+++ b/src/latexgen.cpp
@@ -688,6 +688,16 @@ static void writeDefaultHeaderPart1(FTextStream &t)
writeExtraLatexPackages(t);
writeLatexSpecialFormulaChars(t);
+ QCString macroFile = Config_getString(FORMULA_MACROFILE);
+ if (!macroFile.isEmpty())
+ {
+ QCString dir=Config_getString(LATEX_OUTPUT);
+ QFileInfo fi(macroFile);
+ macroFile=fi.absFilePath().utf8();
+ QCString stripMacroFile = fi.fileName().data();
+ copyFile(macroFile,dir + "/" + stripMacroFile);
+ t << "\\input{" << stripMacroFile << "}" << endl;
+ }
// Hyperlinks
bool pdfHyperlinks = Config_getBool(PDF_HYPERLINKS);