summaryrefslogtreecommitdiffstats
path: root/src/formula.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/formula.cpp')
-rw-r--r--src/formula.cpp286
1 files changed, 286 insertions, 0 deletions
diff --git a/src/formula.cpp b/src/formula.cpp
new file mode 100644
index 0000000..53e8b38
--- /dev/null
+++ b/src/formula.cpp
@@ -0,0 +1,286 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 1997-1999 by Dimitri van Heesch.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation under the terms of the GNU General Public License is hereby
+ * granted. No representations are made about the suitability of this software
+ * for any purpose. It is provided "as is" without express or implied warranty.
+ * See the GNU General Public License for more details.
+ *
+ * All output generated with Doxygen is not covered by this license.
+ *
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <qfile.h>
+#include <qtstream.h>
+#include <qfileinf.h>
+#include <qdir.h>
+
+#include "formula.h"
+#include "image.h"
+#include "util.h"
+#include "message.h"
+
+Formula::Formula(const char *text)
+{
+ static int count=0;
+ number = count++;
+ form=text;
+}
+
+Formula::~Formula()
+{
+}
+
+int Formula::getId()
+{
+ return number;
+}
+
+void FormulaList::generateBitmaps(const char *path)
+{
+ int x1,y1,x2,y2;
+ QDir d(path);
+ // store the original directory
+ if (!d.exists()) { err("Error: Output dir %s does not exist!\n",path); exit(1); }
+ QString oldDir = QDir::currentDirPath().copy();
+ // goto the html output directory (i.e. path)
+ QDir::setCurrent(d.absPath());
+ QDir thisDir;
+ // generate a latex file containing one formula per page.
+ QString texName="_formulas.tex";
+ QList<int> pagesToGenerate;
+ pagesToGenerate.setAutoDelete(TRUE);
+ FormulaListIterator fli(*this);
+ Formula *formula;
+ QFile f(texName);
+ if (f.open(IO_WriteOnly))
+ {
+ QTextStream t(&f);
+ t << "\\documentclass{article}" << endl;
+ t << "\\usepackage{epsf}" << endl; // for those who want to include images
+ t << "\\pagestyle{empty}" << endl;
+ t << "\\begin{document}" << endl;
+ int page=0;
+ for (fli.toFirst();(formula=fli.current());++fli)
+ {
+ QString resultName;
+ resultName.sprintf("form-%d.gif",formula->getId());
+ // only formulas for which no image exists are generated
+ QFileInfo fi(resultName);
+ if (!fi.exists())
+ {
+ // we force a pagebreak after each formula
+ t << formula->getFormulaText() << endl << "\\pagebreak\n\n";
+ pagesToGenerate.append(new int(page));
+ }
+ page++;
+ }
+ t << "\\end{document}" << endl;
+ f.close();
+ }
+ if (pagesToGenerate.count()>0) // there are new formulas
+ {
+ //printf("Running latex...\n");
+ //system("latex _formulas.tex </dev/null >/dev/null");
+ system("latex _formulas.tex");
+ //printf("Running dvips...\n");
+ QListIterator<int> pli(pagesToGenerate);
+ int *pagePtr;
+ int pageIndex=1;
+ for (;(pagePtr=pli.current());++pli,++pageIndex)
+ {
+ int pageNum=*pagePtr;
+ msg("Generating image form-%d.gif for formula\n",pageNum);
+ char dviCmd[256];
+ QString formBase;
+ formBase.sprintf("_form%d",pageNum);
+ // run dvips to convert the page with number pageIndex to an
+ // encapsulated postscript.
+ sprintf(dviCmd,"dvips -q -D 600 -E -n 1 -p %d -o %s.eps _formulas.dvi",
+ pageIndex,formBase.data());
+ system(dviCmd);
+ // now we read the generated postscript file to extract the bounding box
+ QFileInfo fi(formBase+".eps");
+ if (fi.exists())
+ {
+ QString eps = fileToString(formBase+".eps");
+ int i=eps.find("%%BoundingBox:");
+ if (i!=-1)
+ {
+ sscanf(eps.data()+i,"%%%%BoundingBox:%d %d %d %d",&x1,&y1,&x2,&y2);
+ }
+ else
+ {
+ err("Error: Couldn't extract bounding box!\n");
+ }
+ }
+ // next we generate a postscript file which contains the eps
+ // and displays it in the right colors and the right bounding box
+ f.setName(formBase+".ps");
+ if (f.open(IO_WriteOnly))
+ {
+ QTextStream t(&f);
+ t << "1 1 1 setrgbcolor" << endl; // anti-alias to white background
+ t << "newpath" << endl;
+ t << "-1 -1 moveto" << endl;
+ t << x2-x1+2 << " -1 lineto" << endl;
+ t << x2-x1+2 << " " << y2-y1+2 << " lineto" << endl;
+ t << "-1 " << y2-y1+2 << " lineto" <<endl;
+ t << "closepath" << endl;
+ t << "fill" << endl;
+ t << -x1 << " " << -y1 << " translate" << endl;
+ t << "0 0 0 setrgbcolor" << endl;
+ t << "(" << formBase << ".eps) run" << endl;
+ f.close();
+ }
+ // scale the image so that it is four times larger than needed.
+ // and the sizes are a multiple of four.
+ const double scaleFactor = 16.0/3.0;
+ int gx = (((int)((x2-x1)*scaleFactor))+3)&~2;
+ int gy = (((int)((y2-y1)*scaleFactor))+3)&~2;
+ char gsCmd[256];
+ // Then we run ghostscript to convert the postscript to a pixmap
+ // The pixmap is a truecolor image, where only black and white are
+ // used.
+ sprintf(gsCmd,"gs -q -g%dx%d -r%dx%dx -sDEVICE=ppmraw "
+ "-sOutputFile=%s.pnm -DNOPAUSE -- %s.ps",
+ gx,gy,(int)(scaleFactor*72),(int)(scaleFactor*72),
+ formBase.data(),formBase.data()
+ );
+ //printf("Running ghostscript...\n");
+ system(gsCmd);
+ f.setName(formBase+".pnm");
+ uint imageX=0,imageY=0;
+ // we read the generated image again, to obtain the pixel data.
+ if (f.open(IO_ReadOnly))
+ {
+ QTextStream t(&f);
+ QString s;
+ if (!t.eof())
+ s=t.readLine();
+ if (s.length()<2 || s.left(2)!="P6")
+ err("Error: ghostscript produced an illegal image format!");
+ else
+ {
+ // assume the size if after the first line that does not start with
+ // # excluding the first line of the file.
+ while (!t.eof() && (s=t.readLine()) && !s.isEmpty() && s.at(0)=='#');
+ sscanf(s,"%d %d",&imageX,&imageY);
+ }
+ if (imageX>0 && imageY>0)
+ {
+ //printf("Converting image...\n");
+ char *data = new char[imageX*imageY*3]; // rgb 8:8:8 format
+ uint i,x,y,ix,iy;
+ f.readBlock(data,imageX*imageY*3);
+ Image srcImage(imageX,imageY),
+ filteredImage(imageX,imageY),
+ dstImage(imageX/4,imageY/4);
+ uchar *ps=srcImage.getData();
+ // convert image to black (1) and white (0) index.
+ for (i=0;i<imageX*imageY;i++) *ps++= (data[i*3]==0 ? 1 : 0);
+ // apply a simple box filter to the image
+ static int filterMask[]={1,2,1,2,8,2,1,2,1};
+ for (y=0;y<srcImage.getHeight();y++)
+ {
+ for (x=0;x<srcImage.getWidth();x++)
+ {
+ int s=0;
+ for (iy=0;iy<2;iy++)
+ {
+ for (ix=0;ix<2;ix++)
+ {
+ s+=srcImage.getPixel(x+ix-1,y+iy-1)*filterMask[iy*3+ix];
+ }
+ }
+ filteredImage.setPixel(x,y,s);
+ }
+ }
+ // down-sample the image to 1/16th of the area using 16 gray scale
+ // colors.
+ for (y=0;y<dstImage.getHeight();y++)
+ {
+ for (x=0;x<dstImage.getWidth();x++)
+ {
+ int xp=x<<2;
+ int yp=y<<2;
+ int c=filteredImage.getPixel(xp+0,yp+0)+
+ filteredImage.getPixel(xp+1,yp+0)+
+ filteredImage.getPixel(xp+2,yp+0)+
+ filteredImage.getPixel(xp+3,yp+0)+
+ filteredImage.getPixel(xp+0,yp+1)+
+ filteredImage.getPixel(xp+1,yp+1)+
+ filteredImage.getPixel(xp+2,yp+1)+
+ filteredImage.getPixel(xp+3,yp+1)+
+ filteredImage.getPixel(xp+0,yp+2)+
+ filteredImage.getPixel(xp+1,yp+2)+
+ filteredImage.getPixel(xp+2,yp+2)+
+ filteredImage.getPixel(xp+3,yp+2)+
+ filteredImage.getPixel(xp+0,yp+3)+
+ filteredImage.getPixel(xp+1,yp+3)+
+ filteredImage.getPixel(xp+2,yp+3)+
+ filteredImage.getPixel(xp+3,yp+3);
+ // here we scale and clip the color value so the
+ // resulting image has a reasonable contrast
+ dstImage.setPixel(x,y,QMIN(15,(c*15)/(16*10)));
+ }
+ }
+ // save the result as a gif
+ QString resultName;
+ resultName.sprintf("form-%d.gif",pageNum);
+ // the option parameter 1 is used here as a temporary hack
+ // to select the right color palette!
+ dstImage.save(resultName,1);
+ delete[] data;
+ }
+ f.close();
+ }
+ // remove intermediate image files
+ thisDir.remove(formBase+".eps");
+ thisDir.remove(formBase+".pnm");
+ thisDir.remove(formBase+".ps");
+ }
+ // remove intermediate files produced by latex
+ thisDir.remove("_formulas.dvi");
+ thisDir.remove("_formulas.log");
+ thisDir.remove("_formulas.aux");
+ }
+ // remove the latex file itself
+ thisDir.remove("_formulas.tex");
+ // write/update the formula repository so we know what text the
+ // generated gifs represent (we use this next time to avoid regeneration
+ // of the gifs, and to avoid forcing the user to delete all gifs in order
+ // to let a browser refresh the images).
+ f.setName("formula.repository");
+ if (f.open(IO_WriteOnly))
+ {
+ QTextStream t(&f);
+ for (fli.toFirst();(formula=fli.current());++fli)
+ {
+ t << "\\form#" << formula->getId() << ":" << formula->getFormulaText() << endl;
+ }
+ f.close();
+ }
+ // reset the directory to the original location.
+ QDir::setCurrent(oldDir);
+}
+
+
+#ifdef FORMULA_TEST
+int main()
+{
+ FormulaList fl;
+ fl.append(new Formula("$x^2$"));
+ fl.append(new Formula("$y^2$"));
+ fl.append(new Formula("$\\sqrt{x_0^2+x_1^2+x_2^2}$"));
+ fl.generateBitmaps("dest");
+ return 0;
+}
+#endif