CbmRoot
Loading...
Searching...
No Matches
CbmQaReportLatexEngine.cxx
Go to the documentation of this file.
1/* Copyright (C) 2024 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
2 SPDX-License-Identifier: GPL-3.0-only
3 Authors: Sergei Zharko [committer] */
4
9
11
12#include "CbmQaReportFigure.h"
13#include "CbmQaReportHeader.h"
15#include "CbmQaReportSection.h"
16#include "CbmQaReportTable.h"
17#include "CbmQaReportTail.h"
18#include "Logger.h"
19
20#include <chrono>
21#include <ctime>
22#include <iomanip>
23#include <numeric>
24#include <regex>
25#include <sstream>
26
34
35
36// ---------------------------------------------------------------------------------------------------------------------
37//
38std::string LatexEngine::FigureBody(const Figure& figure) const
39{
40 size_t nPlots = figure.GetPlots().size();
41 if (nPlots == 0) {
42 LOG(warn) << "No plots provided for figure " << figure.GetLabel() << ". Ignoring.";
43 return "";
44 }
45
46 std::stringstream out;
47 out << "\\begin{figure}[H]\n";
48 out << " \\centering\n";
49 if (nPlots == 1) {
50 out << " \\includegraphics[width=" << LatexEngine::kFigureWidth << "\\textwidth]{" << figure.GetPlots()[0].fsPath
51 << "}\n";
52 }
53 else {
54 std::vector<size_t> vPlotGrid;
55 bool bDefineDefaultGrid = figure.GetPlotGrid().empty();
56 if (!bDefineDefaultGrid) {
57 size_t nPlotsByGrid = std::accumulate(figure.GetPlotGrid().begin(), figure.GetPlotGrid().end(), 0);
58 if (nPlotsByGrid < nPlots) {
59 LOG(warn) << "Figure " << figure.GetLabel()
60 << ": provided grid does not fit the subfigures, define the default one";
61 bDefineDefaultGrid = true;
62 }
63 }
64
65 if (bDefineDefaultGrid) {
66 constexpr size_t nX = 3;
67 auto nY = static_cast<size_t>(std::ceil(static_cast<double>(nPlots) / nX));
68 vPlotGrid.resize(nY, nX);
69 }
70 else {
71 vPlotGrid = figure.GetPlotGrid();
72 }
73
74 for (auto& i : vPlotGrid) {
75 LOG(info) << "- " << i;
76 }
77
78 size_t iPlot = 0;
79 for (size_t iY = 0; iY < vPlotGrid.size(); ++iY) {
80 if (iPlot >= nPlots) {
81 break;
82 }
83 size_t nX = vPlotGrid[iY];
84 if (nX == 0) {
85 continue;
86 }
87 double wSize = 0.90 / std::min(nX, nPlots - iPlot);
88 for (size_t iX = 0; iX < nX; ++iX) {
89 if (iPlot >= nPlots) {
90 break;
91 }
92 const auto& plot = figure.GetPlots()[iPlot];
93 out << " \\begin{subfigure}[t]{" << wSize << "\\textwidth}\n";
94 out << " \\centering\n";
95 out << " \\includegraphics[width=\\linewidth]{" << plot.fsPath << "}\n";
96 out << " \\caption{" << plot.fsCaption << "}\n";
97 if (!plot.fsLabel.empty()) {
98 out << " \\label{" << figure.GetLabel() << ":" << plot.fsLabel << "}\n";
99 }
100 out << " \\end{subfigure}\n";
101 if (iX == nX - 1) {
102 out << '\n';
103 if (iY != vPlotGrid.size() - 1) {
104 out << "\\vspace{3mm}\n";
105 }
106 }
107 else {
108 out << "\\quad\n";
109 }
110 ++iPlot;
111 }
112 }
113 }
114
115 if (!figure.GetCaption().empty()) {
116 out << " \\caption{" << LatexFormat::Apply(figure.GetCaption()) << "}\n";
117 }
118 out << " \\label{" << figure.GetLabel() << "}\n";
119 out << "\\end{figure}\n";
120 return out.str();
121}
122
123// ---------------------------------------------------------------------------------------------------------------------
124//
125std::string LatexEngine::HeaderBody(const Header& header) const
126{
127 std::stringstream out;
128
129 // TODO: Move these definitions to text-file (?)
130 // Settings
131 out << "\\documentclass[12pt]{article}\n";
132 out << "\\usepackage[english]{babel}\n";
133 out << "\\usepackage{array}\n";
134 out << "\\usepackage{graphicx}\n";
135 out << "\\usepackage{float}\n";
136 out << "\\usepackage{longtable}\n";
137 out << "\\usepackage{helvet}\n";
138 out << "\\usepackage{sansmath}\n\\sansmath\n";
139 out << "\\usepackage{geometry}\n";
140 out << "\\usepackage{subcaption}\n";
141 out << "\\geometry{a4paper, total={170mm,257mm}, left=25mm, right=25mm, top=30mm, bottom=30mm}\n";
142 //out << "\\setlength{\\parindent}{0cm}\n";
143 out << "\\usepackage{hyperref}\n";
144 //out << "\\usepackage{color}\n";
145 //out << "\\definecolor{hrefcolor}{rgb}{0.2, 0.2, 0.6}\n";
146 //out << "\\hypersetup{color}{colorlinks=true, urlcolor=hrefcolor,linkcolor=black,citecolor=hrefcolor}\n";
147 if (!header.GetPageHeader().empty()) {
148 //out << "\\usepackage{fancyhdr}\n\\pagestyle{fancy}\n\\lhead{"
149 // << LatexFormat::Apply(header.GetPageHeader()) << "}\n";
150 }
151 out << "\n";
152 out << "\\newcolumntype{L}[1]{>{\\flushleft\\arraybackslash}p{#1}}\n";
153
154 // Title page settings
155 out << "\\title{" << LatexFormat::Apply(header.GetTitle()) << "\\\\ \\vspace{1cm} \\Large "
156 << LatexFormat::Apply(header.GetSubtitle()) << "}\n";
157 out << "\\author{Author: " << LatexFormat::Apply(header.GetAuthor()) << "}\n";
158 auto timeNow = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
159 out << "\\date{Generated on " << std::put_time(std::localtime(&timeNow), "%a %d.%m.%Y, %X") << "}\n";
160
161
162 // Title page
163 out << "\\begin{document}\n";
164 out << "\\begin{titlepage}\n";
165 out << " \\maketitle\n";
166 out << " \\thispagestyle{empty}\n";
167 out << " \\vfill\n";
168 //out << " \\centering\n";
169 out << " Setup: " << LatexFormat::Apply(header.GetSetup()) << "\\\\\n";
170 out << "\\end{titlepage}\n";
171 out << "\\newpage\n";
172 out << "\\tableofcontents\n";
173 out << "\\newpage\n";
174
175 out << '\n';
176 return out.str();
177}
178
179// ---------------------------------------------------------------------------------------------------------------------
180//
181std::string LatexEngine::SectionBody(const Section& section) const
182{
183 std::stringstream out;
184 std::string sSectionType = "";
185 if (section.GetLevel() == 0) {
186 sSectionType = "section";
187 }
188 else if (section.GetLevel() == 1) {
189 sSectionType = "subsection";
190 }
191 else {
192 sSectionType = "subsubsection";
193 }
194
195 if (section.GetLevel() == 0) {
196 out << "\\newpage\n";
197 }
198 out << "\\" << sSectionType << "{" << section.GetTitle() << "}\n\n";
199 for (const auto& pElement : section.GetDaughterElements()) {
200 out << pElement->GetBody(*this) << '\n';
201 }
202 out << '\n';
203 return out.str();
204}
205
206
207// ---------------------------------------------------------------------------------------------------------------------
208//
209std::string LatexEngine::TableBody(const Table& table) const
210{
211 std::stringstream out;
212 int nRows = table.GetNofRows();
213 int nCols = table.GetNofCols();
214
215
216 out << "\\begin{table}\n";
217 out << "\\footnotesize\n";
218 // tabular header
219 if (!table.GetCaption().empty()) {
220 out << " \\caption{" << LatexFormat::Apply(table.GetCaption()) << "}\n";
221 }
222 out << " \\begin{longtable}{" << std::string(nCols, 'c') << "}\n";
223 // table header
224 out << " \\hline\n";
225 out << " ";
226 for (int iCol = 0; iCol < nCols; ++iCol) {
227 out << table.GetColumnTitle(iCol) << ((iCol < nCols - 1) ? " & " : " \\\\\n");
228 }
229 out << " \\hline\n";
230 // table body
231 for (int iRow = 0; iRow < nRows; ++iRow) {
232 out << " ";
233 for (int iCol = 0; iCol < nCols; ++iCol) {
234 out << table(iRow, iCol) << ((iCol < nCols - 1) ? " & " : " \\\\\n");
235 }
236 }
237 out << " \\hline\n";
238
239 out << " \\end{longtable}\n";
240 out << " \\label{" << table.GetLabel() << "}\n";
241 out << "\\end{table}\n";
242 return out.str();
243}
244
245
246// ---------------------------------------------------------------------------------------------------------------------
247//
248std::string LatexEngine::TailBody(const Tail&) const
249{
250 std::stringstream out;
251 out << "\\end{document}\n";
252 return out.str();
253}
254
255// ---------------------------------------------------------------------------------------------------------------------
256//
257void LatexEngine::Compile(const std::string& source) const
258{
259 // Search for compiler program in path
260 std::string compiler = fsLatexCompiler.substr(0, fsLatexCompiler.find_first_of(' ')); // compiler name without opts
261 int bCompilerFonud = false;
262 {
263 std::stringstream stream(getenv("PATH"));
264 std::string dir;
265 while (!bCompilerFonud && std::getline(stream, dir, ':')) {
266 bCompilerFonud = fs::exists(fs::path(dir) / compiler);
267 }
268 }
269
270 if (!bCompilerFonud) {
271 LOG(error) << "cbm::qa::report::LatexEngine::Compile(): compiler \"" << fsLatexCompiler << "\" not found in PATH. "
272 << "Please use another compiler or compile the sourec in external program";
273 return;
274 }
275
276 // Execute compiler
277 fs::path sourceDir = fs::path(source).parent_path();
278 fs::path currPath = fs::current_path();
279 fs::current_path(sourceDir);
280
281 std::string logFile = compiler + ".log";
282 std::string command = fmt::format("{} {} > {}", fsLatexCompiler, source, logFile);
283 command = command + ";" + command; // compile two times to get contents
284
285 LOG(info) << "cbm::qa::report::LatexEngine::Compile(): executing command: \n\t" << command;
286 int res = std::system(command.c_str());
287 fs::current_path(currPath);
288
289 LOG(info) << "cbm::qa::report::LatexEngine::Compile(): compillation " << (res == 0 ? "succeed" : "failed")
290 << "(compiler log: \"" << logFile << "\")";
291}
Base class for the report figure (header)
Base class for the report header (header)
LaTeX Beamer engine for the cbm::qa::report (source)
Common LaTeX utilities (header)
Base class for the report section (header)
Base class for the report table (header)
const std::vector< std::shared_ptr< Element > > GetDaughterElements() const
Get daughter elements.
const std::string & GetTitle() const
Gets name.
const std::string & GetLabel() const
Gets label.
Figure in the report.
const std::string & GetCaption() const
Gets caption.
const std::vector< Plot > & GetPlots() const
Gets plot vector.
const std::vector< size_t > & GetPlotGrid() const
Gets plot grid.
Header of the report.
const std::string & GetPageHeader() const
Gets page header.
const std::string & GetTitle() const
Gets title.
const std::string & GetSubtitle() const
Gets subtitle.
const std::string & GetSetup() const
Gets setup.
const std::string & GetAuthor() const
Gets author.
Plain LaTeX document engine.
std::string TailBody(const Tail &tail) const override
Creates a body for tail.
void Compile(const std::string &source) const override
Compiles source.
std::string FigureBody(const Figure &figure) const override
Creates a body for figure.
static constexpr double kFigureWidth
Figure width [in textwidth].
std::string SectionBody(const Section &section) const override
Creates a body for section.
std::string TableBody(const Table &table) const override
Creates a body for table.
std::string HeaderBody(const Header &header) const override
Creates a body for header.
static std::string Apply(std::string_view input)
Applies a LaTeX-friendly formatting.
Section of the report.
int GetLevel() const
Gets level of the section.
Table element in the report.
const std::string & GetColumnTitle(int iCol) const
Gets column title.
int GetNofRows() const
Gets number of rows.
int GetNofCols() const
Gets number of columns.
const std::string & GetCaption() const
Gets caption.
Tail of the report.