]> git.lyx.org Git - lyx.git/blob - src/exporter.C
Fix #1736
[lyx.git] / src / exporter.C
1 /**
2  * \file exporter.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author unknown
7  * \author Alfredo Braunstein
8  * \author Lars Gullik Bjønnes
9  * \author Jean Marc Lasgouttes
10  * \author Angus Leeming
11  * \author John Levon
12  * \author André Pönitz
13  *
14  * Full author contact details are available in file CREDITS.
15  */
16
17 #include <config.h>
18
19 #include "exporter.h"
20
21 #include "buffer.h"
22 #include "buffer_funcs.h"
23 #include "bufferparams.h"
24 #include "converter.h"
25 #include "format.h"
26 #include "gettext.h"
27 #include "lyxrc.h"
28 #include "mover.h"
29 #include "output_plaintext.h"
30 #include "outputparams.h"
31 #include "frontends/Alert.h"
32
33 #include "support/FileInfo.h"
34 #include "support/filetools.h"
35 #include "support/lyxlib.h"
36 #include "support/os.h"
37
38 using lyx::support::AddName;
39 using lyx::support::bformat;
40 using lyx::support::ChangeExtension;
41 using lyx::support::contains;
42 using lyx::support::getFormatFromContents;
43 using lyx::support::MakeAbsPath;
44 using lyx::support::MakeDisplayPath;
45 using lyx::support::OnlyFilename;
46 using lyx::support::OnlyPath;
47 using lyx::support::prefixIs;
48
49 using std::find;
50 using std::string;
51 using std::vector;
52
53
54 namespace {
55
56 vector<string> const Backends(Buffer const & buffer)
57 {
58         vector<string> v;
59         if (buffer.params().getLyXTextClass().isTeXClassAvailable())
60                 v.push_back(BufferFormat(buffer));
61         v.push_back("text");
62         return v;
63 }
64
65
66 /// ask the user what to do if a file already exists
67 int checkOverwrite(string const & filename)
68 {
69         if (lyx::support::FileInfo(filename, true).exist()) {
70                 string text = bformat(_("The file %1$s already exists.\n\n"
71                                         "Do you want to over-write that file?"),
72                                       MakeDisplayPath(filename));
73                 return Alert::prompt(_("Over-write file?"),
74                                      text, 0, 2,
75                                      _("&Over-write"), _("Over-write &all"),
76                                      _("&Cancel export"));
77         }
78         return 0;
79 }
80
81
82 enum CopyStatus {
83         SUCCESS,
84         FORCE,
85         CANCEL
86 };
87
88
89 /** copy file \p sourceFile to \p destFile. If \p force is false, the user
90  *  will be asked before existing files are overwritten.
91  *  \return
92  *  - SUCCESS if this file got copied
93  *  - FORCE   if subsequent calls should not ask for confirmation before
94  *            overwriting files anymore.
95  *  - CANCEL  if the export should be cancelled
96  */
97 CopyStatus copyFile(string const & format,
98                     string const & sourceFile, string const & destFile,
99                     string const & latexFile, bool force)
100 {
101         CopyStatus ret = force ? FORCE : SUCCESS;
102
103         // Only copy files that are in our tmp dir, all other files would
104         // overwrite themselves. This check could be changed to
105         // boost::filesystem::equivalent(sourceFile, destFile) if export to
106         // other directories than the document directory is desired.
107         if (!prefixIs(OnlyPath(sourceFile), lyx::support::os::getTmpDir()))
108                 return ret;
109
110         if (!force) {
111                 switch(checkOverwrite(destFile)) {
112                 case 0:
113                         ret = SUCCESS;
114                         break;
115                 case 1:
116                         ret = FORCE;
117                         break;
118                 default:
119                         return CANCEL;
120                 }
121         }
122
123         Mover const & mover = movers(format);
124         if (!mover.copy(sourceFile, destFile, latexFile))
125                 Alert::error(_("Couldn't copy file"),
126                              bformat(_("Copying %1$s to %2$s failed."),
127                                      MakeDisplayPath(sourceFile),
128                                      MakeDisplayPath(destFile)));
129
130         return ret;
131 }
132
133 } //namespace anon
134
135
136 bool Exporter::Export(Buffer * buffer, string const & format,
137                       bool put_in_tempdir, string & result_file)
138 {
139         string backend_format;
140         OutputParams runparams;
141         runparams.flavor = OutputParams::LATEX;
142         runparams.linelen = lyxrc.ascii_linelen;
143         vector<string> backends = Backends(*buffer);
144         if (find(backends.begin(), backends.end(), format) == backends.end()) {
145                 for (vector<string>::const_iterator it = backends.begin();
146                      it != backends.end(); ++it) {
147                         Graph::EdgePath p =
148                                 converters.getPath(*it, format);
149                         if (!p.empty()) {
150                                 runparams.flavor = converters.getFlavor(p);
151                                 backend_format = *it;
152                                 break;
153                         }
154                 }
155                 if (backend_format.empty()) {
156                         Alert::error(_("Couldn't export file"),
157                                 bformat(_("No information for exporting the format %1$s."),
158                                    formats.prettyName(format)));
159                         return false;
160                 }
161         } else
162                 backend_format = format;
163
164         string filename = buffer->getLatexName(false);
165         filename = AddName(buffer->temppath(), filename);
166         filename = ChangeExtension(filename,
167                                    formats.extension(backend_format));
168
169         // Ascii backend
170         if (backend_format == "text")
171                 writeFileAscii(*buffer, filename, runparams);
172         // Linuxdoc backend
173         else if (buffer->isLinuxDoc()) {
174                 runparams.nice = !put_in_tempdir;
175                 buffer->makeLinuxDocFile(filename, runparams);
176         }
177         // Docbook backend
178         else if (buffer->isDocBook()) {
179                 runparams.nice = !put_in_tempdir;
180                 buffer->makeDocBookFile(filename, runparams);
181         }
182         // LaTeX backend
183         else if (backend_format == format) {
184                 runparams.nice = true;
185                 buffer->makeLaTeXFile(filename, string(), runparams);
186         } else if (contains(buffer->filePath(), ' ')) {
187                 Alert::error(_("File name error"),
188                            _("The directory path to the document cannot contain spaces."));
189                 return false;
190         } else {
191                 runparams.nice = false;
192                 buffer->makeLaTeXFile(filename, buffer->filePath(), runparams);
193         }
194
195         if (!converters.convert(buffer, filename, filename,
196                                 backend_format, format, result_file))
197                 return false;
198
199         if (!put_in_tempdir) {
200                 string const tmp_result_file = result_file;
201                 result_file = ChangeExtension(buffer->fileName(),
202                                               formats.extension(format));
203                 // We need to copy referenced files (e. g. included graphics
204                 // if format == "dvi") to the result dir.
205                 vector<ExportedFile> const files =
206                         runparams.exportdata->externalFiles(format);
207                 string const dest = OnlyPath(result_file);
208                 CopyStatus status = SUCCESS;
209                 for (vector<ExportedFile>::const_iterator it = files.begin();
210                                 it != files.end() && status != CANCEL; ++it) {
211                         string const fmt = getFormatFromContents(it->sourceName);
212                         status = copyFile(fmt, it->sourceName,
213                                           MakeAbsPath(it->exportName, dest),
214                                           it->exportName, status == FORCE);
215                 }
216                 if (status == CANCEL) {
217                         buffer->message(_("Document export cancelled."));
218                 } else {
219                         // Finally copy the main file
220                         status = copyFile(format, tmp_result_file,
221                                           result_file, result_file,
222                                           status == FORCE);
223                         buffer->message(bformat(_("Document exported as %1$s"
224                                                   "to file `%2$s'"),
225                                                 formats.prettyName(format),
226                                                 MakeDisplayPath(result_file)));
227                 }
228         }
229
230         return true;
231 }
232
233
234 bool Exporter::Export(Buffer * buffer, string const & format,
235                       bool put_in_tempdir)
236 {
237         string result_file;
238         return Export(buffer, format, put_in_tempdir, result_file);
239 }
240
241
242 bool Exporter::Preview(Buffer * buffer, string const & format)
243 {
244         string result_file;
245         if (!Export(buffer, format, true, result_file))
246                 return false;
247         return formats.view(*buffer, result_file, format);
248 }
249
250
251 bool Exporter::IsExportable(Buffer const & buffer, string const & format)
252 {
253         vector<string> backends = Backends(buffer);
254         for (vector<string>::const_iterator it = backends.begin();
255              it != backends.end(); ++it)
256                 if (converters.isReachable(*it, format))
257                         return true;
258         return false;
259 }
260
261
262 vector<Format const *> const
263 Exporter::GetExportableFormats(Buffer const & buffer, bool only_viewable)
264 {
265         vector<string> backends = Backends(buffer);
266         vector<Format const *> result =
267                 converters.getReachable(backends[0], only_viewable, true);
268         for (vector<string>::const_iterator it = backends.begin() + 1;
269              it != backends.end(); ++it) {
270                 vector<Format const *>  r =
271                         converters.getReachable(*it, only_viewable, false);
272                 result.insert(result.end(), r.begin(), r.end());
273         }
274         return result;
275 }
276
277
278 ExportedFile::ExportedFile(string const & s, string const & e) :
279         sourceName(s), exportName(e) {}
280
281
282 bool operator==(ExportedFile const & f1, ExportedFile const & f2)
283 {
284         return f1.sourceName == f2.sourceName &&
285                f1.exportName == f2.exportName;
286
287 }
288
289
290 void ExportData::addExternalFile(string const & format,
291                                  string const & sourceName,
292                                  string const & exportName)
293 {
294         BOOST_ASSERT(lyx::support::AbsolutePath(sourceName));
295
296         // Make sure that we have every file only once, otherwise copyFile()
297         // would ask several times if it should overwrite a file.
298         vector<ExportedFile> & files = externalfiles[format];
299         ExportedFile file(sourceName, exportName);
300         if (find(files.begin(), files.end(), file) == files.end())
301                 files.push_back(file);
302 }
303
304
305 void ExportData::addExternalFile(string const & format,
306                                  string const & sourceName)
307 {
308         addExternalFile(format, sourceName, OnlyFilename(sourceName));
309 }
310
311
312 vector<ExportedFile> const
313 ExportData::externalFiles(string const & format) const
314 {
315         FileMap::const_iterator cit = externalfiles.find(format);
316         if (cit != externalfiles.end())
317                 return cit->second;
318         return vector<ExportedFile>();
319 }