/***************************************************************************
latexgenerator.cpp - description
-------------------
begin : Mit Jul 24 2002
copyright : (C) 2002-2026 by Andre Simon
email : a.simon@mailbox.org
***************************************************************************/
/*
This file is part of Highlight.
Highlight is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Highlight is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Highlight. If not, see <http://www.gnu.org/licenses/>.
*/
#include "latexgenerator.h"
using std::ostringstream;
using std::string;
namespace highlight
{
LatexGenerator::LatexGenerator()
: CodeGenerator ( LATEX )
{
// avoid "Underfull \hbox (badness 10000)" warnings
newLineTag = "\\\\\n";
longLineTag = "\\hspace*{\\fill}" + newLineTag;
spacer = initialSpacer = "\\ ";
maskWs=true;
maskWsBegin = "\\hl"+STY_NAME_STD+"{";
maskWsEnd = "}";
excludeWs=true;
styleCommentOpen="%";
}
void LatexGenerator::printBody()
{
// if (!beamerMode)
*out << "\\noindent\n" ;
if ( ! this->getBaseFont().empty() )
*out << "\\" << this->getBaseFont() << "\n" ;
else
*out << "\\ttfamily\n";
if ( ! this->getBaseFontSize().empty() && this->getBaseFontSize()!="10")
*out << "\\" << this->getBaseFontSize() << "\n" ;
if ( disableBabelShortHand )
*out << "\\shorthandoff{\"}\n";
processRootState();
*out << "\\mbox{}\n"
<< "\\normalfont\n";
if ( ! this->getBaseFontSize().empty() )
*out << "\\normalsize\n" ;
if ( disableBabelShortHand )
*out << "\\shorthandon{\"}\n";
}
string LatexGenerator::getHeader()
{
ostringstream os;
os << (beamerMode ? "\\documentclass{beamer}\n" : "\\documentclass{article}\n")
<< "\\usepackage{color}\n"
<< "\\usepackage{alltt}\n"
<< "\\usepackage[T1]{fontenc}\n";
if (replaceQuotes)
os << "\\usepackage{ngerman}\n";
if ( StringTools::change_case ( encoding ) == "utf-8" ) {
os << "\\usepackage{ucs}\n\\usepackage[utf8x]{inputenc}\n";
} else if ( encodingDefined() ) {
os << "\\usepackage[utf8]{inputenc}\n";
}
//needed for Righttorque symbol
if ( preFormatter.isEnabled() ) {
os << "\\usepackage{marvosym}\n";
}
if ( currentSyntax->highlightingEnabled() ) {
if ( includeStyleDef ) {
os << "\n"<<getStyleDefinition();
os << CodeGenerator::readUserStyleDef();
} else {
os << "\n\\input {"
<< getStyleOutputPath()
<< "}\n";
}
}
os << "\n\\title{" << docTitle << "}\n"
<< "\\begin{document}\n"
<< "\\pagecolor{bgcolor}\n";
if (beamerMode)
os << "\\frame{\\frametitle{Code section}\n";
if ( prettySymbols ) {
os<<"\\newsavebox{\\hlboxopenbrace}\n"
<<"\\newsavebox{\\hlboxclosebrace}\n"
<<"\\newsavebox{\\hlboxlessthan}\n"
<<"\\newsavebox{\\hlboxgreaterthan}\n"
<<"\\newsavebox{\\hlboxdollar}\n"
<<"\\newsavebox{\\hlboxunderscore}\n"
<<"\\newsavebox{\\hlboxand}\n"
<<"\\newsavebox{\\hlboxhash}\n"
<<"\\newsavebox{\\hlboxat}\n"
<<"\\newsavebox{\\hlboxbackslash}\n"
<<"\\newsavebox{\\hlboxpercent}\n"
<<"\\newsavebox{\\hlboxhat}\n"
<<"\\setbox\\hlboxopenbrace=\\hbox{\\verb.{.}\n"
<<"\\setbox\\hlboxclosebrace=\\hbox{\\verb.}.}\n"
<<"\\setbox\\hlboxlessthan=\\hbox{\\verb.<.}\n"
<<"\\setbox\\hlboxgreaterthan=\\hbox{\\verb.>.}\n"
<<"\\setbox\\hlboxdollar=\\hbox{\\verb.$.}\n"
<<"\\setbox\\hlboxunderscore=\\hbox{\\verb._.}\n"
<<"\\setbox\\hlboxand=\\hbox{\\verb.&.}\n"
<<"\\setbox\\hlboxhash=\\hbox{\\verb.#.}\n"
<<"\\setbox\\hlboxat=\\hbox{\\verb.@.}\n"
<<"\\setbox\\hlboxbackslash=\\hbox{\\verb.\\.}\n"
<<"\\setbox\\hlboxpercent=\\hbox{\\verb.\\%.}\n"
<<"\\setbox\\hlboxhat=\\hbox{\\verb.^.}\n"
<<"\\def\\urltilda{\\kern -.15em\\lower .7ex\\hbox{\\~{}}\\kern .04em}\n";
}
return os.str();
}
string LatexGenerator::getFooter()
{
ostringstream os;
if (beamerMode)
os << "}\n";
os << "\\end{document}\n";
if (!omitVersionComment) {
os << "% LaTeX generated by highlight "
<< highlight::Info::getVersion()
<< ", "
<< highlight::Info::getWebsite()
<< "\n";
}
return os.str();
}
void LatexGenerator::initOutputTags()
{
openTags = {
"\\hl" + STY_NAME_STD + "{",
"\\hl" + STY_NAME_STR + "{",
"\\hl" + STY_NAME_NUM + "{",
"\\hl" + STY_NAME_SLC + "{",
"\\hl" + STY_NAME_COM + "{",
"\\hl" + STY_NAME_ESC + "{",
"\\hl" + STY_NAME_DIR + "{",
"\\hl" + STY_NAME_DST + "{",
"\\hl" + STY_NAME_LIN + "{",
"\\hl" + STY_NAME_SYM + "{",
"\\hl" + STY_NAME_IPL + "{",
"\\hl" + STY_NAME_ERR + "{",
"\\hl" + STY_NAME_ERM + "{"
};
closeTags.assign ( NUMBER_BUILTIN_STATES, "}" );
guideTagOpen = "\\hl" + STY_NAME_GUI + "{";
guideTagClose = "}";
}
string LatexGenerator::getAttributes ( const string & elemName,
const ElementStyle &elem )
{
ostringstream s;
s << "\\newcommand{\\hl"
<< elemName
<< "}[1]{";
if (!elem.getCustomOverride()) {
s <<"\\textcolor[rgb]{"
<< elem.getColour().getRed ( LATEX ) << ","
<< elem.getColour().getGreen ( LATEX ) << ","
<< elem.getColour().getBlue ( LATEX )
<< "}{";
if ( elem.isBold() )
s << "\\bf{";
if ( elem.isItalic() )
s << "\\it{";
s <<"#1";
if ( elem.isBold() )
s << "}";
if ( elem.isItalic() )
s << "}";
s <<"}";
}
if (string customStyle = elem.getCustomAttribute(); !customStyle.empty()) {
s << customStyle;
}
s <<"}\n";
return s.str();
}
string LatexGenerator::getNewLine()
{
ostringstream ss;
printSyntaxError(ss);
// set wrapping arrow if previous line was wrapped
if ( preFormatter.isWrappedLine ( lineNumber-1 ) ) {
ss << "\\Righttorque";
}
if (printNewLines) {
ss << (( showLineNumbers ) ? newLineTag:longLineTag);
}
return ss.str();
}
string LatexGenerator::maskCharacter ( unsigned char c )
{
switch ( c ) {
case ' ':
return spacer;
case '<' :
return prettySymbols ? "\\usebox{\\hlboxlessthan}" : "$<$";
case '>' :
return prettySymbols ? "\\usebox{\\hlboxgreaterthan}" : "$>$";
case '{':
return prettySymbols ? "\\usebox{\\hlboxopenbrace}" : "\\{";
case '}':
return prettySymbols ? "\\usebox{\\hlboxclosebrace}" : "\\}";
case '&':
case '$':
case '#':
case '%': {
string m ( "\\" );
m += c;
return m;
}
case '\"':
return ( replaceQuotes ) ?"\\dq{}":"\"";
case '_':
return "\\textunderscore ";
case '^':
return "\\textasciicircum ";
case '\\':
return "$\\backslash$";
case '~':
return prettySymbols ? "\\urltilda " : "$\\sim$";
case '|':
return "\\textbar ";
// avoid latex compilation failure if [ or * follows a line break (\\)
case '*':
case '[':
case ']':
// avoid "merging" of consecutive '-' chars when included in bold font ( \bf )
case '-': {
string m ( 1, '{' );
m += c;
m += '}';
return m;
}
default :
return string ( 1, c );
}
}
string LatexGenerator::getKeywordOpenTag ( unsigned int styleID )
{
return "\\hl"+currentSyntax->getKeywordClasses() [styleID]+"{";
}
string LatexGenerator::getKeywordCloseTag ( unsigned int styleID )
{
return "}";
}
string LatexGenerator::getStyleDefinition()
{
if ( disableStyleCache || styleDefinitionCache.empty() ) {
ostringstream os;
os << "% highlight theme: "<<docStyle.getDescription()<<"\n";
os << getAttributes ( STY_NAME_STD, docStyle.getDefaultStyle() );
os << getAttributes ( STY_NAME_NUM, docStyle.getNumberStyle() );
os << getAttributes ( STY_NAME_ESC, docStyle.getEscapeCharStyle() );
os << getAttributes ( STY_NAME_STR, docStyle.getStringStyle() );
os << getAttributes ( STY_NAME_DST, docStyle.getPreProcStringStyle() );
os << getAttributes ( STY_NAME_SLC, docStyle.getSingleLineCommentStyle() );
os << getAttributes ( STY_NAME_COM, docStyle.getCommentStyle() );
os << getAttributes ( STY_NAME_DIR, docStyle.getPreProcessorStyle() );
os << getAttributes ( STY_NAME_SYM, docStyle.getOperatorStyle() );
os << getAttributes ( STY_NAME_IPL, docStyle.getInterpolationStyle() );
os << getAttributes ( STY_NAME_LIN, docStyle.getLineStyle() );
os << getAttributes ( STY_NAME_ERR, docStyle.getErrorStyle() );
os << getAttributes ( STY_NAME_ERM, docStyle.getErrorMessageStyle() );
if ( showIndentGuides ) {
os << getAttributes ( STY_NAME_GUI, docStyle.getIndentGuideStyle() );
}
for ( const auto &[first, second] : docStyle.getKeywordStyles() ) {
os << getAttributes ( first, second );
}
os << "\\definecolor{bgcolor}{rgb}{"
<< docStyle.getBgColour().getRed ( LATEX ) << ","
<< docStyle.getBgColour().getGreen ( LATEX ) << ","
<< docStyle.getBgColour().getBlue ( LATEX )
<< "}\n";
styleDefinitionCache=os.str();
}
return styleDefinitionCache;
}
}