1 /******************************************************************************
4 * Copyright (C) 1997-2015 by Dimitri van Heesch.
6 * Permission to use, copy, modify, and distribute this software and its
7 * documentation under the terms of the GNU General Public License is hereby
8 * granted. No representations are made about the suitability of this software
9 * for any purpose. It is provided "as is" without express or implied warranty.
10 * See the GNU General Public License for more details.
12 * Documents produced by Doxygen are derivative works derived from the
13 * input used in their production; they are not affected by this license.
19 #include <qfileinfo.h>
20 #include <qtextstream.h>
31 #include "ftextstream.h"
33 Formula::Formula(const char *text)
49 void FormulaList::generateBitmaps(const char *path)
53 // store the original directory
54 if (!d.exists()) { err("Output dir %s does not exist!\n",path); exit(1); }
55 QCString oldDir = QDir::currentDirPath().utf8();
56 // go to the html output directory (i.e. path)
57 QDir::setCurrent(d.absPath());
59 // generate a latex file containing one formula per page.
60 QCString texName="_formulas.tex";
61 QList<int> pagesToGenerate;
62 pagesToGenerate.setAutoDelete(TRUE);
63 FormulaListIterator fli(*this);
66 bool formulaError=FALSE;
67 if (f.open(IO_WriteOnly))
70 if (Config_getBool(LATEX_BATCHMODE)) t << "\\batchmode" << endl;
71 t << "\\documentclass{article}" << endl;
72 t << "\\usepackage{ifthen}" << endl;
73 t << "\\usepackage{epsfig}" << endl; // for those who want to include images
74 writeExtraLatexPackages(t);
75 writeLatexSpecialFormulaChars(t);
76 t << "\\pagestyle{empty}" << endl;
77 t << "\\begin{document}" << endl;
79 for (fli.toFirst();(formula=fli.current());++fli)
82 resultName.sprintf("form_%d.png",formula->getId());
83 // only formulas for which no image exists are generated
84 QFileInfo fi(resultName);
87 // we force a pagebreak after each formula
88 t << formula->getFormulaText() << endl << "\\pagebreak\n\n";
89 pagesToGenerate.append(new int(page));
91 Doxygen::indexList->addImageFile(resultName);
94 t << "\\end{document}" << endl;
97 if (pagesToGenerate.count()>0) // there are new formulas
99 //printf("Running latex...\n");
100 //system("latex _formulas.tex </dev/null >/dev/null");
101 QCString latexCmd = "latex";
102 portable_sysTimerStart();
103 if (portable_system(latexCmd,"_formulas.tex")!=0)
105 err("Problems running latex. Check your installation or look "
106 "for typos in _formulas.tex and check _formulas.log!\n");
110 portable_sysTimerStop();
111 //printf("Running dvips...\n");
112 QListIterator<int> pli(pagesToGenerate);
115 for (;(pagePtr=pli.current());++pli,++pageIndex)
117 int pageNum=*pagePtr;
118 msg("Generating image form_%d.png for formula\n",pageNum);
121 formBase.sprintf("_form%d",pageNum);
122 // run dvips to convert the page with number pageIndex to an
123 // encapsulated postscript.
124 sprintf(dviArgs,"-q -D 600 -E -n 1 -p %d -o %s.eps _formulas.dvi",
125 pageIndex,formBase.data());
126 portable_sysTimerStart();
127 if (portable_system("dvips",dviArgs)!=0)
129 err("Problems running dvips. Check your installation!\n");
130 portable_sysTimerStop();
131 QDir::setCurrent(oldDir);
134 portable_sysTimerStop();
135 // now we read the generated postscript file to extract the bounding box
136 QFileInfo fi(formBase+".eps");
139 QCString eps = fileToString(formBase+".eps");
140 int i=eps.find("%%BoundingBox:");
143 sscanf(eps.data()+i,"%%%%BoundingBox:%d %d %d %d",&x1,&y1,&x2,&y2);
147 err("Couldn't extract bounding box!\n");
150 // next we generate a postscript file which contains the eps
151 // and displays it in the right colors and the right bounding box
152 f.setName(formBase+".ps");
153 if (f.open(IO_WriteOnly))
156 t << "1 1 1 setrgbcolor" << endl; // anti-alias to white background
157 t << "newpath" << endl;
158 t << "-1 -1 moveto" << endl;
159 t << (x2-x1+2) << " -1 lineto" << endl;
160 t << (x2-x1+2) << " " << (y2-y1+2) << " lineto" << endl;
161 t << "-1 " << (y2-y1+2) << " lineto" <<endl;
162 t << "closepath" << endl;
164 t << -x1 << " " << -y1 << " translate" << endl;
165 t << "0 0 0 setrgbcolor" << endl;
166 t << "(" << formBase << ".eps) run" << endl;
169 // scale the image so that it is four times larger than needed.
170 // and the sizes are a multiple of four.
171 double scaleFactor = 16.0/3.0;
172 int zoomFactor = Config_getInt(FORMULA_FONTSIZE);
173 if (zoomFactor<8 || zoomFactor>50) zoomFactor=10;
174 scaleFactor *= zoomFactor/10.0;
175 int gx = (((int)((x2-x1)*scaleFactor))+3)&~1;
176 int gy = (((int)((y2-y1)*scaleFactor))+3)&~1;
177 // Then we run ghostscript to convert the postscript to a pixmap
178 // The pixmap is a truecolor image, where only black and white are
182 sprintf(gsArgs,"-q -g%dx%d -r%dx%dx -sDEVICE=ppmraw "
183 "-sOutputFile=%s.pnm -dNOPAUSE -dBATCH -- %s.ps",
184 gx,gy,(int)(scaleFactor*72),(int)(scaleFactor*72),
185 formBase.data(),formBase.data()
187 portable_sysTimerStart();
188 if (portable_system(portable_ghostScriptCommand(),gsArgs)!=0)
190 err("Problem running ghostscript %s %s. Check your installation!\n",portable_ghostScriptCommand(),gsArgs);
191 portable_sysTimerStop();
192 QDir::setCurrent(oldDir);
195 portable_sysTimerStop();
196 f.setName(formBase+".pnm");
197 uint imageX=0,imageY=0;
198 // we read the generated image again, to obtain the pixel data.
199 if (f.open(IO_ReadOnly))
204 s=t.readLine().utf8();
205 if (s.length()<2 || s.left(2)!="P6")
206 err("ghostscript produced an illegal image format!");
209 // assume the size is after the first line that does not start with
210 // # excluding the first line of the file.
211 while (!t.eof() && (s=t.readLine().utf8()) && !s.isEmpty() && s.at(0)=='#') { }
212 sscanf(s,"%d %d",&imageX,&imageY);
214 if (imageX>0 && imageY>0)
216 //printf("Converting image...\n");
217 char *data = new char[imageX*imageY*3]; // rgb 8:8:8 format
219 f.readBlock(data,imageX*imageY*3);
220 Image srcImage(imageX,imageY),
221 filteredImage(imageX,imageY),
222 dstImage(imageX/4,imageY/4);
223 uchar *ps=srcImage.getData();
224 // convert image to black (1) and white (0) index.
225 for (i=0;i<imageX*imageY;i++) *ps++= (data[i*3]==0 ? 1 : 0);
226 // apply a simple box filter to the image
227 static int filterMask[]={1,2,1,2,8,2,1,2,1};
228 for (y=0;y<srcImage.getHeight();y++)
230 for (x=0;x<srcImage.getWidth();x++)
237 s+=srcImage.getPixel(x+ix-1,y+iy-1)*filterMask[iy*3+ix];
240 filteredImage.setPixel(x,y,s);
243 // down-sample the image to 1/16th of the area using 16 gray scale
245 // TODO: optimize this code.
246 for (y=0;y<dstImage.getHeight();y++)
248 for (x=0;x<dstImage.getWidth();x++)
252 int c=filteredImage.getPixel(xp+0,yp+0)+
253 filteredImage.getPixel(xp+1,yp+0)+
254 filteredImage.getPixel(xp+2,yp+0)+
255 filteredImage.getPixel(xp+3,yp+0)+
256 filteredImage.getPixel(xp+0,yp+1)+
257 filteredImage.getPixel(xp+1,yp+1)+
258 filteredImage.getPixel(xp+2,yp+1)+
259 filteredImage.getPixel(xp+3,yp+1)+
260 filteredImage.getPixel(xp+0,yp+2)+
261 filteredImage.getPixel(xp+1,yp+2)+
262 filteredImage.getPixel(xp+2,yp+2)+
263 filteredImage.getPixel(xp+3,yp+2)+
264 filteredImage.getPixel(xp+0,yp+3)+
265 filteredImage.getPixel(xp+1,yp+3)+
266 filteredImage.getPixel(xp+2,yp+3)+
267 filteredImage.getPixel(xp+3,yp+3);
268 // here we scale and clip the color value so the
269 // resulting image has a reasonable contrast
270 dstImage.setPixel(x,y,QMIN(15,(c*15)/(16*10)));
273 // save the result as a bitmap
275 resultName.sprintf("form_%d.png",pageNum);
276 // the option parameter 1 is used here as a temporary hack
277 // to select the right color palette!
278 dstImage.save(resultName,1);
283 // remove intermediate image files
284 thisDir.remove(formBase+".eps");
285 thisDir.remove(formBase+".pnm");
286 thisDir.remove(formBase+".ps");
288 // remove intermediate files produced by latex
289 thisDir.remove("_formulas.dvi");
290 if (!formulaError) thisDir.remove("_formulas.log"); // keep file in case of errors
291 thisDir.remove("_formulas.aux");
293 // remove the latex file itself
294 if (!formulaError) thisDir.remove("_formulas.tex");
295 // write/update the formula repository so we know what text the
296 // generated images represent (we use this next time to avoid regeneration
297 // of the images, and to avoid forcing the user to delete all images in order
298 // to let a browser refresh the images).
299 f.setName("formula.repository");
300 if (f.open(IO_WriteOnly))
303 for (fli.toFirst();(formula=fli.current());++fli)
305 t << "\\form#" << formula->getId() << ":" << formula->getFormulaText() << endl;
309 // reset the directory to the original location.
310 QDir::setCurrent(oldDir);
318 fl.append(new Formula("$x^2$"));
319 fl.append(new Formula("$y^2$"));
320 fl.append(new Formula("$\\sqrt{x_0^2+x_1^2+x_2^2}$"));
321 fl.generateBitmaps("dest");