1 /*
2 For general Scribus (>=1.3.2) copyright and licensing information please refer
3 to the COPYING file provided with the program. Following this notice may exist
4 a copyright and/or license notice that predates the release of Scribus 1.3.2
5 for which a new license (GPL+exception) is in place.
6 */
7 #include "collect4output.h"
8
9 #include "scribusdoc.h"
10 #include "scribuscore.h"
11 #include "util.h"
12 #include "prefscontext.h"
13 #include "prefsfile.h"
14 #include "prefsmanager.h"
15 #include "commonstrings.h"
16 #include "undomanager.h"
17 #include "filewatcher.h"
18 #include "pageitem.h"
19 #ifdef HAVE_OSG
20 #include "pageitem_osgframe.h"
21 #endif
22 #include "scraction.h"
23 #include "scpattern.h"
24 #include "util_file.h"
25
26 #include <QDebug>
27 #include <QDir>
28 #include <QMap>
29 #include <QMessageBox>
30 #include <QProgressBar>
31 #include <QString>
32
CollectForOutput(ScribusDoc * doc,const QString & outputDirectory,bool withFonts,bool withProfiles,bool compressDoc)33 CollectForOutput::CollectForOutput(ScribusDoc* doc, const QString& outputDirectory, bool withFonts, bool withProfiles, bool compressDoc)
34 : QObject(ScCore)
35 {
36 m_Doc=doc;
37 if (!outputDirectory.isEmpty())
38 m_outputDirectory=outputDirectory;
39 m_compressDoc = compressDoc;
40 m_withFonts = withFonts;
41 m_withProfiles = withProfiles;
42 dirs = PrefsManager::instance().prefsFile->getContext("dirs");
43 collectedFiles.clear();
44
45 if (m_withFonts)
46 fontCount = m_Doc->UsedFonts.count();
47 if (m_withProfiles)
48 m_Doc->getUsedProfiles(docProfiles);
49 profileCount = m_withProfiles ? docProfiles.count() : 0;
50 itemCount= m_Doc->MasterItems.count()+m_Doc->DocItems.count()+m_Doc->FrameItems.count();
51 patterns = m_Doc->getUsedPatterns();
52 patternCount=patterns.count();
53 }
54
newDirDialog()55 bool CollectForOutput::newDirDialog()
56 {
57 if (ScCore->usingGUI())
58 {
59 QString wdir = ".";
60 QString prefsDocDir = PrefsManager::instance().documentDir();
61 if (!prefsDocDir.isEmpty())
62 wdir = dirs->get("collect", prefsDocDir);
63 else
64 wdir = dirs->get("collect", ".");
65 m_outputDirectory = ScCore->primaryMainWindow()->CFileDialog(wdir, tr("Choose a Directory"), "", "", fdDirectoriesOnly, &m_compressDoc, &m_withFonts, &m_withProfiles);
66 }
67 if (m_outputDirectory.isEmpty())
68 return false;
69 if (!m_outputDirectory.endsWith("/"))
70 m_outputDirectory += "/";
71
72 docProfiles.clear();
73 if (m_withProfiles)
74 m_Doc->getUsedProfiles(docProfiles);
75 fontCount = m_withFonts ? m_Doc->UsedFonts.count() : 0;
76 profileCount = m_withProfiles ? docProfiles.count() : 0;
77
78 QStringList directories;
79 directories.append(m_outputDirectory);
80 directories.append(m_outputDirectory + "images/");
81 if (m_withFonts)
82 directories.append(m_outputDirectory + "fonts/");
83 if (m_withProfiles)
84 directories.append(m_outputDirectory + "profiles/");
85
86 for (int i = 0; i < directories.count(); ++i)
87 {
88 QDir dir(directories[i]);
89 if (dir.exists()) continue;
90
91 bool created = dir.mkpath(directories[i]);
92 if (!created)
93 {
94 ScMessageBox::warning(ScCore->primaryMainWindow(), CommonStrings::trWarning,
95 "<qt>" + tr("Cannot create directory:\n%1").arg(directories[i]) + "</qt>");
96 return false;
97 }
98 }
99 return true;
100 }
101
collect(QString & newFileName)102 QString CollectForOutput::collect(QString &newFileName)
103 {
104 if (!newDirDialog())
105 return "Collect cancelled or unable to create collect destination directory";
106 ScCore->fileWatcher->forceScan();
107 ScCore->fileWatcher->stop();
108 dirs->set("collect", m_outputDirectory.left(m_outputDirectory.lastIndexOf("/",-2)));
109 ScCore->primaryMainWindow()->setStatusBarInfoText( tr("Collecting..."));
110
111 if (!collectItems())
112 {
113 QString errorMsg( tr("Cannot collect all files for output for file:\n%1").arg(newName) );
114 ScMessageBox::warning(ScCore->primaryMainWindow(), CommonStrings::trWarning,
115 "<qt>" + errorMsg + "</qt>");
116 return errorMsg;
117 }
118
119 if (m_withFonts)
120 collectFonts();
121 if (m_withProfiles)
122 collectProfiles();
123
124 /* collect document must go last because of image paths changes in collectItems() */
125 if (!collectDocument())
126 {
127 QString errorMsg( tr("Cannot collect the file: \n%1").arg(newName) );
128 ScMessageBox::warning(ScCore->primaryMainWindow(), CommonStrings::trWarning, "<qt>" + errorMsg + "</qt>");
129 return errorMsg;
130 }
131
132 QDir::setCurrent(m_outputDirectory);
133 ScCore->primaryMainWindow()->updateActiveWindowCaption(newName);
134 UndoManager::instance()->renameStack(newName);
135 ScCore->primaryMainWindow()->scrActions["fileSave"]->setEnabled(false);
136 ScCore->primaryMainWindow()->scrActions["fileRevert"]->setEnabled(false);
137 ScCore->primaryMainWindow()->updateRecent(newName);
138 ScCore->primaryMainWindow()->setStatusBarInfoText("");
139 ScCore->primaryMainWindow()->mainWindowProgressBar->reset();
140 ScCore->fileWatcher->start();
141 collectedFiles.clear();
142 newFileName = newName;
143
144 return QString();
145 }
146
collectDocument()147 bool CollectForOutput::collectDocument()
148 {
149 QFileInfo fi = QFileInfo(m_outputDirectory);
150 newName = m_outputDirectory;
151 if (!fi.exists())
152 return false;
153 if (!fi.isDir() || !fi.isWritable())
154 return false;
155
156 if ((m_Doc->hasName) && (!m_Doc->isConverted))
157 {
158 QFileInfo fis(m_Doc->documentFileName());
159 newName += fis.fileName();
160 }
161 else
162 newName += m_Doc->documentFileName() + ".sla";
163
164 m_Doc->hasName = true;
165 if (m_compressDoc)
166 {
167 if (!newName.endsWith(".gz"))
168 newName += ".gz";
169 }
170 else
171 {
172 if (newName.endsWith(".gz"))
173 newName = newName.remove(".gz");
174 }
175
176 if (!overwrite(ScCore->primaryMainWindow(), newName))
177 return false;
178 if (!ScCore->primaryMainWindow()->DoFileSave(newName))
179 return false;
180 return true;
181 }
182
collectItems()183 bool CollectForOutput::collectItems()
184 {
185 uint counter = 0;
186 int c=0;
187 QList<PageItem*> allItems;
188 PageItem* ite = nullptr;
189 for (uint lc = 0; lc < 2; ++lc)
190 {
191 switch (lc)
192 {
193 case 0:
194 counter = m_Doc->MasterItems.count();
195 break;
196 case 1:
197 counter = m_Doc->DocItems.count();
198 break;
199 }
200 for (uint b = 0; b < counter; ++b)
201 {
202 switch (lc)
203 {
204 case 0:
205 ite = m_Doc->MasterItems.at(b);
206 break;
207 case 1:
208 ite = m_Doc->DocItems.at(b);
209 break;
210 }
211 if (ite->isGroup())
212 allItems = ite->getAllChildren();
213 else
214 allItems.append(ite);
215 for (int ii = 0; ii < allItems.count(); ii++)
216 {
217 ite = allItems.at(ii);
218 processItem(ite);
219 }
220 allItems.clear();
221 if (uiCollect)
222 emit itemsCollected(c++);
223 }
224 }
225 for (auto itf = m_Doc->FrameItems.begin(); itf != m_Doc->FrameItems.end(); ++itf)
226 {
227 PageItem *it = itf.value();
228 if (it->isGroup())
229 allItems = it->asGroupFrame()->getAllChildren();
230 else
231 allItems.append(it);
232 for (int ii = 0; ii < allItems.count(); ii++)
233 {
234 it = allItems.at(ii);
235 processItem(it);
236 }
237 allItems.clear();
238 if (uiCollect)
239 emit itemsCollected(c++);
240 }
241 for (int c = 0; c < patterns.count(); ++c)
242 {
243 ScPattern pa = m_Doc->docPatterns[patterns[c]];
244 for (int o = 0; o < pa.items.count(); o++)
245 {
246 ite = pa.items.at(o);
247 if (ite->isGroup())
248 allItems = ite->getAllChildren();
249 else
250 allItems.append(ite);
251 for (int ii = 0; ii < allItems.count(); ii++)
252 {
253 ite = allItems.at(ii);
254 processItem(ite);
255 }
256 allItems.clear();
257 }
258 if (uiCollect)
259 emit patternsCollected(c);
260 }
261 return true;
262 }
263
processItem(PageItem * ite)264 void CollectForOutput::processItem(PageItem *ite)
265 {
266 if (ite->isImageFrame())
267 {
268 /* hack for subsequent c4o "./" -> "/doc/full/path" */
269 if (!ite->isInlineImage)
270 {
271 QString ofName(ite->Pfile);
272 QFileInfo itf = QFileInfo(ofName);
273 if (!itf.exists())
274 {
275 ofName = QDir::toNativeSeparators(PrefsManager::instance().documentDir() + "/" + ofName);
276 itf.setFile(ofName);
277 }
278 // end of hack
279 if (itf.exists())
280 {
281 const QString& oldFile = ofName;
282 ite->Pfile = collectFile(oldFile, itf.fileName());
283 ScCore->fileWatcher->removeFile(oldFile);
284 ScCore->fileWatcher->addFile(ite->Pfile);
285 }
286 }
287 }
288 #ifdef HAVE_OSG
289 if (ite->isOSGFrame())
290 {
291 PageItem_OSGFrame *osgframe = ite->asOSGFrame();
292 QString ofName(osgframe->modelFile);
293 QFileInfo itf = QFileInfo(ofName);
294 if (!itf.exists())
295 {
296 ofName = QDir::toNativeSeparators(PrefsManager::instance().documentDir() + "/" + ofName);
297 itf.setFile(ofName);
298 }
299 if (itf.exists())
300 {
301 QString oldFile = ofName;
302 osgframe->modelFile = collectFile(oldFile, itf.fileName());
303 }
304 }
305 #endif
306 if (ite->isTextFrame())
307 {
308 if (ite->isAnnotation())
309 {
310 QFileInfo itf;
311 if (!ite->Pfile.isEmpty())
312 {
313 if (!ite->isInlineImage)
314 {
315 itf = QFileInfo(ite->Pfile);
316 if (itf.exists())
317 {
318 QString oldFile = ite->Pfile;
319 ite->Pfile = collectFile(oldFile, itf.fileName());
320 ScCore->fileWatcher->removeFile(oldFile);
321 ScCore->fileWatcher->addFile(ite->Pfile);
322 }
323 }
324 }
325 if (!ite->Pfile2.isEmpty())
326 {
327 itf = QFileInfo(ite->Pfile2);
328 if (itf.exists())
329 ite->Pfile2 = collectFile(ite->Pfile2, itf.fileName());
330 }
331 if (!ite->Pfile3.isEmpty())
332 {
333 itf = QFileInfo(ite->Pfile3);
334 if (itf.exists())
335 ite->Pfile3 = collectFile(ite->Pfile3, itf.fileName());
336 }
337 }
338 }
339 }
340
collectFonts()341 bool CollectForOutput::collectFonts()
342 {
343 PrefsManager& prefsManager = PrefsManager::instance();
344 QMap<QString,int>::Iterator it3;
345 QMap<QString,int>::Iterator it3end = m_Doc->UsedFonts.end();
346 int c=0;
347 for (it3 = m_Doc->UsedFonts.begin(); it3 != it3end; ++it3)
348 {
349 QFileInfo itf(prefsManager.appPrefs.fontPrefs.AvailFonts[it3.key()].fontFilePath());
350 QString oldFileITF(prefsManager.appPrefs.fontPrefs.AvailFonts[it3.key()].fontFilePath());
351 QString outFileITF(m_outputDirectory + "fonts/" + itf.fileName());
352 bool success = copyFileAtomic(oldFileITF, outFileITF);
353 if (!success)
354 qDebug()<<"CollectForOutput::collectFile copyFileAtomic failed for"<<oldFileITF<<"to"<<outFileITF;
355 #ifndef Q_OS_WIN32
356 else
357 {
358 QFile of(outFileITF);
359 if (of.exists())
360 {
361 bool permsSet=of.setPermissions(QFile::permissions(oldFileITF));
362 if (!permsSet)
363 qDebug()<<"Unable to set permissions successfully while collecting for output on"<<outFileITF;
364 }
365 else
366 qDebug()<<"Unable to set permissions successfully while collecting for output on"<<outFileITF<<"as the file does not exist";
367 }
368 #endif
369 if (prefsManager.appPrefs.fontPrefs.AvailFonts[it3.key()].type() == ScFace::TYPE1)
370 {
371 QStringList metrics;
372 QString fontDir = itf.absolutePath();
373 QString fontFile = itf.fileName();
374 metrics += findFontMetrics(fontDir, fontFile);
375 if ( metrics.empty() )
376 {
377 QDir dir;
378 if (dir.exists(fontDir + "/AFMs"))
379 metrics += findFontMetrics(fontDir + "/AFMs", fontFile);
380 if (dir.exists(fontDir + "/afm") && metrics.empty())
381 metrics += findFontMetrics(fontDir + "/afm", fontFile);
382 if (dir.exists(fontDir + "/Pfm") && metrics.empty())
383 metrics += findFontMetrics(fontDir + "/Pfm", fontFile);
384 if (dir.exists(fontDir + "/pfm") && metrics.empty())
385 metrics += findFontMetrics(fontDir + "/pfm", fontFile);
386 }
387 for (int a = 0; a < metrics.size(); a++)
388 {
389 QString origAFM = metrics[a];
390 QFileInfo fi(origAFM);
391 QString outFileAFM(m_outputDirectory + "fonts/" + fi.fileName());
392 bool success = copyFileAtomic(origAFM, outFileAFM);
393 if (!success)
394 qDebug()<<"CollectForOutput::collectFile copyFileAtomic failed for"<<origAFM<<"to"<<outFileAFM;
395 #ifndef Q_OS_WIN32
396 else
397 {
398 QFile of(outFileAFM);
399 if (of.exists())
400 {
401 bool permsSet=of.setPermissions(QFile::permissions(origAFM));
402 if (!permsSet)
403 qDebug()<<"Unable to set permissions successfully while collecting for output on"<<outFileAFM;
404 }
405 else
406 qDebug()<<"Unable to set permissions successfully while collecting for output on"<<outFileAFM<<"as the file does not exist";
407 }
408 #endif
409 }
410 }
411 if (uiCollect)
412 emit fontsCollected(c++);
413 }
414 return true;
415 }
416
findFontMetrics(const QString & baseDir,const QString & baseName) const417 QStringList CollectForOutput::findFontMetrics(const QString& baseDir, const QString& baseName) const
418 {
419 QStringList metricsFiles;
420 QString basePath = baseDir + "/" + baseName;
421 QString afnm(basePath);
422 afnm.chop(3);
423
424 // Look for afm files
425 QString afmName(afnm+"afm");
426 if (QFile::exists(afmName))
427 metricsFiles.append(afmName);
428 else
429 {
430 afmName = afnm+"Afm";
431 if (QFile::exists(afmName))
432 metricsFiles.append(afmName);
433 else
434 {
435 afmName = afnm+"AFM";
436 if (QFile::exists(afmName))
437 metricsFiles.append(afmName);
438 }
439 }
440 // Look for pfm files
441 QString pfmName(afnm+"pfm");
442 if (QFile::exists(pfmName))
443 metricsFiles.append(pfmName);
444 else
445 {
446 pfmName = afnm+"Pfm";
447 if (QFile::exists(pfmName))
448 metricsFiles.append(pfmName);
449 else
450 {
451 pfmName = afnm+"PFM";
452 if (QFile::exists(pfmName))
453 metricsFiles.append(pfmName);
454 }
455 }
456 return metricsFiles;
457 }
458
collectProfiles()459 bool CollectForOutput::collectProfiles()
460 {
461 int c = 0;
462 ProfilesL::Iterator itend = docProfiles.end();
463 for (ProfilesL::Iterator it = docProfiles.begin(); it != itend; ++it)
464 {
465 QString oldFile(it.value());
466 QString outFile(m_outputDirectory + "profiles/" + QFileInfo(oldFile).fileName());
467 bool success = copyFileAtomic(oldFile, outFile);
468 if (!success)
469 qDebug()<<"CollectForOutput::collectFile copyFileAtomic failed for"<<oldFile<<"to"<<outFile;
470 #ifndef Q_OS_WIN32
471 else
472 {
473 QFile of(outFile);
474 if (of.exists())
475 {
476 bool permsSet=of.setPermissions(QFile::permissions(oldFile));
477 if (!permsSet)
478 qDebug()<<"Unable to set permissions successfully while collecting for output on"<<outFile;
479 }
480 else
481 qDebug()<<"Unable to set permissions successfully while collecting for output on"<<outFile<<"as the file does not exist";
482 }
483 #endif
484 if (uiCollect)
485 emit profilesCollected(c++);
486 }
487 return true;
488 }
489
collectFile(const QString & oldFile,QString newFile)490 QString CollectForOutput::collectFile(const QString& oldFile, QString newFile)
491 {
492 uint cnt = 1;
493 bool copy = true;
494
495 while (collectedFiles.contains(newFile))
496 {
497 // overwrite only different sources
498 if (collectedFiles[newFile] == oldFile)
499 {
500 copy = false;
501 break;
502 }
503 QFileInfo fi(newFile);
504 QString basename = fi.baseName().left(fi.baseName().lastIndexOf("_"));
505 newFile = QString("%1_%2.%3").arg(basename).arg(cnt).arg(fi.completeSuffix());
506 ++cnt;
507 }
508 if (copy)
509 {
510 QString outFile(m_outputDirectory + "images/" + newFile);
511 bool success = copyFileAtomic(oldFile, outFile);
512 if (!success)
513 qDebug()<<"CollectForOutput::collectFile copyFileAtomic failed for"<<oldFile<<"to"<<outFile;
514 #ifndef Q_OS_WIN32
515 else
516 {
517 QFile of(outFile);
518 if (of.exists())
519 {
520 bool permsSet=of.setPermissions(QFile::permissions(oldFile));
521 if (!permsSet)
522 qDebug()<<"Unable to set permissions successfully while collecting for output on"<<outFile;
523 }
524 else
525 qDebug()<<"Unable to set permissions successfully while collecting for output on"<<outFile<<"as the file does not exist";
526 }
527 #endif
528 }
529 collectedFiles[newFile] = oldFile;
530 return m_outputDirectory + "images/" + newFile;
531 }
532