|
Packit |
1c1d7e |
/******************************************************************************
|
|
Packit |
1c1d7e |
*
|
|
Packit |
1c1d7e |
*
|
|
Packit |
1c1d7e |
* Copyright (C) 1997-2015 by Dimitri van Heesch.
|
|
Packit |
1c1d7e |
*
|
|
Packit |
1c1d7e |
* Permission to use, copy, modify, and distribute this software and its
|
|
Packit |
1c1d7e |
* documentation under the terms of the GNU General Public License is hereby
|
|
Packit |
1c1d7e |
* granted. No representations are made about the suitability of this software
|
|
Packit |
1c1d7e |
* for any purpose. It is provided "as is" without express or implied warranty.
|
|
Packit |
1c1d7e |
* See the GNU General Public License for more details.
|
|
Packit |
1c1d7e |
*
|
|
Packit |
1c1d7e |
* Documents produced by Doxygen are derivative works derived from the
|
|
Packit |
1c1d7e |
* input used in their production; they are not affected by this license.
|
|
Packit |
1c1d7e |
*
|
|
Packit |
1c1d7e |
*/
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
#include <stdlib.h>
|
|
Packit |
1c1d7e |
#include <qfile.h>
|
|
Packit |
1c1d7e |
#include <qfileinfo.h>
|
|
Packit |
1c1d7e |
#include <qtextstream.h>
|
|
Packit |
1c1d7e |
#include <qdir.h>
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
#include "formula.h"
|
|
Packit |
1c1d7e |
#include "image.h"
|
|
Packit |
1c1d7e |
#include "util.h"
|
|
Packit |
1c1d7e |
#include "message.h"
|
|
Packit |
1c1d7e |
#include "config.h"
|
|
Packit |
1c1d7e |
#include "portable.h"
|
|
Packit |
1c1d7e |
#include "index.h"
|
|
Packit |
1c1d7e |
#include "doxygen.h"
|
|
Packit |
1c1d7e |
#include "ftextstream.h"
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
Formula::Formula(const char *text)
|
|
Packit |
1c1d7e |
{
|
|
Packit |
1c1d7e |
static int count=0;
|
|
Packit |
1c1d7e |
number = count++;
|
|
Packit |
1c1d7e |
form=text;
|
|
Packit |
1c1d7e |
}
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
Formula::~Formula()
|
|
Packit |
1c1d7e |
{
|
|
Packit |
1c1d7e |
}
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
int Formula::getId()
|
|
Packit |
1c1d7e |
{
|
|
Packit |
1c1d7e |
return number;
|
|
Packit |
1c1d7e |
}
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
void FormulaList::generateBitmaps(const char *path)
|
|
Packit |
1c1d7e |
{
|
|
Packit |
1c1d7e |
int x1,y1,x2,y2;
|
|
Packit |
1c1d7e |
QDir d(path);
|
|
Packit |
1c1d7e |
// store the original directory
|
|
Packit |
1c1d7e |
if (!d.exists()) { err("Output dir %s does not exist!\n",path); exit(1); }
|
|
Packit |
1c1d7e |
QCString oldDir = QDir::currentDirPath().utf8();
|
|
Packit |
1c1d7e |
// go to the html output directory (i.e. path)
|
|
Packit |
1c1d7e |
QDir::setCurrent(d.absPath());
|
|
Packit |
1c1d7e |
QDir thisDir;
|
|
Packit |
1c1d7e |
// generate a latex file containing one formula per page.
|
|
Packit |
1c1d7e |
QCString texName="_formulas.tex";
|
|
Packit |
1c1d7e |
QList<int> pagesToGenerate;
|
|
Packit |
1c1d7e |
pagesToGenerate.setAutoDelete(TRUE);
|
|
Packit |
1c1d7e |
FormulaListIterator fli(*this);
|
|
Packit |
1c1d7e |
Formula *formula;
|
|
Packit |
1c1d7e |
QFile f(texName);
|
|
Packit |
1c1d7e |
bool formulaError=FALSE;
|
|
Packit |
1c1d7e |
if (f.open(IO_WriteOnly))
|
|
Packit |
1c1d7e |
{
|
|
Packit |
1c1d7e |
FTextStream t(&f);
|
|
Packit |
1c1d7e |
if (Config_getBool(LATEX_BATCHMODE)) t << "\\batchmode" << endl;
|
|
Packit |
1c1d7e |
t << "\\documentclass{article}" << endl;
|
|
Packit |
1c1d7e |
t << "\\usepackage{epsfig}" << endl; // for those who want to include images
|
|
Packit |
1c1d7e |
writeExtraLatexPackages(t);
|
|
Packit |
1c1d7e |
t << "\\pagestyle{empty}" << endl;
|
|
Packit |
1c1d7e |
t << "\\begin{document}" << endl;
|
|
Packit |
1c1d7e |
int page=0;
|
|
Packit |
1c1d7e |
for (fli.toFirst();(formula=fli.current());++fli)
|
|
Packit |
1c1d7e |
{
|
|
Packit |
1c1d7e |
QCString resultName;
|
|
Packit |
1c1d7e |
resultName.sprintf("form_%d.png",formula->getId());
|
|
Packit |
1c1d7e |
// only formulas for which no image exists are generated
|
|
Packit |
1c1d7e |
QFileInfo fi(resultName);
|
|
Packit |
1c1d7e |
if (!fi.exists())
|
|
Packit |
1c1d7e |
{
|
|
Packit |
1c1d7e |
// we force a pagebreak after each formula
|
|
Packit |
1c1d7e |
t << formula->getFormulaText() << endl << "\\pagebreak\n\n";
|
|
Packit |
1c1d7e |
pagesToGenerate.append(new int(page));
|
|
Packit |
1c1d7e |
}
|
|
Packit |
1c1d7e |
Doxygen::indexList->addImageFile(resultName);
|
|
Packit |
1c1d7e |
page++;
|
|
Packit |
1c1d7e |
}
|
|
Packit |
1c1d7e |
t << "\\end{document}" << endl;
|
|
Packit |
1c1d7e |
f.close();
|
|
Packit |
1c1d7e |
}
|
|
Packit |
1c1d7e |
if (pagesToGenerate.count()>0) // there are new formulas
|
|
Packit |
1c1d7e |
{
|
|
Packit |
1c1d7e |
//printf("Running latex...\n");
|
|
Packit |
1c1d7e |
//system("latex _formulas.tex </dev/null >/dev/null");
|
|
Packit |
1c1d7e |
QCString latexCmd = Config_getString(LATEX_CMD_NAME);
|
|
Packit |
1c1d7e |
if (latexCmd.isEmpty()) latexCmd="latex";
|
|
Packit |
1c1d7e |
portable_sysTimerStart();
|
|
Packit |
1c1d7e |
if (portable_system(latexCmd,"_formulas.tex")!=0)
|
|
Packit |
1c1d7e |
{
|
|
Packit |
1c1d7e |
err("Problems running latex. Check your installation or look "
|
|
Packit |
1c1d7e |
"for typos in _formulas.tex and check _formulas.log!\n");
|
|
Packit |
1c1d7e |
formulaError=TRUE;
|
|
Packit |
1c1d7e |
//return;
|
|
Packit |
1c1d7e |
}
|
|
Packit |
1c1d7e |
portable_sysTimerStop();
|
|
Packit |
1c1d7e |
//printf("Running dvips...\n");
|
|
Packit |
1c1d7e |
QListIterator<int> pli(pagesToGenerate);
|
|
Packit |
1c1d7e |
int *pagePtr;
|
|
Packit |
1c1d7e |
int pageIndex=1;
|
|
Packit |
1c1d7e |
for (;(pagePtr=pli.current());++pli,++pageIndex)
|
|
Packit |
1c1d7e |
{
|
|
Packit |
1c1d7e |
int pageNum=*pagePtr;
|
|
Packit |
1c1d7e |
msg("Generating image form_%d.png for formula\n",pageNum);
|
|
Packit |
1c1d7e |
char dviArgs[4096];
|
|
Packit |
1c1d7e |
QCString formBase;
|
|
Packit |
1c1d7e |
formBase.sprintf("_form%d",pageNum);
|
|
Packit |
1c1d7e |
// run dvips to convert the page with number pageIndex to an
|
|
Packit |
1c1d7e |
// encapsulated postscript.
|
|
Packit |
1c1d7e |
sprintf(dviArgs,"-q -D 600 -E -n 1 -p %d -o %s.eps _formulas.dvi",
|
|
Packit |
1c1d7e |
pageIndex,formBase.data());
|
|
Packit |
1c1d7e |
portable_sysTimerStart();
|
|
Packit |
1c1d7e |
if (portable_system("dvips",dviArgs)!=0)
|
|
Packit |
1c1d7e |
{
|
|
Packit |
1c1d7e |
err("Problems running dvips. Check your installation!\n");
|
|
Packit |
1c1d7e |
portable_sysTimerStop();
|
|
Packit |
1c1d7e |
QDir::setCurrent(oldDir);
|
|
Packit |
1c1d7e |
return;
|
|
Packit |
1c1d7e |
}
|
|
Packit |
1c1d7e |
portable_sysTimerStop();
|
|
Packit |
1c1d7e |
// now we read the generated postscript file to extract the bounding box
|
|
Packit |
1c1d7e |
QFileInfo fi(formBase+".eps");
|
|
Packit |
1c1d7e |
if (fi.exists())
|
|
Packit |
1c1d7e |
{
|
|
Packit |
1c1d7e |
QCString eps = fileToString(formBase+".eps");
|
|
Packit |
1c1d7e |
int i=eps.find("%%BoundingBox:");
|
|
Packit |
1c1d7e |
if (i!=-1)
|
|
Packit |
1c1d7e |
{
|
|
Packit |
1c1d7e |
sscanf(eps.data()+i,"%%%%BoundingBox:%d %d %d %d",&x1,&y1,&x2,&y2;;
|
|
Packit |
1c1d7e |
}
|
|
Packit |
1c1d7e |
else
|
|
Packit |
1c1d7e |
{
|
|
Packit |
1c1d7e |
err("Couldn't extract bounding box!\n");
|
|
Packit |
1c1d7e |
}
|
|
Packit |
1c1d7e |
}
|
|
Packit |
1c1d7e |
// next we generate a postscript file which contains the eps
|
|
Packit |
1c1d7e |
// and displays it in the right colors and the right bounding box
|
|
Packit |
1c1d7e |
f.setName(formBase+".ps");
|
|
Packit |
1c1d7e |
if (f.open(IO_WriteOnly))
|
|
Packit |
1c1d7e |
{
|
|
Packit |
1c1d7e |
FTextStream t(&f);
|
|
Packit |
1c1d7e |
t << "1 1 1 setrgbcolor" << endl; // anti-alias to white background
|
|
Packit |
1c1d7e |
t << "newpath" << endl;
|
|
Packit |
1c1d7e |
t << "-1 -1 moveto" << endl;
|
|
Packit |
1c1d7e |
t << (x2-x1+2) << " -1 lineto" << endl;
|
|
Packit |
1c1d7e |
t << (x2-x1+2) << " " << (y2-y1+2) << " lineto" << endl;
|
|
Packit |
1c1d7e |
t << "-1 " << (y2-y1+2) << " lineto" <
|
|
Packit |
1c1d7e |
t << "closepath" << endl;
|
|
Packit |
1c1d7e |
t << "fill" << endl;
|
|
Packit |
1c1d7e |
t << -x1 << " " << -y1 << " translate" << endl;
|
|
Packit |
1c1d7e |
t << "0 0 0 setrgbcolor" << endl;
|
|
Packit |
1c1d7e |
t << "(" << formBase << ".eps) run" << endl;
|
|
Packit |
1c1d7e |
f.close();
|
|
Packit |
1c1d7e |
}
|
|
Packit |
1c1d7e |
// scale the image so that it is four times larger than needed.
|
|
Packit |
1c1d7e |
// and the sizes are a multiple of four.
|
|
Packit |
1c1d7e |
double scaleFactor = 16.0/3.0;
|
|
Packit |
1c1d7e |
int zoomFactor = Config_getInt(FORMULA_FONTSIZE);
|
|
Packit |
1c1d7e |
if (zoomFactor<8 || zoomFactor>50) zoomFactor=10;
|
|
Packit |
1c1d7e |
scaleFactor *= zoomFactor/10.0;
|
|
Packit |
1c1d7e |
int gx = (((int)((x2-x1)*scaleFactor))+3)&~;;
|
|
Packit |
1c1d7e |
int gy = (((int)((y2-y1)*scaleFactor))+3)&~;;
|
|
Packit |
1c1d7e |
// Then we run ghostscript to convert the postscript to a pixmap
|
|
Packit |
1c1d7e |
// The pixmap is a truecolor image, where only black and white are
|
|
Packit |
1c1d7e |
// used.
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
char gsArgs[4096];
|
|
Packit |
1c1d7e |
sprintf(gsArgs,"-q -g%dx%d -r%dx%dx -sDEVICE=ppmraw "
|
|
Packit |
1c1d7e |
"-sOutputFile=%s.pnm -dNOPAUSE -dBATCH -- %s.ps",
|
|
Packit |
1c1d7e |
gx,gy,(int)(scaleFactor*72),(int)(scaleFactor*72),
|
|
Packit |
1c1d7e |
formBase.data(),formBase.data()
|
|
Packit |
1c1d7e |
);
|
|
Packit |
1c1d7e |
portable_sysTimerStart();
|
|
Packit |
1c1d7e |
if (portable_system(portable_ghostScriptCommand(),gsArgs)!=0)
|
|
Packit |
1c1d7e |
{
|
|
Packit |
1c1d7e |
err("Problem running ghostscript %s %s. Check your installation!\n",portable_ghostScriptCommand(),gsArgs);
|
|
Packit |
1c1d7e |
portable_sysTimerStop();
|
|
Packit |
1c1d7e |
QDir::setCurrent(oldDir);
|
|
Packit |
1c1d7e |
return;
|
|
Packit |
1c1d7e |
}
|
|
Packit |
1c1d7e |
portable_sysTimerStop();
|
|
Packit |
1c1d7e |
f.setName(formBase+".pnm");
|
|
Packit |
1c1d7e |
uint imageX=0,imageY=0;
|
|
Packit |
1c1d7e |
// we read the generated image again, to obtain the pixel data.
|
|
Packit |
1c1d7e |
if (f.open(IO_ReadOnly))
|
|
Packit |
1c1d7e |
{
|
|
Packit |
1c1d7e |
QTextStream t(&f);
|
|
Packit |
1c1d7e |
QCString s;
|
|
Packit |
1c1d7e |
if (!t.eof())
|
|
Packit |
1c1d7e |
s=t.readLine().utf8();
|
|
Packit |
1c1d7e |
if (s.length()<2 || s.left(2)!="P6")
|
|
Packit |
1c1d7e |
err("ghostscript produced an illegal image format!");
|
|
Packit |
1c1d7e |
else
|
|
Packit |
1c1d7e |
{
|
|
Packit |
1c1d7e |
// assume the size is after the first line that does not start with
|
|
Packit |
1c1d7e |
// # excluding the first line of the file.
|
|
Packit |
1c1d7e |
while (!t.eof() && (s=t.readLine().utf8()) && !s.isEmpty() && s.at(0)=='#') { }
|
|
Packit |
1c1d7e |
sscanf(s,"%d %d",&imageX,&imageY;;
|
|
Packit |
1c1d7e |
}
|
|
Packit |
1c1d7e |
if (imageX>0 && imageY>0)
|
|
Packit |
1c1d7e |
{
|
|
Packit |
1c1d7e |
//printf("Converting image...\n");
|
|
Packit |
1c1d7e |
char *data = new char[imageX*imageY*3]; // rgb 8:8:8 format
|
|
Packit |
1c1d7e |
uint i,x,y,ix,iy;
|
|
Packit |
1c1d7e |
f.readBlock(data,imageX*imageY*3);
|
|
Packit |
1c1d7e |
Image srcImage(imageX,imageY),
|
|
Packit |
1c1d7e |
filteredImage(imageX,imageY),
|
|
Packit |
1c1d7e |
dstImage(imageX/4,imageY/4);
|
|
Packit |
1c1d7e |
uchar *ps=srcImage.getData();
|
|
Packit |
1c1d7e |
// convert image to black (1) and white (0) index.
|
|
Packit |
1c1d7e |
for (i=0;i
|
|
Packit |
1c1d7e |
// apply a simple box filter to the image
|
|
Packit |
1c1d7e |
static int filterMask[]={1,2,1,2,8,2,1,2,1};
|
|
Packit |
1c1d7e |
for (y=0;y
|
|
Packit |
1c1d7e |
{
|
|
Packit |
1c1d7e |
for (x=0;x
|
|
Packit |
1c1d7e |
{
|
|
Packit |
1c1d7e |
int s=0;
|
|
Packit |
1c1d7e |
for (iy=0;iy<2;iy++)
|
|
Packit |
1c1d7e |
{
|
|
Packit |
1c1d7e |
for (ix=0;ix<2;ix++)
|
|
Packit |
1c1d7e |
{
|
|
Packit |
1c1d7e |
s+=srcImage.getPixel(x+ix-1,y+iy-1)*filterMask[iy*3+ix];
|
|
Packit |
1c1d7e |
}
|
|
Packit |
1c1d7e |
}
|
|
Packit |
1c1d7e |
filteredImage.setPixel(x,y,s);
|
|
Packit |
1c1d7e |
}
|
|
Packit |
1c1d7e |
}
|
|
Packit |
1c1d7e |
// down-sample the image to 1/16th of the area using 16 gray scale
|
|
Packit |
1c1d7e |
// colors.
|
|
Packit |
1c1d7e |
// TODO: optimize this code.
|
|
Packit |
1c1d7e |
for (y=0;y
|
|
Packit |
1c1d7e |
{
|
|
Packit |
1c1d7e |
for (x=0;x
|
|
Packit |
1c1d7e |
{
|
|
Packit |
1c1d7e |
int xp=x<<2;
|
|
Packit |
1c1d7e |
int yp=y<<2;
|
|
Packit |
1c1d7e |
int c=filteredImage.getPixel(xp+0,yp+0)+
|
|
Packit |
1c1d7e |
filteredImage.getPixel(xp+1,yp+0)+
|
|
Packit |
1c1d7e |
filteredImage.getPixel(xp+2,yp+0)+
|
|
Packit |
1c1d7e |
filteredImage.getPixel(xp+3,yp+0)+
|
|
Packit |
1c1d7e |
filteredImage.getPixel(xp+0,yp+1)+
|
|
Packit |
1c1d7e |
filteredImage.getPixel(xp+1,yp+1)+
|
|
Packit |
1c1d7e |
filteredImage.getPixel(xp+2,yp+1)+
|
|
Packit |
1c1d7e |
filteredImage.getPixel(xp+3,yp+1)+
|
|
Packit |
1c1d7e |
filteredImage.getPixel(xp+0,yp+2)+
|
|
Packit |
1c1d7e |
filteredImage.getPixel(xp+1,yp+2)+
|
|
Packit |
1c1d7e |
filteredImage.getPixel(xp+2,yp+2)+
|
|
Packit |
1c1d7e |
filteredImage.getPixel(xp+3,yp+2)+
|
|
Packit |
1c1d7e |
filteredImage.getPixel(xp+0,yp+3)+
|
|
Packit |
1c1d7e |
filteredImage.getPixel(xp+1,yp+3)+
|
|
Packit |
1c1d7e |
filteredImage.getPixel(xp+2,yp+3)+
|
|
Packit |
1c1d7e |
filteredImage.getPixel(xp+3,yp+3);
|
|
Packit |
1c1d7e |
// here we scale and clip the color value so the
|
|
Packit |
1c1d7e |
// resulting image has a reasonable contrast
|
|
Packit |
1c1d7e |
dstImage.setPixel(x,y,QMIN(15,(c*15)/(16*10)));
|
|
Packit |
1c1d7e |
}
|
|
Packit |
1c1d7e |
}
|
|
Packit |
1c1d7e |
// save the result as a bitmap
|
|
Packit |
1c1d7e |
QCString resultName;
|
|
Packit |
1c1d7e |
resultName.sprintf("form_%d.png",pageNum);
|
|
Packit |
1c1d7e |
// the option parameter 1 is used here as a temporary hack
|
|
Packit |
1c1d7e |
// to select the right color palette!
|
|
Packit |
1c1d7e |
dstImage.save(resultName,1);
|
|
Packit |
1c1d7e |
delete[] data;
|
|
Packit |
1c1d7e |
}
|
|
Packit |
1c1d7e |
f.close();
|
|
Packit |
1c1d7e |
}
|
|
Packit |
1c1d7e |
// remove intermediate image files
|
|
Packit |
1c1d7e |
thisDir.remove(formBase+".eps");
|
|
Packit |
1c1d7e |
thisDir.remove(formBase+".pnm");
|
|
Packit |
1c1d7e |
thisDir.remove(formBase+".ps");
|
|
Packit |
1c1d7e |
}
|
|
Packit |
1c1d7e |
// remove intermediate files produced by latex
|
|
Packit |
1c1d7e |
thisDir.remove("_formulas.dvi");
|
|
Packit |
1c1d7e |
if (!formulaError) thisDir.remove("_formulas.log"); // keep file in case of errors
|
|
Packit |
1c1d7e |
thisDir.remove("_formulas.aux");
|
|
Packit |
1c1d7e |
}
|
|
Packit |
1c1d7e |
// remove the latex file itself
|
|
Packit |
1c1d7e |
if (!formulaError) thisDir.remove("_formulas.tex");
|
|
Packit |
1c1d7e |
// write/update the formula repository so we know what text the
|
|
Packit |
1c1d7e |
// generated images represent (we use this next time to avoid regeneration
|
|
Packit |
1c1d7e |
// of the images, and to avoid forcing the user to delete all images in order
|
|
Packit |
1c1d7e |
// to let a browser refresh the images).
|
|
Packit |
1c1d7e |
f.setName("formula.repository");
|
|
Packit |
1c1d7e |
if (f.open(IO_WriteOnly))
|
|
Packit |
1c1d7e |
{
|
|
Packit |
1c1d7e |
FTextStream t(&f);
|
|
Packit |
1c1d7e |
for (fli.toFirst();(formula=fli.current());++fli)
|
|
Packit |
1c1d7e |
{
|
|
Packit |
1c1d7e |
t << "\\form#" << formula->getId() << ":" << formula->getFormulaText() << endl;
|
|
Packit |
1c1d7e |
}
|
|
Packit |
1c1d7e |
f.close();
|
|
Packit |
1c1d7e |
}
|
|
Packit |
1c1d7e |
// reset the directory to the original location.
|
|
Packit |
1c1d7e |
QDir::setCurrent(oldDir);
|
|
Packit |
1c1d7e |
}
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
#ifdef FORMULA_TEST
|
|
Packit |
1c1d7e |
int main()
|
|
Packit |
1c1d7e |
{
|
|
Packit |
1c1d7e |
FormulaList fl;
|
|
Packit |
1c1d7e |
fl.append(new Formula("$x^2$"));
|
|
Packit |
1c1d7e |
fl.append(new Formula("$y^2$"));
|
|
Packit |
1c1d7e |
fl.append(new Formula("$\\sqrt{x_0^2+x_1^2+x_2^2}$"));
|
|
Packit |
1c1d7e |
fl.generateBitmaps("dest");
|
|
Packit |
1c1d7e |
return 0;
|
|
Packit |
1c1d7e |
}
|
|
Packit |
1c1d7e |
#endif
|