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