1 /*
2 blahtex: a TeX to MathML converter designed with MediaWiki in mind
3 blahtexml: an extension of blahtex with XML processing in mind
4 http://gva.noekeon.org/blahtexml
5 
6 Copyright (c) 2006, David Harvey
7 Copyright (c) 2010, Gilles Van Assche
8 All rights reserved.
9 
10 Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
11 
12     * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
13     * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
14     * Neither the names of the authors nor the names of their affiliation may be used to endorse or promote products derived from this software without specific prior written permission.
15 
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
17 */
18 
19 #include "BlahtexCore/Misc.h"
20 #include "UnicodeConverter.h"
21 #include "md5Wrapper.h"
22 #include "mainPng.h"
23 #include <cerrno>
24 #include <sys/stat.h>
25 #include <iostream>
26 #include <fstream>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <sstream>
30 
31 #include <unistd.h>
32 
33 using namespace std;
34 using namespace blahtex;
35 
36 
37 // From main.cpp:
38 extern UnicodeConverter gUnicodeConverter;
39 
40 
41 // TemporaryFile manages a temporary file; it deletes the named file when
42 // the object goes out of scope.
43 class TemporaryFile
44 {
45     string mFilename;
46 
47     // This flag might get set to false if we are in some kind of
48     // debugging mode and want to keep temp files.
49     bool mShouldDelete;
50 
51 public:
TemporaryFile(const string & filename,bool shouldDelete=true)52     TemporaryFile(
53         const string& filename,
54         bool shouldDelete = true
55     ) :
56         mFilename(filename),
57         mShouldDelete(shouldDelete)
58     { }
59 
~TemporaryFile()60     ~TemporaryFile()
61     {
62         if (mShouldDelete)
63             unlink(mFilename.c_str());
64     }
65 };
66 
67 
68 // Tests whether a file exists
FileExists(const string & filename)69 bool FileExists(const string& filename)
70 {
71     struct stat temp;
72     return (stat(filename.c_str(), &temp) == 0);
73 }
74 
75 
76 // Attempts to run given command from the given directory.
77 // Returns true if the system() call was successful, otherwise false.
78 // Can throw a "CannotChangeDirectory" exception if problems occur.
Execute(const string & command,const string & directory="./")79 bool Execute(
80     const string& command,
81     const string& directory = "./"
82 )
83 {
84     char buffer[5000];
85 
86     bool NeedToChange = (directory != "" && directory != "./");
87 
88     if (NeedToChange)
89     {
90         if (getcwd(buffer, 5000) == NULL)
91             throw blahtex::Exception(L"CannotChangeDirectory");
92 
93         if (chdir(directory.c_str()) != 0)
94             throw blahtex::Exception(L"CannotChangeDirectory");
95     }
96 
97     bool result = (system(command.c_str()) == 0);
98 
99     if (NeedToChange)
100     {
101         if (chdir(buffer) != 0)
102             throw blahtex::Exception(L"CannotChangeDirectory");
103     }
104 
105     return result;
106 }
107 
108 
MakePngFile(const wstring & purifiedTex,const string & pngFilename,const PngParams & params)109 PngInfo MakePngFile(
110     const wstring& purifiedTex,
111     const string& pngFilename,
112     const PngParams& params
113 )
114 {
115     PngInfo info;
116 
117     string purifiedTexUtf8 = gUnicodeConverter.ConvertOut(purifiedTex);
118 
119     // This md5 is used for the temp filenames.
120     string md5 = ComputeMd5(purifiedTexUtf8);
121 
122     string pngActualFilename =
123         pngFilename.empty() ? (md5 + ".png") : pngFilename;
124 
125     // Send output to tex file.
126     {
127         ofstream texFile(
128             (params.tempDirectory + md5 + ".tex").c_str(),
129             ios::out | ios::binary
130         );
131         if (!texFile)
132             throw blahtex::Exception(
133                 L"CannotCreateTexFile"
134             );
135         texFile << purifiedTexUtf8;
136         if (!texFile)
137             throw blahtex::Exception(
138                 L"CannotWriteTexFile"
139             );
140     }
141 
142     // These are temporary files we want deleted when we're done.
143     TemporaryFile  texTemp(params.tempDirectory + md5 + ".tex",  params.deleteTempFiles);
144     TemporaryFile  auxTemp(params.tempDirectory + md5 + ".aux",  params.deleteTempFiles);
145     TemporaryFile  logTemp(params.tempDirectory + md5 + ".log",  params.deleteTempFiles);
146     TemporaryFile  dviTemp(params.tempDirectory + md5 + ".dvi",  params.deleteTempFiles);
147     TemporaryFile dataTemp(params.tempDirectory + md5 + ".data", params.deleteTempFiles);
148 
149 
150     if (!Execute(
151             params.shellLatex + " " + md5 + ".tex >/dev/null 2>/dev/null",
152             params.tempDirectory
153         )
154         ||
155         !FileExists(params.tempDirectory + md5 + ".dvi")
156     )
157         throw blahtex::Exception(L"CannotRunLatex");
158 
159 
160     if (!Execute(
161             params.shellDvipng + " " + md5 + ".dvi " +
162                 "--picky --bg Transparent --gamma 1.3 -D 120 -q -T tight " +
163                 "--height --depth " +
164                 "-o \"" + pngActualFilename +
165                 "\" > " + md5 + ".data 2>/dev/null",
166             params.tempDirectory
167         )
168         ||
169         !FileExists(params.tempDirectory + pngActualFilename)
170     )
171         throw blahtex::Exception(L"CannotRunDvipng");
172 
173     if (rename(
174         (params.tempDirectory + pngActualFilename).c_str(),
175         (params.pngDirectory + pngActualFilename).c_str()
176     ))
177         throw blahtex::Exception(L"CannotWritePngDirectory");
178 
179 
180     // Read the height and depth of the image from dvipng's output.
181     {
182         ifstream dataFile(
183             (params.tempDirectory + md5 + ".data").c_str(),
184             ios::in | ios::binary
185         );
186 
187         if (dataFile)
188         {
189             string line, temp;
190             while (getline(dataFile, line))
191             {
192                 string::size_type heightPos = line.find("height=");
193                 string::size_type depthPos = line.find("depth=");
194                 if (heightPos != string::npos && depthPos != string::npos)
195                 {
196                     info.mDimensionsValid = true;
197                     temp = line.substr(heightPos + 7, 1000);
198                     istringstream(temp) >> info.mHeight;
199                     string temp = line.substr(depthPos + 6, 1000);
200                     istringstream(temp) >> info.mDepth;
201                 }
202             }
203         }
204     }
205     info.fullFileName = params.pngDirectory + pngActualFilename;
206     info.mMd5 = md5;
207     return info;
208 }
209 
210 // end of file @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
211