1 /*
2  *  description: converter from xml to png files for new file format
3  *  date: 8 may 2009
4  *
5  *  Copyright (C) 2010  Thomas Braun <thomas.braun@virtuell-zuhause.de>
6  *  Copyright (C) 2013  Jan Sundermeyer <jsundermeyer@sf.net>
7  *
8  *  This program is free software; you can redistribute it and/or
9  *  modify it under the terms of the GNU General Public License as
10  *  published by the Free Software Foundation; either version 2 of
11  *  the License, or (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  *
21  */
22 
23 #include <QDomDocument>
24 #include <QFile>
25 #include <QTextStream>
26 #include <QDebug>
27 #include <QList>
28 #include <QStringList>
29 #include <QTemporaryFile>
30 #include <QImage>
31 #include <QCoreApplication>
32 
33 #include <iostream>
34 #include <stdlib.h>
35 
36 #include "symbolviewclasses.h"
37 
38 /* TODO
39    - more error checking when parsing xml file
40 */
41 
readImageComments(const QString & fileName)42 void readImageComments(const QString &fileName)
43 {
44    QImage image;
45    QString output;
46 
47    if(image.load(fileName)) {
48       qDebug() << QString("Image %1 has Command _%2_").arg(fileName).arg(image.text("Command"));
49       qDebug() << QString("Image %1 has Comment _%2_").arg(fileName).arg(image.text("Comment"));
50       qDebug() << QString("image %1 has Package _%2_").arg(fileName).arg(image.text("Packages"));
51       qDebug() << QString("Image %1 has CommandUnicode _%2_").arg(fileName).arg(image.text("CommandUnicode"));
52       qDebug() << QString("Image %1 has UnicodePackages _%2_").arg(fileName).arg(image.text("UnicodePackages"));
53    }
54    else {
55       qDebug() << "===readComment=== ERROR " << fileName << " could not be loaded";
56    }
57 }
58 
convertUTF8toLatin1String(const QString & string)59 QString convertUTF8toLatin1String(const QString &string){
60 
61    QVector<uint> stringAsInt;
62    QString stringAsLatin1;
63 
64    stringAsInt = string.toUcs4();
65    QVector<uint>::const_iterator it;
66    for(it = stringAsInt.begin(); it != stringAsInt.end(); it++) {
67       stringAsLatin1 += QString("U+%1,").arg(*it);
68    }
69    return stringAsLatin1;
70 }
71 
pkgListToString(const QList<Package> & packages)72 QString pkgListToString(const QList<Package> &packages){
73 
74    QString packagesArg, packagesName;
75    int i=0;
76    for(; i < packages.count()-1 ; i++){
77       packagesArg  += packages[i].arguments + ',';
78       packagesName += packages[i].name + ',';
79    }
80    if(i<packages.count()){
81        packagesArg  += packages[i].arguments;
82        packagesName += packages[i].name;
83    }
84    QString result = ( packagesArg.isEmpty() ? "" : '[' + packagesArg + ']' ) + ( packagesName.isEmpty() ? "" : '{' + packagesName + '}' );
85    return result;
86 }
87 
writeSVGComments(const Command & cmd,const QString & fileName)88 void writeSVGComments(const Command &cmd, const QString &fileName)
89 {
90    QString unicodeCommandAsLatin1, commentAsLatin1;
91    QString packagesarg, packages;
92 
93    if(!cmd.unicodeCommand.isEmpty()) {
94       unicodeCommandAsLatin1 = convertUTF8toLatin1String(cmd.unicodeCommand);
95    }
96    if(!cmd.comment.isEmpty()) {
97       commentAsLatin1 = convertUTF8toLatin1String(cmd.comment);
98    }
99 
100    qDebug() << "fileName is " << fileName;
101    qDebug() << "Command is " << cmd.latexCommand;
102    qDebug() << "unicodeCommandAsLatin1 is " << unicodeCommandAsLatin1;
103    qDebug() << "commentAsLatin1 is " << commentAsLatin1;
104    qDebug() << "comment is " << cmd.comment;
105 
106    QFile file( fileName );
107 
108   if( !file.open( QIODevice::ReadOnly ) ){
109     qDebug() << "could not open file";
110     return;
111   }
112 
113   QStringList fileContent;
114   QTextStream stream(&file);
115   QString line;
116   do {
117       line = stream.readLine();
118       if(!line.isNull())
119           fileContent<<line;
120   } while (!line.isNull());
121 
122   file.close();
123 
124 
125   //QFile file( fn );
126   if( !file.open( QIODevice::WriteOnly ) )
127   return;
128 
129   QTextStream ts( &file );
130   for(int i=0;i<fileContent.size();i++){
131       line=fileContent.at(i);
132       if(line.startsWith("<defs>")){
133 	  QString Command=cmd.latexCommand;
134 	  Command.replace("<","&lt;");
135           ts<<"<title>"<<Command<<"</title>"<<"\n";
136           QString additional;
137           if (!commentAsLatin1.isEmpty() ) {
138               additional="Comment=\""+commentAsLatin1+"\" ";
139           }
140           if( !unicodeCommandAsLatin1.isEmpty() ) {
141               additional+="CommandUnicode=\""+unicodeCommandAsLatin1+"\" ";
142               additional+="UnicodePackages=\""+pkgListToString(cmd.unicodePackages)+"\" ";
143           }
144           ts<<"<desc "<<"Packages=\""<<pkgListToString(cmd.packages)<<"\" />\n";
145 	  ts<<"<additionalInfo "<<additional<<" />\n";
146       }
147       ts<<line<<"\n";
148   }
149 
150   file.close();
151 
152 }
153 
readSVG(QString fn)154 int readSVG(QString fn){
155     QFile file( fn );
156 
157    if( !file.open( QIODevice::ReadOnly ) ){
158      qDebug() << "could not open file";
159      return -1;
160    }
161 
162    QString errorMsg;
163    int errorLine,errorColumn;
164    QDomDocument doc( "svg" );
165 
166    if( !doc.setContent( &file,false, &errorMsg, &errorLine, &errorColumn) )
167    {
168      qDebug() << "could not find xml content";
169      qDebug() << errorMsg;
170      qDebug() << "line is " << errorLine;
171      qDebug() << "column is " << errorColumn;
172      file.close();
173      return -2;
174    }
175    file.close();
176 
177    // check root element
178    QDomElement root = doc.documentElement();
179    if( root.tagName() != "svg" ) {
180      qDebug() << "wrong format";
181      return -3;
182    }
183    QDomNode n=doc.createElement("title");
184    QDomNode cont=doc.createTextNode("title");
185    n.appendChild(cont);
186    //n.setNodeValue("test a");
187    root.appendChild(n);
188 
189    //QFile file( fn );
190    if( !file.open( QIODevice::WriteOnly ) )
191    return -1;
192 
193    QTextStream ts( &file );
194    ts << doc.toString();
195 
196    file.close();
197    /*
198    QDomNode n = root.firstChild();
199    while( !n.isNull() ) {
200      QDomElement e = n.toElement();
201 
202      if( e.isNull() ) {
203        n = n.nextSibling();
204        continue;
205      }
206      qDebug() << "element name is " << e.tagName();
207      //QString tagName = e.tagName();
208      n = n.nextSibling();
209   }*/
210   return 0;
211 }
212 
generateSVG(QString latexFile,int index,QString symbolGroupName)213 QString generateSVG(QString latexFile, int index, QString symbolGroupName) {
214 
215    QString texfile, texfileWithoutSuffix,svgfile;
216    int latexret, dvipngret;
217 
218    QTemporaryFile file("XXXXXX.tex");
219    file.setAutoRemove(false);
220    if (file.open()) {
221       QTextStream t(&file);
222       t.setCodec("UTF-8");
223       t << latexFile;
224 
225       texfile = file.fileName();
226       texfileWithoutSuffix = texfile.left(texfile.length() - 4);
227       if(index>0)
228 	svgfile = QString("img%1%2.svg").arg(index,3,10,QChar('0')).arg(symbolGroupName);
229       else
230 	svgfile=symbolGroupName+".svg";
231       qDebug() << texfile;
232       qDebug() << texfileWithoutSuffix;
233       qDebug() << svgfile;
234 
235       file.close();
236    }
237 
238    QString texcommand=QString("latex -interaction=batchmode %1").arg(texfile);
239    QString dvipngcommand=QString("dvisvgm -e -o %1 %2.dvi").arg(svgfile).arg(texfileWithoutSuffix);
240 
241    qDebug() << texcommand;
242    qDebug() << dvipngcommand;
243 
244    latexret = system(texcommand.toLatin1());
245    dvipngret= system(dvipngcommand.toLatin1());
246 
247    if (latexret) {
248       qDebug() << "Error compiling the latex file";
249       return QString();
250    }
251 
252    if(dvipngret) {
253       qDebug() << "Error producing the pngs";
254       return QString();
255    }
256 
257    return svgfile;
258 }
259 
writeImageComments(const Command & cmd,const QString & fileName)260 void writeImageComments(const Command &cmd, const QString &fileName)
261 {
262 
263    QImage image;
264    QString unicodeCommandAsLatin1, commentAsLatin1;
265    QString packagesarg, packages;
266 
267    if(!cmd.unicodeCommand.isEmpty()) {
268       unicodeCommandAsLatin1 = convertUTF8toLatin1String(cmd.unicodeCommand);
269    }
270    if(!cmd.comment.isEmpty()) {
271       commentAsLatin1 = convertUTF8toLatin1String(cmd.comment);
272    }
273 
274    qDebug() << "fileName is " << fileName;
275    qDebug() << "Command is " << cmd.latexCommand;
276    qDebug() << "unicodeCommandAsLatin1 is " << unicodeCommandAsLatin1;
277    qDebug() << "commentAsLatin1 is " << commentAsLatin1;
278    qDebug() << "comment is " << cmd.comment;
279 
280    if(image.load(fileName)) {
281 
282       image.setText("Command",cmd.latexCommand);
283       if( !unicodeCommandAsLatin1.isEmpty() ) {
284 	 image.setText("CommandUnicode",unicodeCommandAsLatin1);
285 	 image.setText("UnicodePackages",pkgListToString(cmd.unicodePackages));
286       }
287       if (!commentAsLatin1.isEmpty() ) {
288 	 image.setText("Comment",commentAsLatin1);
289       }
290 
291       image.setText("Packages",pkgListToString(cmd.packages));
292       if(!image.save(fileName,"PNG")) {
293 	 qDebug() << "Image " << fileName << " could not be saved";
294 	 exit(1);
295       }
296    }
297    else {
298       qDebug() << "===writeComment=== ERROR " << fileName << "could not be loaded";
299    }
300 
301 }
302 
generatePNG(QString latexFile,int index,QString symbolGroupName,QString batikConvert)303 QString generatePNG(QString latexFile, int index, QString symbolGroupName,QString batikConvert) {
304 
305    QString texfile, texfileWithoutSuffix,pngfile;
306    int latexret, dvipngret,convertret,rmret;
307 
308    QTemporaryFile file("XXXXXX.tex");
309    file.setAutoRemove(false);
310    if (file.open()) {
311       QTextStream t(&file);
312       t.setCodec("UTF-8");
313       t << latexFile;
314 
315       texfile = file.fileName();
316       texfileWithoutSuffix = texfile.left(texfile.length() - 4);
317       if(index>0)
318 	pngfile = QString("img%1%2.png").arg(index,3,10,QChar('0')).arg(symbolGroupName);
319       else
320 	pngfile=symbolGroupName+".png";
321       qDebug() << texfile;
322       qDebug() << texfileWithoutSuffix;
323       qDebug() << pngfile;
324 
325       file.close();
326    }
327 
328    QString texcommand=QString("latex -interaction=batchmode %1").arg(texfile);
329    QString dvipngcommand=QString("dvisvgm -e -o %1.svg %2.dvi").arg(texfileWithoutSuffix).arg(texfileWithoutSuffix);
330    QString convertCommand=QString("%1 %2.svg %3").arg(batikConvert).arg(texfileWithoutSuffix).arg(pngfile);
331    QString removeCommand=QString("rm %1.svg").arg(texfileWithoutSuffix);
332    //QString dvipngcommand=QString("dvipng  --strict --picky --freetype -x 1440 -bg Transparent -D 600 -O -1.2in,-1.2in -T bbox -z 6 -o %1 %2.dvi").arg(pngfile).arg(texfileWithoutSuffix);
333 
334    qDebug() << texcommand;
335    qDebug() << dvipngcommand;
336    qDebug() << convertCommand;
337 
338    latexret = system(texcommand.toLatin1());
339    dvipngret= system(dvipngcommand.toLatin1());
340    convertret= system(convertCommand.toLatin1());
341    rmret=system(removeCommand.toLatin1());
342 
343    if (latexret) {
344       qDebug() << "Error compiling the latex file";
345       return QString();
346    }
347 
348    if(dvipngret) {
349       qDebug() << "Error producing the svg";
350       return QString();
351    }
352 
353    if(convertret) {
354       qDebug() << "Error converting svg to png";
355       return QString();
356    }
357    if(rmret) {
358       qDebug() << "Error removing svg";
359       return QString();
360    }
361 
362    return pngfile;
363 }
364 
generateLatexFile(const Preamble & preamble,const Command & cmd)365 QString generateLatexFile(const Preamble &preamble, const Command &cmd)
366 {
367    QString output;
368    Package pkg;
369    QString cmdString;
370 
371    output += QString("\\documentclass[%1]{%2}\n").arg(preamble.classArguments).arg(preamble.className);
372    output += '\n';
373    output += preamble.additional;
374    output += '\n';
375 
376    for(int i=0; i < cmd.packages.count(); i++){
377       pkg = cmd.packages[i];
378       if(pkg.arguments.isEmpty()) {
379 	 output += QString("\\usepackage{%1}\n").arg(pkg.name);
380       }
381       else{
382 	 output += QString("\\usepackage[%1]{%2}\n").arg(pkg.arguments).arg(pkg.name);
383       }
384    }
385 
386    output += "\\begin{document}\n";
387    output += '\n';
388    if(!cmd.iconMode)
389       output += "\\special{dvisvgm:bbox 0 0}\n";
390    cmdString = !cmd.ImageCommand.isEmpty() ? cmd.ImageCommand : cmd.latexCommand;
391    output += cmd.mathMode ? QString("\\ensuremath{%1}\n").arg(cmdString) : QString("%1\n").arg(cmdString);
392    output += '\n';
393    output += "\\end{document}\n";
394 
395    return output;
396 }
397 
getAllPackages(const QDomElement & e)398 QList<Package> getAllPackages(const QDomElement &e){
399 
400    QList<Package> packages;
401    Package pkg;
402    QDomElement element;
403 
404    if(e.isNull()){
405       return packages;
406    }
407 
408    QDomNodeList cmdNodes = e.childNodes();
409 
410    for(int i=0; i < cmdNodes.count();i++) {
411       element = cmdNodes.item(i).toElement();
412       if( element.tagName()== "package") {
413 	 pkg.name = element.firstChildElement("name").text();
414 	 pkg.arguments =  element.firstChildElement("arguments").text();
415 	 packages.append(pkg);
416 	 qDebug() << "pkg.name is " << pkg.name;
417 	 qDebug() << "pkg.arguments is " << pkg.arguments;
418       }
419    }
420    return packages;
421 }
422 
getCommandDefinition(const QDomElement & e,QList<Package> unicodePackages)423 Command getCommandDefinition(const QDomElement &e, QList<Package> unicodePackages)
424 {
425    if(e.isNull()) {
426       return Command();
427    }
428 
429    Package pkg;
430    Command cmd;
431 
432    cmd.unicodePackages = unicodePackages;
433    cmd.packages = getAllPackages(e);
434    cmd.mathMode = e.firstChildElement("mathMode").text() == "true" ? true : false;
435    cmd.forcePNG = e.firstChildElement("forcePNG").text() == "true" ? true : false;
436    cmd.iconMode = e.firstChildElement("iconMode").text() == "true" ? true : false;
437    cmd.name = e.firstChildElement("name").text();
438    qDebug()<<cmd.iconMode;
439    cmd.comment = e.firstChildElement("comment").text();
440    cmd.latexCommand = e.firstChildElement("latexCommand").text();
441    cmd.unicodeCommand = e.firstChildElement("unicodeCommand").text();
442    cmd.ImageCommand = e.firstChildElement("imageCommand").text();
443 
444    qDebug() << QString("cmd: latexCommand=%1, unicodeCommand=%2, imageCommand=%3, comment=%4, mathmode=%5").arg(cmd.latexCommand).arg(cmd.unicodeCommand).arg(cmd.ImageCommand).arg(cmd.comment).arg(cmd.mathMode);
445 
446    if(cmd.packages.count() > 0 ){
447       qDebug() << "packages are";
448       for(int i=0; i < cmd.packages.count(); i++){
449 	 qDebug() << QString("name=%1, arguments=%2").arg(cmd.packages[i].name).arg(cmd.packages[i].arguments);
450       }
451    }
452    else{
453       qDebug() << "no packages to include";
454    }
455 
456    return cmd;
457 }
458 
usage()459 void usage(){
460 
461    qDebug() << QString("usage: gesymb-ng mySymbols.xml path/to/batikConvert");
462    exit(1);
463 }
464 
main(int argc,char ** argv)465 int main(int argc, char** argv)
466 {
467    Preamble preamble;
468    Version version;
469    QList<Command> commands;
470    QString symbolGroupName;
471    QList<Package> unicodePkgList;
472 
473    if(argc < 3){
474       usage();
475    }
476    QFile file( argv[1] );
477    QString batik=argv[2];
478 
479   if( !file.open( QIODevice::ReadOnly ) ){
480     qDebug() << "could not open file";
481     return -1;
482   }
483 
484   QString errorMsg;
485   int errorLine,errorColumn;
486   QDomDocument doc( "KileSymbolSources" );
487 
488   if( !doc.setContent( &file,false, &errorMsg, &errorLine, &errorColumn) )
489   {
490     qDebug() << "could not find xml content";
491     qDebug() << errorMsg;
492     qDebug() << "line is " << errorLine;
493     qDebug() << "column is " << errorColumn;
494     file.close();
495     return -2;
496   }
497   file.close();
498 
499   // check root element
500   QDomElement root = doc.documentElement();
501   if( root.tagName() != "symbols" ) {
502     qDebug() << "wrong format";
503     return -3;
504   }
505 
506   QDomNode n = root.firstChild();
507   while( !n.isNull() ) {
508     QDomElement e = n.toElement();
509 
510     if( e.isNull() ) {
511       n = n.nextSibling();
512       continue;
513     }
514     qDebug() << "element name is " << e.tagName();
515     QString tagName = e.tagName();
516 
517     if( tagName == "formatVersion" ){
518       version.major = e.attribute("major");
519       version.minor = e.attribute("minor");
520     }
521     else if( tagName == "symbolGroupName" ){
522        symbolGroupName = e.text();
523     }
524     else if(tagName == "preamble"){
525       preamble.className = e.firstChildElement("class").text();
526       preamble.classArguments = e.firstChildElement("arguments").text();
527       preamble.additional= e.firstChildElement("additional").text();
528 
529       qDebug() << "class is " << preamble.className;
530       qDebug() << "arguments is " << preamble.classArguments;
531       qDebug() << "additional is " << preamble.additional;
532 
533     }
534     else if( tagName == "unicodeCommandPackages") {
535        unicodePkgList = getAllPackages(e);
536     }
537     else if( tagName == "commandDefinition" ){
538        commands.append(getCommandDefinition(e,unicodePkgList));
539     }
540     else{
541        qDebug() << "unexpected node: " << tagName;
542     }
543       n = n.nextSibling();
544     }
545 
546     QString content,pngfile;
547     for(int i=0; i < commands.count();i++) {
548        content = generateLatexFile(preamble,commands[i]);
549        qDebug() << content;
550        if(commands[i].forcePNG){
551 	  if(commands[i].name.isEmpty())
552 	    pngfile = generatePNG(content,i+1,symbolGroupName,batik);
553 	  else
554 	    pngfile = generatePNG(content,-1,commands[i].name,batik);
555 	  writeImageComments(commands[i],pngfile);
556        }else{
557 	 if(commands[i].name.isEmpty())
558 	    pngfile = generateSVG(content,i+1,symbolGroupName);
559 	 else
560 	    pngfile = generateSVG(content,-1,commands[i].name);
561 	 writeSVGComments(commands[i],pngfile);
562        }
563        //readImageComments(pngfile);
564     }
565 
566 }
567