summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--addon/doxywizard/expert.cpp3
-rw-r--r--doc/formulas.doc20
-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
7 files changed, 242 insertions, 2 deletions
diff --git a/addon/doxywizard/expert.cpp b/addon/doxywizard/expert.cpp
index 64df27f..1303a22 100644
--- a/addon/doxywizard/expert.cpp
+++ b/addon/doxywizard/expert.cpp
@@ -398,6 +398,7 @@ static QString getDocsForNode(const QDomElement &child)
// Remove / replace doxygen markup strings
// the regular expressions are hard to read so the intention will be given
+ // Note: see also configgen.py in the src directory for other doxygen parts
QRegExp regexp;
// remove \n at end and replace by a space
regexp.setPattern(SA("\\n$"));
@@ -432,6 +433,8 @@ static QString getDocsForNode(const QDomElement &child)
docs.replace(regexp,SA("\"External Indexing and Searching\""));
regexp.setPattern(SA("\\\\ref[ ]+external"));
docs.replace(regexp,SA("\"Linking to external documentation\""));
+ regexp.setPattern(SA("\\\\ref[ ]+formulas"));
+ docs.replace(regexp,SA("\"Including formulas\""));
// fallback for not handled
docs.replace(SA("\\\\ref"),SA(""));
// \b word -> <b>word<\b>
diff --git a/doc/formulas.doc b/doc/formulas.doc
index 520f089..88a8725 100644
--- a/doc/formulas.doc
+++ b/doc/formulas.doc
@@ -100,9 +100,27 @@ the section should contain valid command for the specific environment.
\warning Currently, doxygen is not very fault tolerant in recovering
from typos in formulas. It may be necessary to remove the
-files <code>formula.repository</code> that are written to the html and rtf directories to
+files <code>formula.repository</code> that are written to the html, rtf etc. directories to
get rid of an incorrect formula as well as the <code>form_*</code> files.
+To have the possibility to define your own \LaTeX commands, for e.g. formula building blocks
+or consistent writing of certain words, the configuration option \ref cfg_formula_macrofile "FORMULA_MACROFILE"
+can be used. to supply a file with \LaTeX commands.
+This file can contain \LaTeX `\newcommand` and \`renewcommand` commands and they are included
+formulas (image version and MathJax version) as well as in the generated \LaTeX output (for PDF generation).<br>
+The `\newcommand` (and `\renewcommand`) are restricted to a version without optional
+parameters so only the following types are supported:
+```
+\newcommand{\cmd}{replacement}
+ and
+\newcommand{\cmd}[nr]{replacement}
+```
+e.g.
+```
+\newcommand{\E}{\mathrm{E}}
+\newcommand{\ccSum}[3]{\sum_{#1}^{#2}{#3}}
+```
+
\htmlonly
Go to the <a href="tables.html">next</a> section or return to the
<a href="index.html">index</a>.
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);