Root/
Source at commit 1322 created 12 years 7 months ago. By meklort, Add doxygen to utils folder | |
---|---|
1 | /******************************************************************************␊ |
2 | * ␊ |
3 | *␊ |
4 | * Copyright (C) 1997-2011 by Dimitri van Heesch.␊ |
5 | *␊ |
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.␊ |
11 | *␊ |
12 | * Documents produced by Doxygen are derivative works derived from the␊ |
13 | * input used in their production; they are not affected by this license.␊ |
14 | *␊ |
15 | */␊ |
16 | ␊ |
17 | #include <stdlib.h>␊ |
18 | #include <unistd.h>␊ |
19 | ␊ |
20 | #include "qtbc.h"␊ |
21 | #include <qfile.h>␊ |
22 | #include <qfileinfo.h>␊ |
23 | #include <qtextstream.h>␊ |
24 | #include <qdir.h>␊ |
25 | ␊ |
26 | #include "formula.h"␊ |
27 | #include "image.h"␊ |
28 | #include "util.h"␊ |
29 | #include "message.h"␊ |
30 | #include "config.h"␊ |
31 | #include "portable.h"␊ |
32 | #include "index.h"␊ |
33 | #include "doxygen.h"␊ |
34 | #include "ftextstream.h"␊ |
35 | ␊ |
36 | Formula::Formula(const char *text)␊ |
37 | {␊ |
38 | static int count=0;␊ |
39 | number = count++;␊ |
40 | form=text;␊ |
41 | }␊ |
42 | ␊ |
43 | Formula::~Formula()␊ |
44 | {␊ |
45 | }␊ |
46 | ␊ |
47 | int Formula::getId()␊ |
48 | {␊ |
49 | return number;␊ |
50 | }␊ |
51 | ␊ |
52 | void FormulaList::generateBitmaps(const char *path)␊ |
53 | {␊ |
54 | int x1,y1,x2,y2;␊ |
55 | QDir d(path);␊ |
56 | // store the original directory␊ |
57 | if (!d.exists()) { err("error: Output dir %s does not exist!\n",path); exit(1); }␊ |
58 | QCString oldDir = convertToQCString(QDir::currentDirPath());␊ |
59 | // go to the html output directory (i.e. path)␊ |
60 | QDir::setCurrent(d.absPath());␊ |
61 | QDir thisDir;␊ |
62 | // generate a latex file containing one formula per page.␊ |
63 | QCString texName="_formulas.tex";␊ |
64 | QList<int> pagesToGenerate;␊ |
65 | pagesToGenerate.setAutoDelete(TRUE);␊ |
66 | FormulaListIterator fli(*this);␊ |
67 | Formula *formula;␊ |
68 | QFile f(texName);␊ |
69 | bool formulaError=FALSE;␊ |
70 | if (f.open(IO_WriteOnly))␊ |
71 | {␊ |
72 | FTextStream t(&f);␊ |
73 | if (Config_getBool("LATEX_BATCHMODE")) t << "\\batchmode" << endl;␊ |
74 | t << "\\documentclass{article}" << endl;␊ |
75 | t << "\\usepackage{epsfig}" << endl; // for those who want to include images␊ |
76 | const char *s=Config_getList("EXTRA_PACKAGES").first();␊ |
77 | while (s)␊ |
78 | {␊ |
79 | t << "\\usepackage{" << s << "}\n";␊ |
80 | s=Config_getList("EXTRA_PACKAGES").next();␊ |
81 | }␊ |
82 | t << "\\pagestyle{empty}" << endl; ␊ |
83 | t << "\\begin{document}" << endl;␊ |
84 | int page=0;␊ |
85 | for (fli.toFirst();(formula=fli.current());++fli)␊ |
86 | {␊ |
87 | QCString resultName;␊ |
88 | resultName.sprintf("form_%d.png",formula->getId());␊ |
89 | // only formulas for which no image exists are generated␊ |
90 | QFileInfo fi(resultName);␊ |
91 | if (!fi.exists())␊ |
92 | {␊ |
93 | // we force a pagebreak after each formula␊ |
94 | t << formula->getFormulaText() << endl << "\\pagebreak\n\n";␊ |
95 | pagesToGenerate.append(new int(page));␊ |
96 | }␊ |
97 | Doxygen::indexList.addImageFile(resultName);␊ |
98 | page++;␊ |
99 | }␊ |
100 | t << "\\end{document}" << endl;␊ |
101 | f.close();␊ |
102 | }␊ |
103 | if (pagesToGenerate.count()>0) // there are new formulas␊ |
104 | {␊ |
105 | //printf("Running latex...\n");␊ |
106 | //system("latex _formulas.tex </dev/null >/dev/null");␊ |
107 | QCString latexCmd = Config_getString("LATEX_CMD_NAME");␊ |
108 | if (latexCmd.isEmpty()) latexCmd="latex";␊ |
109 | portable_sysTimerStart();␊ |
110 | if (portable_system(latexCmd,"_formulas.tex")!=0)␊ |
111 | {␊ |
112 | err("Problems running latex. Check your installation or look "␊ |
113 | "for typos in _formulas.tex and check _formulas.log!\n");␊ |
114 | formulaError=TRUE;␊ |
115 | //return;␊ |
116 | }␊ |
117 | portable_sysTimerStop();␊ |
118 | //printf("Running dvips...\n");␊ |
119 | QListIterator<int> pli(pagesToGenerate);␊ |
120 | int *pagePtr;␊ |
121 | int pageIndex=1;␊ |
122 | for (;(pagePtr=pli.current());++pli,++pageIndex)␊ |
123 | {␊ |
124 | int pageNum=*pagePtr;␊ |
125 | msg("Generating image form_%d.png for formula\n",pageNum);␊ |
126 | char dviArgs[4096];␊ |
127 | QCString formBase;␊ |
128 | formBase.sprintf("_form%d",pageNum);␊ |
129 | // run dvips to convert the page with number pageIndex to an␊ |
130 | // encapsulated postscript.␊ |
131 | sprintf(dviArgs,"-q -D 600 -E -n 1 -p %d -o %s.eps _formulas.dvi",␊ |
132 | pageIndex,formBase.data());␊ |
133 | portable_sysTimerStart();␊ |
134 | if (portable_system("dvips",dviArgs)!=0)␊ |
135 | {␊ |
136 | err("Problems running dvips. Check your installation!\n");␊ |
137 | portable_sysTimerStop();␊ |
138 | return;␊ |
139 | }␊ |
140 | portable_sysTimerStop();␊ |
141 | // now we read the generated postscript file to extract the bounding box␊ |
142 | QFileInfo fi(formBase+".eps");␊ |
143 | if (fi.exists())␊ |
144 | {␊ |
145 | QCString eps = fileToString(formBase+".eps");␊ |
146 | int i=eps.find("%%BoundingBox:");␊ |
147 | if (i!=-1)␊ |
148 | {␊ |
149 | sscanf(eps.data()+i,"%%%%BoundingBox:%d %d %d %d",&x1,&y1,&x2,&y2);␊ |
150 | }␊ |
151 | else␊ |
152 | {␊ |
153 | err("error: Couldn't extract bounding box!\n");␊ |
154 | }␊ |
155 | } ␊ |
156 | // next we generate a postscript file which contains the eps␊ |
157 | // and displays it in the right colors and the right bounding box␊ |
158 | f.setName(formBase+".ps");␊ |
159 | if (f.open(IO_WriteOnly))␊ |
160 | {␊ |
161 | FTextStream t(&f);␊ |
162 | t << "1 1 1 setrgbcolor" << endl; // anti-alias to white background␊ |
163 | t << "newpath" << endl;␊ |
164 | t << "-1 -1 moveto" << endl;␊ |
165 | t << (x2-x1+2) << " -1 lineto" << endl;␊ |
166 | t << (x2-x1+2) << " " << (y2-y1+2) << " lineto" << endl;␊ |
167 | t << "-1 " << (y2-y1+2) << " lineto" <<endl;␊ |
168 | t << "closepath" << endl;␊ |
169 | t << "fill" << endl;␊ |
170 | t << -x1 << " " << -y1 << " translate" << endl;␊ |
171 | t << "0 0 0 setrgbcolor" << endl;␊ |
172 | t << "(" << formBase << ".eps) run" << endl;␊ |
173 | f.close();␊ |
174 | }␊ |
175 | // scale the image so that it is four times larger than needed.␊ |
176 | // and the sizes are a multiple of four.␊ |
177 | double scaleFactor = 16.0/3.0; ␊ |
178 | int zoomFactor = Config_getInt("FORMULA_FONTSIZE");␊ |
179 | if (zoomFactor<8 || zoomFactor>50) zoomFactor=10;␊ |
180 | scaleFactor *= zoomFactor/10.0;␊ |
181 | int gx = (((int)((x2-x1)*scaleFactor))+3)&~1;␊ |
182 | int gy = (((int)((y2-y1)*scaleFactor))+3)&~1;␊ |
183 | // Then we run ghostscript to convert the postscript to a pixmap␊ |
184 | // The pixmap is a truecolor image, where only black and white are␊ |
185 | // used. ␊ |
186 | ␊ |
187 | char gsArgs[4096];␊ |
188 | sprintf(gsArgs,"-q -g%dx%d -r%dx%dx -sDEVICE=ppmraw "␊ |
189 | "-sOutputFile=%s.pnm -dNOPAUSE -dBATCH -- %s.ps",␊ |
190 | gx,gy,(int)(scaleFactor*72),(int)(scaleFactor*72),␊ |
191 | formBase.data(),formBase.data()␊ |
192 | );␊ |
193 | portable_sysTimerStart();␊ |
194 | if (portable_system(portable_ghostScriptCommand(),gsArgs)!=0)␊ |
195 | {␊ |
196 | err("Problem running ghostscript %s %s. Check your installation!\n",portable_ghostScriptCommand(),gsArgs);␊ |
197 | portable_sysTimerStop();␊ |
198 | return;␊ |
199 | }␊ |
200 | portable_sysTimerStop();␊ |
201 | f.setName(formBase+".pnm");␊ |
202 | uint imageX=0,imageY=0;␊ |
203 | // we read the generated image again, to obtain the pixel data.␊ |
204 | if (f.open(IO_ReadOnly))␊ |
205 | {␊ |
206 | QTextStream t(&f);␊ |
207 | QCString s;␊ |
208 | if (!t.eof())␊ |
209 | s=t.readLine();␊ |
210 | if (s.length()<2 || s.left(2)!="P6")␊ |
211 | err("error: ghostscript produced an illegal image format!");␊ |
212 | else␊ |
213 | {␊ |
214 | // assume the size if after the first line that does not start with␊ |
215 | // # excluding the first line of the file.␊ |
216 | while (!t.eof() && (s=t.readLine()) && !s.isEmpty() && s.at(0)=='#') { }␊ |
217 | sscanf(s,"%d %d",&imageX,&imageY);␊ |
218 | }␊ |
219 | if (imageX>0 && imageY>0)␊ |
220 | {␊ |
221 | //printf("Converting image...\n");␊ |
222 | char *data = new char[imageX*imageY*3]; // rgb 8:8:8 format␊ |
223 | uint i,x,y,ix,iy;␊ |
224 | f.readBlock(data,imageX*imageY*3);␊ |
225 | Image srcImage(imageX,imageY),␊ |
226 | filteredImage(imageX,imageY),␊ |
227 | dstImage(imageX/4,imageY/4);␊ |
228 | uchar *ps=srcImage.getData();␊ |
229 | // convert image to black (1) and white (0) index.␊ |
230 | for (i=0;i<imageX*imageY;i++) *ps++= (data[i*3]==0 ? 1 : 0);␊ |
231 | // apply a simple box filter to the image ␊ |
232 | static int filterMask[]={1,2,1,2,8,2,1,2,1};␊ |
233 | for (y=0;y<srcImage.getHeight();y++)␊ |
234 | {␊ |
235 | for (x=0;x<srcImage.getWidth();x++)␊ |
236 | {␊ |
237 | int s=0;␊ |
238 | for (iy=0;iy<2;iy++)␊ |
239 | {␊ |
240 | for (ix=0;ix<2;ix++)␊ |
241 | {␊ |
242 | s+=srcImage.getPixel(x+ix-1,y+iy-1)*filterMask[iy*3+ix];␊ |
243 | }␊ |
244 | }␊ |
245 | filteredImage.setPixel(x,y,s);␊ |
246 | }␊ |
247 | }␊ |
248 | // down-sample the image to 1/16th of the area using 16 gray scale␊ |
249 | // colors.␊ |
250 | // TODO: optimize this code.␊ |
251 | for (y=0;y<dstImage.getHeight();y++)␊ |
252 | {␊ |
253 | for (x=0;x<dstImage.getWidth();x++)␊ |
254 | {␊ |
255 | int xp=x<<2;␊ |
256 | int yp=y<<2;␊ |
257 | int c=filteredImage.getPixel(xp+0,yp+0)+␊ |
258 | filteredImage.getPixel(xp+1,yp+0)+␊ |
259 | filteredImage.getPixel(xp+2,yp+0)+␊ |
260 | filteredImage.getPixel(xp+3,yp+0)+␊ |
261 | filteredImage.getPixel(xp+0,yp+1)+␊ |
262 | filteredImage.getPixel(xp+1,yp+1)+␊ |
263 | filteredImage.getPixel(xp+2,yp+1)+␊ |
264 | filteredImage.getPixel(xp+3,yp+1)+␊ |
265 | filteredImage.getPixel(xp+0,yp+2)+␊ |
266 | filteredImage.getPixel(xp+1,yp+2)+␊ |
267 | filteredImage.getPixel(xp+2,yp+2)+␊ |
268 | filteredImage.getPixel(xp+3,yp+2)+␊ |
269 | filteredImage.getPixel(xp+0,yp+3)+␊ |
270 | filteredImage.getPixel(xp+1,yp+3)+␊ |
271 | filteredImage.getPixel(xp+2,yp+3)+␊ |
272 | filteredImage.getPixel(xp+3,yp+3);␊ |
273 | // here we scale and clip the color value so the␊ |
274 | // resulting image has a reasonable contrast␊ |
275 | dstImage.setPixel(x,y,QMIN(15,(c*15)/(16*10)));␊ |
276 | }␊ |
277 | }␊ |
278 | // save the result as a bitmap␊ |
279 | QCString resultName;␊ |
280 | resultName.sprintf("form_%d.png",pageNum);␊ |
281 | // the option parameter 1 is used here as a temporary hack␊ |
282 | // to select the right color palette! ␊ |
283 | dstImage.save(resultName,1);␊ |
284 | delete[] data;␊ |
285 | }␊ |
286 | f.close();␊ |
287 | } ␊ |
288 | // remove intermediate image files␊ |
289 | thisDir.remove(formBase+".eps");␊ |
290 | thisDir.remove(formBase+".pnm");␊ |
291 | thisDir.remove(formBase+".ps");␊ |
292 | }␊ |
293 | // remove intermediate files produced by latex␊ |
294 | thisDir.remove("_formulas.dvi");␊ |
295 | if (!formulaError) thisDir.remove("_formulas.log"); // keep file in case of errors␊ |
296 | thisDir.remove("_formulas.aux");␊ |
297 | }␊ |
298 | // remove the latex file itself␊ |
299 | if (!formulaError) thisDir.remove("_formulas.tex");␊ |
300 | // write/update the formula repository so we know what text the ␊ |
301 | // generated images represent (we use this next time to avoid regeneration␊ |
302 | // of the images, and to avoid forcing the user to delete all images in order␊ |
303 | // to let a browser refresh the images).␊ |
304 | f.setName("formula.repository");␊ |
305 | if (f.open(IO_WriteOnly))␊ |
306 | {␊ |
307 | FTextStream t(&f);␊ |
308 | for (fli.toFirst();(formula=fli.current());++fli)␊ |
309 | {␊ |
310 | t << "\\form#" << formula->getId() << ":" << formula->getFormulaText() << endl;␊ |
311 | }␊ |
312 | f.close();␊ |
313 | }␊ |
314 | // reset the directory to the original location.␊ |
315 | QDir::setCurrent(oldDir);␊ |
316 | }␊ |
317 | ␊ |
318 | ␊ |
319 | #ifdef FORMULA_TEST␊ |
320 | int main()␊ |
321 | {␊ |
322 | FormulaList fl;␊ |
323 | fl.append(new Formula("$x^2$"));␊ |
324 | fl.append(new Formula("$y^2$"));␊ |
325 | fl.append(new Formula("$\\sqrt{x_0^2+x_1^2+x_2^2}$"));␊ |
326 | fl.generateBitmaps("dest");␊ |
327 | return 0;␊ |
328 | }␊ |
329 | #endif␊ |
330 |