65 uint32_t formulaCount=0;
66 msg(
"Reading formula repository...\n");
69 std::string
prefix(
"\\_form#");
71 bool hasNextLine = !getline(f,readLine).fail();
75 int lineNr = nextLineNr;
78 while ((hasNextLine = !getline(f,readLine).fail()))
82 line +=
"\n" + readLine;
86 static const reg::Ex re_new(R
"(\\_form#(\d+)=(\d+)x(\d+):)");
88 static const reg::Ex re_old(R
"(\\_form#(\d+):)");
97 id = std::stoi(match[1].str());
98 width = std::stoi(match[2].str());
99 height = std::stoi(match[3].str());
100 text = line.substr(match.position()+match.length());
108 msg(
"old formula.repository format detected; forcing upgrade.\n");
109 p->repositoriesValid =
false;
114 warn_uncond(
"{}/formula.repository contains invalid content at line {}: found: '{}'\n",dir,lineNr,line);
115 p->repositoriesValid =
false;
119 auto it =
p->formulaIdMap.find(
id);
121 if (it!=
p->formulaIdMap.end())
123 formula = it->second;
124 if (formula->
text().
str()!=text)
126 msg(
"differences detected between formula.repository files; forcing upgrade.\n");
127 p->repositoriesValid =
false;
135 formula =
p->formulas.add(text,
id,width,height);
136 p->formulaIdMap.emplace(
id,formula);
142 formImgName.
sprintf(
"form_%d",formula->
id());
143 FileInfo fiPng((dir+
"/"+formImgName+
".png").str());
144 FileInfo fiSvg((dir+
"/"+formImgName+
".svg").str());
150 FileInfo fiPngDark((dir+
"/"+formImgName+
"_dark.png").str());
151 FileInfo fiSvgDark((dir+
"/"+formImgName+
"_dark.svg").str());
152 bool isCachedDark = fiPngDark.
exists() || fiSvgDark.
exists();
161 if (formulaCount>0 && formulaCount!=
p->formulas.size())
163 msg(
"differences detected between formula.repository files; forcing upgrade.\n");
164 p->repositoriesValid =
false;
169 p->repositoriesValid =
false;
176 if (!
p->repositoriesValid)
180 p->repositoriesValid =
true;
187 DString texName=fileName+
".tex";
192 t <<
"\\documentclass{article}\n";
193 t <<
"\\usepackage{iftex}\n";
194 t <<
"\\usepackage{ifthen}\n";
195 t <<
"\\usepackage{epsfig}\n";
196 t <<
"\\usepackage[utf8]{inputenc}\n";
197 t <<
"\\usepackage{xcolor}\n";
201 t <<
"\\color{white}\n";
202 t <<
"\\pagecolor{black}\n";
209 if (!macroFile.
empty())
213 t <<
"\\input{" << stripMacroFile <<
"}\n";
216 t <<
"\\pagestyle{empty}\n";
217 t <<
"\\begin{document}\n";
218 for (
const auto &formula :
p->formulas)
220 int id = formula->id();
224 (mode==
Mode::Dark && !formula->isCachedDark())
228 t << formula->text() <<
"\n\\pagebreak\n\n";
229 formulasToGenerate.push_back(
id);
235 t <<
"\\end{document}\n";
244 const size_t argsLen = 4096;
253 err(
"Problems running latex. Check your installation or look "
254 "for typos in {0}.tex and check {0}.log!\n",fileName);
259 if (logFile.
empty() ||
272 const size_t argsLen = 4096;
276 snprintf(args,argsLen,
"-q -D 600 -n 1 -p %d -o %s_tmp.ps %s.dvi",pageIndex,
qPrint(formBase),
qPrint(fileName));
279 err(
"Problems running dvips. Check your installation!\n");
287 const size_t argsLen = 4096;
290 snprintf(args,argsLen,
"-q -dBATCH -dNOPAUSE -P- -dNOSAFER -sDEVICE=bbox %s_tmp.ps 2>%s_tmp.epsi",
301 int *x1,
int *y1,
int *x2,
int *y2,
302 double *x1hi,
double *y1hi,
double *x2hi,
double *y2hi)
304 FileInfo fi((formBase+
"_tmp.epsi").str());
310 sscanf(eps.
data()+i,
"%%%%BoundingBox:%d %d %d %d",x1,y1,x2,y2);
314 err(
"Couldn't extract bounding box from {}_tmp.epsi\n",formBase);
319 sscanf(eps.
data()+i,
"%%%%HiResBoundingBox:%lf %lf %lf %lf",x1hi,y1hi,x2hi,y2hi);
323 err(
"Couldn't extract high resolution bounding box from {}_tmp.epsi\n",formBase);
335 double scaleFactor = 1.25;
337 if (zoomFactor<8 || zoomFactor>50) zoomFactor=10;
338 scaleFactor *= zoomFactor/10.0;
343 formula->
setWidth(
static_cast<int>((x2-x1)*scaleFactor+0.5));
344 formula->
setHeight(
static_cast<int>((y2-y1)*scaleFactor+0.5));
351 const size_t argsLen = 4096;
354 snprintf(args,argsLen,
"-q -dBATCH -dNOPAUSE -P- -dNOSAFER -sDEVICE=pdfwrite"
355 " -o %s_tmp.pdf -c \"[/CropBox [%d %d %d %d] /PAGES pdfmark\" -f %s_tmp.ps",
367 const size_t argsLen = 4096;
370 snprintf(args,argsLen,
"-q -dBATCH -dNOPAUSE -P- -dNOSAFER -sDEVICE=eps2write"
371 " -o %s_tmp.eps -f %s_tmp.ps",
qPrint(formBase),
qPrint(formBase));
382 const size_t argsLen = 4096;
384 snprintf(args,argsLen,
"%s_tmp.pdf %s",
qPrint(formBase),
qPrint(outFile));
387 err(
"Problems running pdf2svg. Check your installation!\n");
395 const size_t argsLen = 4096;
398 if (inkscapeVersion == -1)
400 err(
"Problems determining the version of inkscape. Check your installation!\n");
403 else if (inkscapeVersion == 0)
413 err(
"Problems running inkscape. Check your installation!\n");
421 int x1,
int y1,
int x2,
int y2,
422 double x1hi,
double y1hi,
double x2hi,
double y2hi)
428 if (epsIn.is_open() && epsOut.is_open())
431 while (getline(epsIn,line))
433 if (line.rfind(
"%%BoundingBox",0)==0)
435 epsOut <<
"%%BoundingBox: " << std::max(0,x1-1) <<
" " << std::max(0,y1-1) <<
" " << (x2+1) <<
" " << (y2+1) <<
"\n";
437 else if (line.rfind(
"%%HiResBoundingBox",0)==0)
439 epsOut <<
"%%HiResBoundingBox: " << std::max(0.0,x1hi-1.0) <<
" " << std::max(0.0,y1hi-1.0) <<
" " << (x2hi+1.0) <<
" " << (y2hi+1.0) <<
"\n";
443 epsOut << line <<
"\n";
451 err(
"Problems correcting the eps files from {}_tmp.eps to {}_tmp_corr.eps\n",
460 const size_t argsLen = 4096;
462 snprintf(args,argsLen,
"-q -dNOSAFER -dBATCH -dNOPAUSE -dEPSCrop -sDEVICE=pngalpha -dGraphicsAlphaBits=4 -dTextAlphaBits=4 "
463 "-r%d -sOutputFile=%s %s_tmp_corr.eps",
static_cast<int>(scaleFactor*72),
qPrint(outFile),
qPrint(formBase));
478 msg(
"Generating image {} for formula\n",outputFile);
485 int x1=0,y1=0,x2=0,y2=0;
486 double x1hi=0.0,y1hi=0.0,x2hi=0.0,y2hi=0.0;
491 if (!
extractBoundingBox(formBase,&x1,&y1,&x2,&y2,&x1hi,&y1hi,&x2hi,&y2hi))
return tempFiles;
497 formBaseLight.
sprintf(
"_form%d",pageNum);
498 if (!
extractBoundingBox(formBaseLight,&x1,&y1,&x2,&y2,&x1hi,&y1hi,&x2hi,&y2hi))
return tempFiles;
519 err(
"Neither 'pdf2svg' nor 'inkscape' present for conversion of formula to 'svg'\n");
523 tempFiles.push_back(formBase.
str()+
"_tmp.pdf");
537 if (!
createPNG(formBase,outputFile,scaleFactor))
return tempFiles;
539 tempFiles.push_back(formBase.
str()+
"_tmp.eps");
540 tempFiles.push_back(formBase.
str()+
"_tmp_corr.eps");
544 tempFiles.push_back(formBase.
str()+
"_tmp.ps");
547 tempFiles.push_back(formBase.
str()+
"_tmp.epsi");
556 createLatexFile(formulaFileName,format,mode,formulasToGenerate,toIndex);
558 if (!formulasToGenerate.empty())
562 auto getFormula = [
this](
int pageNum) ->
Formula *
564 auto it =
p->formulaIdMap.find(pageNum);
565 if (it!=
p->formulaIdMap.end())
573 std::size_t numThreads =
static_cast<std::size_t
>(
Config_getInt(NUM_PROC_THREADS));
577 std::vector< std::future< StringVector > > results;
578 for (
int pageNum : formulasToGenerate)
581 auto formula = getFormula(pageNum);
584 return generateFormula(thisDir,formulaFileName,formula,pageNum,pageIndex,format,hd,mode);
586 results.emplace_back(threadPool.
queue(processFormula));
589 for (
auto &f : results)
592 p->tempFiles.insert(
p->tempFiles.end(),tf.begin(),tf.end());
597 for (
int pageNum : formulasToGenerate)
600 auto formula = getFormula(pageNum);
602 p->tempFiles.insert(
p->tempFiles.end(),tf.begin(),tf.end());
608 p->tempFiles.push_back(formulaFileName.
str()+
".dvi");
609 p->tempFiles.push_back(formulaFileName.
str()+
".log");
610 p->tempFiles.push_back(formulaFileName.
str()+
".aux");
613 p->tempFiles.push_back(formulaFileName.
str()+
".tex");
623 for (
const auto &formula :
p->formulas)
625 t <<
"\\_form#" << formula->id();
626 if (formula->width()!=-1 && formula->height()!=-1)
628 t <<
"=" << formula->width() <<
"x" << formula->height();
630 t <<
":" << formula->text() <<
"\n";
641 term(
"Output directory '{}' does not exist!\n",path);
647 if (!macroFile.
empty())
658 if (!macroFile.
empty())
665 if (toIndex &&
Config_getEnum(HTML_COLORSTYLE)!=HTML_COLORSTYLE_t::LIGHT)
675 for (
const auto &file :
p->tempFiles)
688 p->formulaIdMap.clear();
693 Formula *formula =
p->formulas.find(formulaText);
696 return formula->
id();
699 int id =
static_cast<int>(
p->formulas.size());
700 formula =
p->formulas.add(formulaText,
id,width,height);
701 p->formulaIdMap.emplace(
id,formula);
707 auto it =
p->formulaIdMap.find(formulaId);
708 return it !=
p->formulaIdMap.end() ? it->second :
nullptr;
714 auto it =
p->formulaIdMap.find(formulaId);
715 return it !=
p->formulaIdMap.end() ? it->second :
nullptr;
722 return !
p->formulas.empty();
735 static int inkscapeVersion = -2;
736 if (inkscapeVersion == -2)
738 DString inkscapeVersionFile =
"inkscape_version" ;
739 inkscapeVersion = -1;
752 if (inkscapeVersionIn.is_open())
755 while (getline(inkscapeVersionIn,line))
757 size_t dotPos = line.find(
'.');
758 if (line.rfind(
"Inkscape ",0)==0 && dotPos>0)
761 inkscapeVersion = std::stoi(line.substr(9,dotPos-9));
765 inkscapeVersionIn.close();
773 thisDir.
remove(inkscapeVersionFile.
str());
776 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.