]> git.lyx.org Git - lyx.git/blob - src/converter.C
Various fixes, removed "default" language, inserted new lyxrc tag
[lyx.git] / src / converter.C
1 /* This file is part of
2  * ====================================================== 
3  * 
4  *           LyX, The Document Processor
5  *        
6  *           Copyright 1995 Matthias Ettrich
7  *           Copyright 1995-2000 The LyX Team.
8  *
9  * ====================================================== */
10
11 #include <config.h>
12
13 #ifdef __GNUG__
14 #pragma implementation
15 #endif
16
17 #include <queue>
18 #include <algorithm> // sort()
19
20 #include "converter.h"
21 #include "lyxrc.h"
22 #include "support/syscall.h"
23 #include "support/path.h"
24 #include "buffer.h"
25 #include "bufferview_funcs.h"
26 #include "LaTeX.h"
27 #include "LyXView.h"
28 #include "minibuffer.h"
29 #include "lyx_gui_misc.h"
30 #include "lyx_cb.h" // ShowMessage()
31
32 using std::map;
33 using std::vector;
34 using std::queue;
35 using std::pair;
36 using std::sort;
37 using std::endl;
38
39 //////////////////////////////////////////////////////////////////////////////
40
41 map<string, Format> Formats::formats;
42 vector<Command> Converter::commands;
43 string Converter::latex_command;
44
45 inline
46 string const add_options(string const & command, string const & options)
47 {
48         string head;
49         string tail = split(command, head, ' ');
50         return head + ' ' + options + ' ' + tail;
51 }
52
53 //////////////////////////////////////////////////////////////////////////////
54
55 Format::Format(string const & n)
56         : name(n), in_degree(0)
57 {
58         struct Item {
59                 char const * name;
60                 char const * prettyname;
61         };
62         Item items[] = {
63                 { "tex", "LaTeX" },
64                 { "dvi", "DVI" },
65                 { "ps", "PostScript" },
66                 { "txt", "Ascii" },
67                 { "html", "HTML" },
68                 { "pdf", "PDF" },
69                 { 0, 0}
70         };
71
72         prettyname = n;
73         for (int i = 0; items[i].name != 0; ++i)
74                 if (items[i].name == n) {
75                         prettyname = items[i].prettyname;
76                         break;
77                 }
78 }
79
80
81 void Formats::Add(string const & name)
82 {
83         if (formats.find(name) == formats.end())
84                 formats[name] = Format(name);
85 }
86
87
88 void Formats::SetViewer(string const & name, string const & command)
89 {
90
91         string command2 = subst(command, "$$FName", "'$$FName'");
92         if (!contains(command,"$$FName"))
93                 command2 += " '$$FName'";
94
95         Add(name);
96         GetFormat(name)->viewer = command2;
97 }
98
99
100 bool Formats::View(Buffer * buffer, string const & filename)
101 {
102         if (filename.empty())
103                 return false;
104
105         string extension = GetExtension(filename);
106         Format * format = GetFormat(extension);
107         if (!format || format->viewer.empty()) {
108                 WriteAlert(_("Can not view file"),
109                            _("No information for viewing ")
110                            + Formats::PrettyName(extension));
111                            return false;
112         }
113
114         string command = format->viewer;
115
116         if (extension == "dvi" &&
117             !lyxrc.view_dvi_paper_option.empty()) {
118                 string options = lyxrc.view_dvi_paper_option;
119                 options += " " + Converter::dvi_papersize(buffer);
120                 if (buffer->params.orientation 
121                     == BufferParams::ORIENTATION_LANDSCAPE)
122                         options += 'r';
123                 command = add_options(command, options);
124         }
125
126         string command2 = subst(command, "$$FName", OnlyFilename(filename));
127         lyxerr << "Executing command: " << command2 << endl;
128         ShowMessage(buffer, _("Executing command:"), command2);
129
130         command = subst(command, "$$FName", filename);
131         Systemcalls one;
132         int res = one.startscript(Systemcalls::SystemDontWait, command);
133
134         if (res) {
135                 WriteAlert(_("Can not view file"),
136                            _("Error while executing"),
137                            command.substr(0, 50));
138                 return false;
139         }
140         return true;
141 }
142
143
144 Format * Formats::GetFormat(string const & name)
145 {
146         map<string, Format>::iterator it = formats.find(name);
147         if (it != formats.end())
148                 return &(*it).second;
149         else
150                 return 0;
151 }
152
153
154 string const Formats::PrettyName(string const & name)
155 {
156         string format;
157         Converter::SplitFormat(name, format);
158         Format * f = GetFormat(format);
159         if (f)
160                 return f->prettyname;
161         else
162                 return format;
163 }
164
165
166 //////////////////////////////////////////////////////////////////////////////
167
168 void Converter::Add(string const & from, string const & to,
169                     string const & command, string const & flags)
170 {
171         if (command == "none")
172                 return;
173
174         string command2 = 
175                    subst(command, "$$FName", "'$$FName'");
176         command2 = subst(command2, "$$BaseName", "'$$BaseName'");
177         command2 = subst(command2, "$$OutName", "'$$OutName'");
178         Command Com(from, to, command2);
179
180         if (from == "tex" &&
181             (to == "dvi" ||
182              (to == "pdf" && latex_command.empty())))
183                 latex_command = command2;
184
185         // Read the flags
186         string flag_name,flag_value;
187         string flag_list(flags);
188         while (!flag_list.empty()) {
189                 flag_list = split(flag_list, flag_value,',');
190                 flag_value = split(flag_value, flag_name, '=');
191                 if (flag_name == "originaldir")
192                         Com.original_dir = true;
193                 else if (flag_name == "needaux")
194                         Com.need_aux = true;
195                 else if (flag_name == "resultdir")
196                         Com.result_dir = (flag_value.empty())
197                                 ? "$$BaseName" : flag_value;
198                 else if (flag_name == "resultfile")
199                         Com.result_file = flag_value;
200         }
201         if (!Com.result_dir.empty() && Com.result_file.empty())
202                 Com.result_file = "index." + to;
203
204         for (vector<Command>::iterator it = commands.begin();
205              it != commands.end(); ++it)
206                 if ((*it).from == from && (*it).to == to) {
207                         *it = Com;
208                         return;
209                 }
210         commands.push_back(Com);
211         Formats::Add(from);
212         Formats::Add(to);
213         ++Formats::GetFormat(to)->in_degree;
214 }
215
216
217 vector< pair<string,string> > const
218 Converter::GetReachable(string const & from, bool only_viewable)
219 {
220         vector< pair<string,string> > result;
221         Format * format = Formats::GetFormat(from);
222         if (!format)
223                 return result;
224
225         int sort_start = 0;
226         if (!only_viewable || !format->viewer.empty()) {
227                 result.push_back(pair<string,string>(from, format->prettyname));
228                 sort_start = 1;
229         }
230
231         queue< vector<Command>::iterator > Q;
232         for (vector<Command>::iterator it = commands.begin();
233              it != commands.end(); ++it)
234                 if ((*it).from == from) {
235                         Q.push(it);
236                         (*it).visited = true;
237                 } else
238                         (*it).visited = false;
239
240         while (!Q.empty()) {
241                 vector<Command>::iterator it = Q.front();
242                 format = Formats::GetFormat((*it).to);
243                 string name = format->name;
244                 string prettyname = format->prettyname;
245                 if (format->in_degree > 1) {
246                         name += ":" + (*it).from;
247                         string tmp;
248                         split((*it).command, tmp, ' ');
249                         prettyname  += _(" (using ") + tmp + ")";       
250                 }
251                 if (!only_viewable || !format->viewer.empty())
252                         result.push_back(pair<string,string>(name, prettyname));
253                 Q.pop();
254                 for (vector<Command>::iterator it2 = commands.begin();
255                      it2 != commands.end(); ++it2)
256                         if (!(*it2).visited && (*it).to == (*it2).from) {
257                                 Q.push(it2);
258                                 (*it2).visited = true;
259                         }
260         }
261
262         sort(result.begin() + sort_start, result.end());
263         return result;
264 }
265
266
267 bool Converter::Convert(Buffer * buffer, string const & from_file,
268                         string const & to_file, string const & using_format,
269                         string * view_file)
270 {
271         if (view_file)
272                 *view_file = to_file;
273
274         string from_format = GetExtension(from_file);
275         string to_format = GetExtension(to_file);
276         if (from_format == to_format)
277                 if (from_file != to_file)
278                         return lyx::rename(from_file, to_file);
279                 else
280                         return true;
281
282         queue< vector<Command>::iterator > Q;
283         for (vector<Command>::iterator it = commands.begin();
284              it != commands.end(); ++it)
285                 if ((*it).from == from_format) {
286                         Q.push(it);
287                         (*it).visited = true;
288                         (*it).previous = commands.end();
289                 } else
290                         (*it).visited = false;
291
292         if (Q.empty()) {
293                 WriteAlert(_("Can not convert file"),
294                            ("Unknown format ") + from_format);
295                 return false;
296         }
297
298         bool found = false;
299         vector<Command>::iterator it;
300         while (!Q.empty()) {
301                 it = Q.front();
302                 if ((*it).to == to_format &&
303                     (using_format.empty() || using_format == (*it).from)) {
304                         found = true;
305                         break;
306                 }
307                 Q.pop();
308                 for (vector<Command>::iterator it2 = commands.begin();
309                      it2 != commands.end(); ++it2)
310                         if (!(*it2).visited && (*it).to == (*it2).from) {
311                                 Q.push(it2);
312                                 (*it2).visited = true;
313                                 (*it2).previous = it;
314                         }
315         }
316
317         if (!found) {
318                 WriteAlert(_("Can not convert file"),
319                            _("No information for converting from ")
320                            + Formats::PrettyName(from_format) + _(" to ")
321                            + Formats::PrettyName(to_format));
322                 return false;
323         }
324
325         vector< vector<Command>::iterator > S;
326         while (it != commands.end()) {
327                 S.push_back(it);
328                 it = (*it).previous;
329         }
330
331         string path = OnlyPath(from_file);
332         Path p(path);
333
334         bool run_latex = false;
335         string from_base = ChangeExtension(from_file, "");
336         string to_base = ChangeExtension(to_file, "");
337         string infile;
338         string outfile = from_file;
339         for (vector< vector<Command>::iterator >::reverse_iterator rit =
340                      S.rbegin(); rit != S.rend(); ++rit) {
341                 it = *rit;
342                 lyxerr << "Converting from  "
343                        << (*it).from << " to " << (*it).to << endl;
344                 infile = outfile;
345                 outfile = (*it).result_dir.empty()
346                         ? ChangeExtension(from_file, (*it).to)
347                         : AddName(subst((*it).result_dir,
348                                         "$$BaseName", from_base),
349                                   subst((*it).result_file,
350                                         "$$BaseName", OnlyFilename(from_base)));
351
352                 if ((*it).from == "tex" &&
353                     ( (*it).to == "dvi" || (*it).to == "pdf") ) {
354                         lyxrc.pdf_mode = (*it).to == "pdf";
355                         lyxerr << "Running " << (*it).command << endl;
356                         run_latex = true;
357                         if (!runLaTeX(buffer, (*it).command))
358                                 return false;
359                 } else {
360                         if ((*it).need_aux && !run_latex
361                             && !latex_command.empty()) {
362                                 lyxerr << "Running " << latex_command 
363                                        << " to update aux file"<<  endl;
364                                 runLaTeX(buffer, latex_command);
365                         }
366
367                         string infile2 = ((*it).original_dir)
368                                 ? infile : MakeRelPath(infile, path);
369                         string outfile2 = ((*it).original_dir)
370                                 ? outfile : MakeRelPath(outfile, path);
371
372                         string command = (*it).command;
373                         command = subst(command, "$$FName", infile2);
374                         command = subst(command, "$$BaseName", from_base);
375                         command = subst(command, "$$OutName", outfile2);
376
377                         if ((*it).from == "dvi" && (*it).to == "ps")
378                                 command = add_options(command,
379                                                       dvips_options(buffer));
380
381                         lyxerr << "Calling " << command << endl;
382                         ShowMessage(buffer, _("Executing command:"), command);
383                         
384                         Systemcalls one;
385                         int res;
386                         if ((*it).original_dir) {
387                                 Path p(buffer->filepath);
388                                 res = one.startscript(Systemcalls::System, command);
389                         } else
390                                 res = one.startscript(Systemcalls::System, command);
391                         if (res) {
392                                 WriteAlert(_("Can not convert file"),
393                                            "Error while executing",
394                                            command.substr(0, 50));
395                                 return false;
396                         }
397                 }
398         }
399
400         if (!(*it).result_dir.empty()) {
401                 if (view_file)
402                         *view_file = AddName(subst((*it).result_dir,
403                                                    "$$BaseName", to_base),
404                                              subst((*it).result_file,
405                                                    "$$BaseName", OnlyFilename(to_base)));
406                 if (from_base != to_base) {
407                         string from = subst((*it).result_dir,
408                                             "$$BaseName", from_base);
409                         string to = subst((*it).result_dir,
410                                           "$$BaseName", to_base);
411                         return lyx::rename(from, to);
412                 }
413
414         } else if (outfile != to_file)
415                 if ((*it).from == "tex" &&
416                     ( (*it).to == "dvi" || (*it).to == "pdf") )
417                         return lyx::copy(outfile, to_file);
418                 else
419                         return lyx::rename(outfile, to_file);
420
421         return true;
422 }
423
424
425 string const Converter::SplitFormat(string const & str, string & format)
426 {
427         string using_format = split(str, format, ':');
428         if (format.empty())
429                 format = "dvi";
430         return using_format;
431 }
432
433
434 bool Converter::runLaTeX(Buffer * buffer, string const & command)
435 {
436         
437         BufferView * bv = buffer->getUser();
438
439         if (!bv->text) return 0;
440
441         ProhibitInput(bv);
442
443         string name = buffer->getLatexName();
444
445         bv->owner()->getMiniBuffer()->Set(_("Running LaTeX..."));   
446
447         // Remove all error insets
448         bool a = bv->removeAutoInsets();
449
450         // do the LaTex run(s)
451         TeXErrors terr;
452         LaTeX latex(command, name, buffer->filepath);
453         int result = latex.run(terr,
454                             bv->owner()->getMiniBuffer()); // running latex
455
456         if ((result & LaTeX::ERRORS)) {
457                 // Insert all errors as errors boxes
458                 bv->insertErrors(terr);
459         }
460
461         // if we removed error insets before we ran LaTeX or if we inserted
462         // error insets after we ran LaTeX this must be run:
463         if (a || (result & LaTeX::ERRORS)){
464                 bv->redraw();
465                 bv->fitCursor();
466                 //bv->updateScrollbar();
467         }
468
469         // check return value from latex.run().
470         if ((result & LaTeX::NO_LOGFILE)) {
471                 WriteAlert(_("LaTeX did not work!"),
472                            _("Missing log file:"), name);
473         } else if ((result & LaTeX::ERRORS)) {
474                 int num_errors = latex.getNumErrors();
475                 string s;
476                 string t;
477                 if (num_errors == 1) {
478                         s = _("One error detected");
479                         t = _("You should try to fix it.");
480                 } else {
481                         s = tostr(num_errors);
482                         s += _(" errors detected.");
483                         t = _("You should try to fix them.");
484                 }
485                 WriteAlert(_("There were errors during the LaTeX run."),
486                            s, t);
487         }
488         AllowInput(bv);
489  
490         return (result & (LaTeX::NO_LOGFILE | LaTeX::ERRORS)) == 0;
491
492 }
493
494
495 string const Converter::dvi_papersize(Buffer const * buffer)
496 {
497         char real_papersize = buffer->params.papersize;
498         if (real_papersize == BufferParams::PAPER_DEFAULT)
499                 real_papersize = lyxrc.default_papersize;
500
501         switch (real_papersize) {
502         case BufferParams::PAPER_A3PAPER:
503                 return "a3";
504         case BufferParams::PAPER_A4PAPER:
505                 return "a4";
506         case BufferParams::PAPER_A5PAPER:
507                 return "a5";
508         case BufferParams::PAPER_B5PAPER:
509                 return "b5";
510         case BufferParams::PAPER_EXECUTIVEPAPER:
511                 return "foolscap";
512         case BufferParams::PAPER_LEGALPAPER:
513                 return "legal";
514         case BufferParams::PAPER_USLETTER:
515         default:
516                 return "us";
517         }
518 }
519
520
521 string const Converter::dvips_options(Buffer const * buffer)
522 {
523         string result;
524         if (buffer->params.use_geometry
525             && buffer->params.papersize2 == BufferParams::VM_PAPER_CUSTOM
526             && !lyxrc.print_paper_dimension_flag.empty()
527             && !buffer->params.paperwidth.empty()
528             && !buffer->params.paperheight.empty()) {
529                 // using a custom papersize
530                 result = lyxrc.print_paper_dimension_flag;
531                 result += ' ' + buffer->params.paperwidth;
532                 result += ',' + buffer->params.paperheight;
533         } else {
534                 string paper_option = dvi_papersize(buffer);
535                 if (paper_option == "us")
536                         paper_option = "letter";
537                 if (paper_option != "letter" ||
538                     buffer->params.orientation != BufferParams::ORIENTATION_LANDSCAPE) {
539                         // dvips won't accept -t letter -t landscape.  In all other
540                         // cases, include the paper size explicitly.
541                         result = lyxrc.print_paper_flag;
542                         result += ' ' + paper_option;
543                 }
544         }
545         if (buffer->params.orientation == BufferParams::ORIENTATION_LANDSCAPE)
546                 result += ' ' + lyxrc.print_landscape_flag;
547         return result;
548 }