64 uint32_t formulaCount=0;
65 msg(
"Reading formula repository...\n");
68 std::string
prefix(
"\\_form#");
70 bool hasNextLine = !getline(f,readLine).fail();
74 int lineNr = nextLineNr;
77 while ((hasNextLine = !getline(f,readLine).fail()))
81 line +=
"\n" + readLine;
85 static const reg::Ex re_new(R
"(\\_form#(\d+)=(\d+)x(\d+):)");
87 static const reg::Ex re_old(R
"(\\_form#(\d+):)");
96 id = std::stoi(match[1].str());
97 width = std::stoi(match[2].str());
98 height = std::stoi(match[3].str());
99 text = line.substr(match.position()+match.length());
107 msg(
"old formula.repository format detected; forcing upgrade.\n");
108 p->repositoriesValid =
false;
113 warn_uncond(
"{}/formula.repository contains invalid content at line {}: found: '{}'\n",dir,lineNr,line);
114 p->repositoriesValid =
false;
118 auto it =
p->formulaIdMap.find(
id);
120 if (it!=
p->formulaIdMap.end())
122 formula = it->second;
123 if (formula->
text().
str()!=text)
125 msg(
"differences detected between formula.repository files; forcing upgrade.\n");
126 p->repositoriesValid =
false;
134 formula =
p->formulas.add(text,
id,width,height);
135 p->formulaIdMap.emplace(
id,formula);
141 formImgName.
sprintf(
"form_%d",formula->
id());
142 FileInfo fiPng((dir+
"/"+formImgName+
".png").str());
143 FileInfo fiSvg((dir+
"/"+formImgName+
".svg").str());
149 FileInfo fiPngDark((dir+
"/"+formImgName+
"_dark.png").str());
150 FileInfo fiSvgDark((dir+
"/"+formImgName+
"_dark.svg").str());
151 bool isCachedDark = fiPngDark.
exists() || fiSvgDark.
exists();
160 if (formulaCount>0 && formulaCount!=
p->formulas.size())
162 msg(
"differences detected between formula.repository files; forcing upgrade.\n");
163 p->repositoriesValid =
false;
168 p->repositoriesValid =
false;
175 if (!
p->repositoriesValid)
179 p->repositoriesValid =
true;
186 DString texName=fileName+
".tex";
191 t <<
"\\documentclass{article}\n";
192 t <<
"\\usepackage{iftex}\n";
193 t <<
"\\usepackage{ifthen}\n";
194 t <<
"\\usepackage{epsfig}\n";
195 t <<
"\\usepackage[utf8]{inputenc}\n";
196 t <<
"\\usepackage{xcolor}\n";
200 t <<
"\\color{white}\n";
201 t <<
"\\pagecolor{black}\n";
208 if (!macroFile.
empty())
212 t <<
"\\input{" << stripMacroFile <<
"}\n";
215 t <<
"\\pagestyle{empty}\n";
216 t <<
"\\begin{document}\n";
217 for (
const auto &formula :
p->formulas)
219 int id = formula->id();
223 (mode==
Mode::Dark && !formula->isCachedDark())
227 t << formula->text() <<
"\n\\pagebreak\n\n";
228 formulasToGenerate.push_back(
id);
234 t <<
"\\end{document}\n";
243 const size_t argsLen = 4096;
252 err(
"Problems running latex. Check your installation or look "
253 "for typos in {0}.tex and check {0}.log!\n",fileName);
258 if (logFile.
empty() ||
271 const size_t argsLen = 4096;
275 snprintf(args,argsLen,
"-q -D 600 -n 1 -p %d -o %s_tmp.ps %s.dvi",pageIndex,
qPrint(formBase),
qPrint(fileName));
278 err(
"Problems running dvips. Check your installation!\n");
286 const size_t argsLen = 4096;
289 snprintf(args,argsLen,
"-q -dBATCH -dNOPAUSE -P- -dNOSAFER -sDEVICE=bbox %s_tmp.ps 2>%s_tmp.epsi",
300 int *x1,
int *y1,
int *x2,
int *y2,
301 double *x1hi,
double *y1hi,
double *x2hi,
double *y2hi)
303 FileInfo fi((formBase+
"_tmp.epsi").str());
309 sscanf(eps.
data()+i,
"%%%%BoundingBox:%d %d %d %d",x1,y1,x2,y2);
313 err(
"Couldn't extract bounding box from {}_tmp.epsi\n",formBase);
318 sscanf(eps.
data()+i,
"%%%%HiResBoundingBox:%lf %lf %lf %lf",x1hi,y1hi,x2hi,y2hi);
322 err(
"Couldn't extract high resolution bounding box from {}_tmp.epsi\n",formBase);
334 double scaleFactor = 1.25;
336 if (zoomFactor<8 || zoomFactor>50) zoomFactor=10;
337 scaleFactor *= zoomFactor/10.0;
342 formula->
setWidth(
static_cast<int>((x2-x1)*scaleFactor+0.5));
343 formula->
setHeight(
static_cast<int>((y2-y1)*scaleFactor+0.5));
350 const size_t argsLen = 4096;
353 snprintf(args,argsLen,
"-q -dBATCH -dNOPAUSE -P- -dNOSAFER -sDEVICE=pdfwrite"
354 " -o %s_tmp.pdf -c \"[/CropBox [%d %d %d %d] /PAGES pdfmark\" -f %s_tmp.ps",
366 const size_t argsLen = 4096;
369 snprintf(args,argsLen,
"-q -dBATCH -dNOPAUSE -P- -dNOSAFER -sDEVICE=eps2write"
370 " -o %s_tmp.eps -f %s_tmp.ps",
qPrint(formBase),
qPrint(formBase));
381 const size_t argsLen = 4096;
383 snprintf(args,argsLen,
"%s_tmp.pdf %s",
qPrint(formBase),
qPrint(outFile));
386 err(
"Problems running pdf2svg. Check your installation!\n");
394 const size_t argsLen = 4096;
397 if (inkscapeVersion == -1)
399 err(
"Problems determining the version of inkscape. Check your installation!\n");
402 else if (inkscapeVersion == 0)
412 err(
"Problems running inkscape. Check your installation!\n");
420 int x1,
int y1,
int x2,
int y2,
421 double x1hi,
double y1hi,
double x2hi,
double y2hi)
427 if (epsIn.is_open() && epsOut.is_open())
430 while (getline(epsIn,line))
432 if (line.rfind(
"%%BoundingBox",0)==0)
434 epsOut <<
"%%BoundingBox: " << std::max(0,x1-1) <<
" " << std::max(0,y1-1) <<
" " << (x2+1) <<
" " << (y2+1) <<
"\n";
436 else if (line.rfind(
"%%HiResBoundingBox",0)==0)
438 epsOut <<
"%%HiResBoundingBox: " << std::max(0.0,x1hi-1.0) <<
" " << std::max(0.0,y1hi-1.0) <<
" " << (x2hi+1.0) <<
" " << (y2hi+1.0) <<
"\n";
442 epsOut << line <<
"\n";
450 err(
"Problems correcting the eps files from {}_tmp.eps to {}_tmp_corr.eps\n",
459 const size_t argsLen = 4096;
461 snprintf(args,argsLen,
"-q -dNOSAFER -dBATCH -dNOPAUSE -dEPSCrop -sDEVICE=pngalpha -dGraphicsAlphaBits=4 -dTextAlphaBits=4 "
462 "-r%d -sOutputFile=%s %s_tmp_corr.eps",
static_cast<int>(scaleFactor*72),
qPrint(outFile),
qPrint(formBase));
477 msg(
"Generating image {} for formula\n",outputFile);
484 int x1=0,y1=0,x2=0,y2=0;
485 double x1hi=0.0,y1hi=0.0,x2hi=0.0,y2hi=0.0;
490 if (!
extractBoundingBox(formBase,&x1,&y1,&x2,&y2,&x1hi,&y1hi,&x2hi,&y2hi))
return tempFiles;
496 formBaseLight.
sprintf(
"_form%d",pageNum);
497 if (!
extractBoundingBox(formBaseLight,&x1,&y1,&x2,&y2,&x1hi,&y1hi,&x2hi,&y2hi))
return tempFiles;
518 err(
"Neither 'pdf2svg' nor 'inkscape' present for conversion of formula to 'svg'\n");
522 tempFiles.push_back(formBase.
str()+
"_tmp.pdf");
536 if (!
createPNG(formBase,outputFile,scaleFactor))
return tempFiles;
538 tempFiles.push_back(formBase.
str()+
"_tmp.eps");
539 tempFiles.push_back(formBase.
str()+
"_tmp_corr.eps");
543 tempFiles.push_back(formBase.
str()+
"_tmp.ps");
546 tempFiles.push_back(formBase.
str()+
"_tmp.epsi");
555 createLatexFile(formulaFileName,format,mode,formulasToGenerate,toIndex);
557 if (!formulasToGenerate.empty())
561 auto getFormula = [
this](
int pageNum) ->
Formula *
563 auto it =
p->formulaIdMap.find(pageNum);
564 if (it!=
p->formulaIdMap.end())
572 std::size_t numThreads =
static_cast<std::size_t
>(
Config_getInt(NUM_PROC_THREADS));
576 std::vector< std::future< StringVector > > results;
577 for (
int pageNum : formulasToGenerate)
580 auto formula = getFormula(pageNum);
583 return generateFormula(thisDir,formulaFileName,formula,pageNum,pageIndex,format,hd,mode);
585 results.emplace_back(threadPool.
queue(processFormula));
588 for (
auto &f : results)
591 p->tempFiles.insert(
p->tempFiles.end(),tf.begin(),tf.end());
596 for (
int pageNum : formulasToGenerate)
599 auto formula = getFormula(pageNum);
601 p->tempFiles.insert(
p->tempFiles.end(),tf.begin(),tf.end());
607 p->tempFiles.push_back(formulaFileName.
str()+
".dvi");
608 p->tempFiles.push_back(formulaFileName.
str()+
".log");
609 p->tempFiles.push_back(formulaFileName.
str()+
".aux");
612 p->tempFiles.push_back(formulaFileName.
str()+
".tex");
622 for (
const auto &formula :
p->formulas)
624 t <<
"\\_form#" << formula->id();
625 if (formula->width()!=-1 && formula->height()!=-1)
627 t <<
"=" << formula->width() <<
"x" << formula->height();
629 t <<
":" << formula->text() <<
"\n";
640 term(
"Output directory '{}' does not exist!\n",path);
646 if (!macroFile.
empty())
657 if (!macroFile.
empty())
664 if (toIndex &&
Config_getEnum(HTML_COLORSTYLE)!=HTML_COLORSTYLE_t::LIGHT)
674 for (
const auto &file :
p->tempFiles)
687 p->formulaIdMap.clear();
692 Formula *formula =
p->formulas.find(formulaText);
695 return formula->
id();
698 int id =
static_cast<int>(
p->formulas.size());
699 formula =
p->formulas.add(formulaText,
id,width,height);
700 p->formulaIdMap.emplace(
id,formula);
706 auto it =
p->formulaIdMap.find(formulaId);
707 return it !=
p->formulaIdMap.end() ? it->second :
nullptr;
713 auto it =
p->formulaIdMap.find(formulaId);
714 return it !=
p->formulaIdMap.end() ? it->second :
nullptr;
721 return !
p->formulas.empty();
734 static int inkscapeVersion = -2;
735 if (inkscapeVersion == -2)
737 DString inkscapeVersionFile =
"inkscape_version" ;
738 inkscapeVersion = -1;
751 if (inkscapeVersionIn.is_open())
754 while (getline(inkscapeVersionIn,line))
756 size_t dotPos = line.find(
'.');
757 if (line.rfind(
"Inkscape ",0)==0 && dotPos>0)
760 inkscapeVersion = std::stoi(line.substr(9,dotPos-9));
764 inkscapeVersionIn.close();
772 thisDir.
remove(inkscapeVersionFile.
str());
775 return inkscapeVersion;
A String class for use with Doxygen wrapping std::string and adding some additional functionality off...
bool empty() const
Returns true iff the string is empty (std::string compatible alias for isEmpty()).
static constexpr size_t npos
value used to indicate 'not found' or 'to the end of the string', matching std::string::npos
size_t find(char c, size_t pos=0) const
DString & sprintf(const char *format,...)
const std::string & str() const
const char * data() const
Returns a pointer to the contents of the string in the form of a 0-terminated C string.
static bool isFlagSet(const DebugMask mask)
Class representing a directory in the file system.
static std::string currentDirPath()
std::string absPath() const
bool remove(const std::string &path, bool acceptsAbsPath=true) const
static bool setCurrent(const std::string &path)
static IndexList * indexList
Minimal replacement for QFileInfo.
std::string fileName() const
std::string absFilePath() const
void addImageFile(const DString &name)
Container class representing a vector of objects with keys.
Text streaming class that buffers data.
void flush()
Flushes the buffer.
Class managing a pool of worker threads.
auto queue(F &&f, Args &&... args) -> std::future< decltype(f(args...))>
Queue the callable function f for the threads to execute.
Class representing a regular expression.
Object representing the matching results.
#define Config_getInt(name)
#define Config_getString(name)
#define Config_getEnum(name)
std::vector< int > IntVector
std::vector< std::string > StringVector
const char * qPrint(const char *s)
void writeExtraLatexPackages(TextStream &t)
void writeLatexSpecialFormulaChars(TextStream &t)
#define warn_uncond(fmt,...)
std::ifstream openInputStream(const DString &name, bool binary=false, bool openAtEnd=false)
const char * ghostScriptCommand()
int system(const DString &command, const DString &args, bool commandHasConsole=true)
std::ofstream openOutputStream(const DString &name, bool append=false)
bool checkForExecutable(const DString &fileName)
bool search(std::string_view str, Match &match, const Ex &re, size_t pos)
Search in a given string str starting at position pos for a match against regular expression re.
Portable versions of functions that are platform dependent.
DString fileToString(const DString &name, bool filter, bool isSourceCode)
bool copyFile(const DString &src, const DString &dest)
Copies the contents of file with name src to the newly created file with name dest.
A bunch of utility functions.