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 /***************************************************************************
8 util.cpp - description
9 -------------------
10 begin : Fri Sep 14 2001
11 copyright : (C) 2001 by Franz Schmid
12 email : Franz.Schmid@altmuehlnet.de
13 ***************************************************************************/
14
15 /***************************************************************************
16 * *
17 * This program is free software; you can redistribute it and/or modify *
18 * it under the terms of the GNU General Public License as published by *
19 * the Free Software Foundation; either version 2 of the License, or *
20 * (at your option) any later version. *
21 * *
22 ***************************************************************************/
23
24 #include <algorithm>
25 #include "util.h"
26 #include <zlib.h>
27 #include <qglobal.h>
28 #include <QApplication>
29 #include <QCryptographicHash>
30 #include <QDomElement>
31 #include <QMessageBox>
32 #include <QProcess>
33 #include <QSignalBlocker>
34
35 #include "pageitem.h"
36 #include "pageitem_table.h"
37 #include "scribusview.h"
38 #include "scribusdoc.h"
39 #include "scpainter.h"
40 #include "ui/scmessagebox.h"
41
42 #include <csignal>
43
44 #if !defined(_WIN32) && !defined(Q_OS_MAC)
45 #include <execinfo.h>
46 #include <cxxabi.h>
47 #endif
48 #if defined(_WIN32)
49 #include <windows.h>
50 #endif
51
52 using namespace std;
53
cleanupLang(const QString & lang)54 QString cleanupLang(const QString& lang)
55 {
56 int dotIndex = lang.indexOf(QChar('.'));
57 if (dotIndex < 0)
58 return lang;
59
60 return lang.left(dotIndex);
61 }
62
System(const QString & exename,const QStringList & args,const QString & fileStdErr,const QString & fileStdOut,const bool * cancel)63 int System(const QString& exename, const QStringList & args, const QString& fileStdErr, const QString& fileStdOut, const bool* cancel)
64 {
65 QProcess proc;
66 if (!fileStdOut.isEmpty())
67 proc.setStandardOutputFile(fileStdOut);
68 if (!fileStdErr.isEmpty())
69 proc.setStandardErrorFile(fileStdErr);
70 proc.start(exename, args);
71 if (proc.waitForStarted(15000))
72 {
73 while (!proc.waitForFinished(15000))
74 {
75 qApp->processEvents();
76 if (cancel && (*cancel))
77 {
78 proc.kill();
79 break;
80 }
81 }
82 }
83 if (cancel && (*cancel))
84 return -1;
85 return proc.exitCode();
86 }
87
88 // On Windows, return short path name, else return longPath;
getShortPathName(const QString & longPath)89 QString getShortPathName(const QString & longPath)
90 {
91 QString shortPath(longPath);
92 #if defined _WIN32
93 QFileInfo fInfo(longPath);
94 if (fInfo.exists())
95 {
96 WCHAR shortName[MAX_PATH + 1];
97 // An error should not be blocking as ERROR_INVALID_PARAMETER can simply mean
98 // that volume does not support 8.3 filenames, so return longPath in this case
99 QString nativePath = QDir::toNativeSeparators(longPath);
100 int ret = GetShortPathNameW((LPCWSTR) nativePath.utf16(), shortName, MAX_PATH);
101 if (ret != ERROR_INVALID_PARAMETER && ret < MAX_PATH)
102 shortPath = QString::fromUtf16((const ushort*) shortName);
103 }
104 #endif
105 return shortPath;
106 }
107
108 // On Windows, return short path name, else return longPath;
getLongPathName(const QString & shortPath)109 QString getLongPathName(const QString & shortPath)
110 {
111 QString longPath(shortPath);
112 #if defined _WIN32
113 QFileInfo fInfo(longPath);
114 if (fInfo.exists())
115 {
116 WCHAR longName[MAX_PATH + 1];
117 // An error should not be blocking as ERROR_INVALID_PARAMETER can simply mean
118 // that volume does not support long filenames, so return shortPath in this case
119 QString nativePath = QDir::toNativeSeparators(shortPath);
120 int ret = GetLongPathNameW((LPCWSTR) nativePath.utf16(), longName, MAX_PATH);
121 if (ret != ERROR_INVALID_PARAMETER && ret < MAX_PATH)
122 longPath = QString::fromUtf16((const ushort*) longName);
123 }
124 #endif
125 return longPath;
126 }
127
128 // Legacy implementation of LoadText with incorrect
129 // handling of unicode data. This should be retired.
130 // Use loadRawText instead.
131 // FIXME XXX
132 //
loadText(const QString & filename,QString * buffer)133 bool loadText(const QString& filename, QString *buffer)
134 {
135 QFile f(filename);
136 QFileInfo fi(f);
137 if (!fi.exists())
138 return false;
139 QByteArray bb(f.size(), ' ');
140 if (!f.open(QIODevice::ReadOnly))
141 return false;
142 f.read(bb.data(), f.size());
143 f.close();
144 for (int i = 0; i < bb.size(); ++i)
145 *buffer += QChar(bb[i]);
146 /*
147 int len = bb.size();
148 int oldLen = Buffer->length();
149 Buffer->setLength( oldLen + len + 1);
150 // digged into Qt 3.3 sources to find that. Might break in Qt 4 -- AV
151 unsigned short * ucsString = const_cast<unsigned short *>(Buffer->ucs2()) + oldLen;
152 char * data = bb.data();
153 for (uint posi = 0; posi < len; ++posi)
154 *ucsString++ = *data++;
155 *ucsString = 0;
156 */
157 return true;
158 }
159
loadRawText(const QString & filename,QByteArray & buf)160 bool loadRawText(const QString & filename, QByteArray & buf)
161 {
162 bool ret = false;
163 QFile f(filename);
164 QFileInfo fi(f);
165 if (fi.exists())
166 {
167 // Allocating one more bytes for null terminator is unneeded with Qt4
168 // as QByteArray ensure null termination automatically
169 // Triggers also QDomDocument parsing errors
170 QByteArray tempBuf(f.size() /*+ 1*/, ' ');
171 if (f.open(QIODevice::ReadOnly))
172 {
173 qint64 bytesRead = f.read(tempBuf.data(), f.size());
174 ret = bytesRead == f.size();
175 if (ret)
176 buf = tempBuf; // sharing makes this efficient
177 }
178 }
179 if (f.isOpen())
180 f.close();
181 return ret;
182 }
183
loadRawBytes(const QString & filename,QByteArray & buf)184 bool loadRawBytes(const QString & filename, QByteArray & buf)
185 {
186 bool ret = false;
187 QFile f(filename);
188 QFileInfo fi(f);
189 if (fi.exists())
190 {
191 QByteArray tempBuf(f.size(), ' ');
192 if (f.open(QIODevice::ReadOnly))
193 {
194 qint64 bytesRead = f.read(tempBuf.data(), f.size());
195 ret = bytesRead == f.size();
196 if (ret)
197 buf = tempBuf; // sharing makes this efficient
198 }
199 }
200 if (f.isOpen())
201 f.close();
202 return ret;
203 }
204
loadRawBytes(const QString & filename,QByteArray & buf,int maxLength)205 bool loadRawBytes(const QString & filename, QByteArray & buf, int maxLength)
206 {
207 if (maxLength < 0)
208 return false;
209 bool ret = false;
210
211 QFile f(filename);
212 QFileInfo fi(f);
213 if (fi.exists())
214 {
215 QByteArray tempBuf(maxLength, ' ');
216 if (f.open(QIODevice::ReadOnly))
217 {
218 qint64 bytesRead = f.read(tempBuf.data(), (qint64) maxLength);
219 ret = (bytesRead > 0);
220 if (ret)
221 {
222 tempBuf.resize((int) bytesRead);
223 buf = tempBuf; // sharing makes this efficient
224 }
225 }
226 }
227 if (f.isOpen())
228 f.close();
229 return ret;
230 }
231
CompressStr(QString * in)232 QString CompressStr(QString *in)
233 {
234 QString out;
235 QByteArray bb(in->length(), ' ');
236 if (bb.size() == in->length())
237 {
238 for (int i = 0; i < in->length(); ++i)
239 {
240 // bb.insert(ax, in->at(ax)); JG monstruously inefficient due to frequent memory reallocation
241 bb[i] = in->at(i).cell();
242 assert(in->at(i).row() == 0);
243 }
244 uLong exlen = (uLong)(bb.size() * 0.001 + 16) + bb.size();
245 QByteArray bc(exlen, ' ');
246 if( bc.size() == static_cast<qint32>(exlen) )
247 {
248 int errcode = compress2((Byte *)bc.data(), &exlen, (Byte *)bb.data(), uLong(bb.size()), 9);
249 if (errcode != Z_OK)
250 {
251 qDebug("compress2 failed with code %i", errcode);
252 out = *in;
253 }
254 else {
255 for (uint cl = 0; cl < exlen; ++cl)
256 out += QChar(bc[cl]);
257 }
258 }
259 else
260 {
261 qDebug("insufficient memory to allocate %i bytes", in->length());
262 out = *in;
263 }
264 }
265 else
266 {
267 qDebug("insufficient memory to allocate %i bytes", in->length());
268 out = *in;
269 }
270 return out;
271 }
272
CompressArray(const QByteArray & in)273 QByteArray CompressArray(const QByteArray& in)
274 {
275 QByteArray out;
276 uLong exlen = uint(in.size() * 0.001 + 16) + in.size();
277 QByteArray temp(exlen, ' ');
278 int errcode = compress2((Byte *)temp.data(), &exlen, (Byte *)in.data(), uLong(in.size()), 9);
279 if (errcode == Z_OK)
280 {
281 temp.resize(exlen);
282 out = temp;
283 }
284 else
285 qDebug("compress2 failed with code %i", errcode);
286 return out;
287 }
288
toAscii85(quint32 value,bool & allZero)289 char *toAscii85( quint32 value, bool& allZero )
290 {
291 int digit;
292 static char asciiVal[6];
293 allZero = true;
294 for (int i = 0; i < 5; ++i)
295 {
296 digit = value % 85;
297 if (digit != 0)
298 allZero = false;
299 asciiVal[4-i] = digit + 33;
300 value = (value - digit) / 85;
301 }
302 asciiVal[5] = 0;
303 return asciiVal;
304 }
305
toHex(uchar u)306 char *toHex( uchar u )
307 {
308 static char hexVal[3];
309 int i = 1;
310 while ( i >= 0 )
311 {
312 ushort hex = (u & 0x000f);
313 if ( hex < 0x0a )
314 hexVal[i] = '0'+hex;
315 else
316 hexVal[i] = 'A'+(hex-0x0a);
317 u = u >> 4;
318 i--;
319 }
320 hexVal[2] = '\0';
321 return hexVal;
322 }
323
String2Hex(QString * in,bool lang)324 QString String2Hex(QString *in, bool lang)
325 {
326 int i = 0;
327 QString out;
328 for (int j = 0; j < in->length(); ++j)
329 {
330 // Qt4 .cell() added ???
331 out += toHex(QChar(in->at(j)).cell());
332 ++i;
333 if ((i > 40) && (lang))
334 {
335 out += '\n';
336 i=0;
337 }
338 }
339 return out;
340 }
341
Path2Relative(const QString & Path,const QString & baseDir)342 QString Path2Relative(const QString& Path, const QString& baseDir)
343 {
344 QDir d(baseDir);
345 return d.relativeFilePath(Path);
346 }
347
Relative2Path(const QString & File,const QString & baseDir)348 QString Relative2Path(const QString& File, const QString& baseDir)
349 {
350 QString absPath;
351 QFileInfo fi(File);
352 if (File.isEmpty())
353 absPath = File;
354 else if (fi.isRelative())
355 {
356 QDir d(baseDir);
357 absPath = d.absoluteFilePath(File);
358 absPath = QDir::cleanPath(absPath);
359 }
360 else
361 absPath = File;
362 return absPath;
363 }
364
365 /***************************************************************************
366 begin : Wed Oct 29 2003
367 copyright : (C) 2003 The Scribus Team
368 email : paul@all-the-johnsons.co.uk
369 ***************************************************************************/
370 // check if the file exists, if it does, ask if they're sure
371 // return true if they're sure, else return false;
372
overwrite(QWidget * parent,const QString & filename)373 bool overwrite(QWidget *parent, const QString& filename)
374 {
375 bool retval = true;
376 QFileInfo fi(filename);
377 if (fi.exists())
378 {
379 QString fn = QDir::toNativeSeparators(filename);
380 int t = ScMessageBox::warning(parent, QObject::tr("File exists"),
381 "<qt>"+ QObject::tr("A file named '%1' already exists.<br/>Do you want to replace it with the file you are saving?").arg(fn) +"</qt>",
382 QMessageBox::Ok | QMessageBox::Cancel,
383 QMessageBox::Cancel, // GUI default
384 QMessageBox::Ok); // batch default
385 if (t == QMessageBox::Cancel)
386 retval = false;
387 }
388 return retval;
389 }
390
WordAndPara(PageItem * currItem,int * w,int * p,int * c,int * wN,int * pN,int * cN)391 void WordAndPara(PageItem* currItem, int *w, int *p, int *c, int *wN, int *pN, int *cN)
392 {
393 QChar Dat = QChar(32);
394 int para = 0;
395 int ww = 0;
396 int cc = 0;
397 int paraN = 0;
398 int wwN = 0;
399 int ccN = 0;
400 bool first = true;
401 PageItem *nextItem = currItem;
402 PageItem *nbl = currItem;
403 while (nextItem != nullptr)
404 {
405 if (nextItem->prevInChain() != nullptr)
406 nextItem = nextItem->prevInChain();
407 else
408 break;
409 }
410 while (nextItem != nullptr)
411 {
412 for (int a = qMax(nextItem->firstInFrame(),0); a <= nextItem->lastInFrame() && a < nextItem->itemText.length(); ++a)
413 {
414 QChar b = nextItem->itemText.text(a);
415 if (b == SpecialChars::PARSEP)
416 para++;
417 if ((!b.isLetterOrNumber()) && (Dat.isLetterOrNumber()) && (!first))
418 ww++;
419 if (b.isSurrogate())
420 ++a;
421 cc++;
422 Dat = b;
423 first = false;
424 }
425 nbl = nextItem;
426 nextItem = nextItem->nextInChain();
427 }
428 if (nbl->frameOverflows())
429 {
430 paraN++;
431 for (int a = nbl->lastInFrame()+1; a < nbl->itemText.length(); ++a)
432 {
433 QChar b = nbl->itemText.text(a);
434 if (b == SpecialChars::PARSEP)
435 paraN++;
436 if ((!b.isLetterOrNumber()) && (Dat.isLetterOrNumber()) && (!first))
437 wwN++;
438 if (b.isSurrogate())
439 ++a;
440 ccN++;
441 Dat = b;
442 first = false;
443 }
444 }
445 else {
446 para++;
447 }
448 if (Dat.isLetterOrNumber())
449 {
450 if (nbl->frameOverflows())
451 wwN++;
452 else
453 ww++;
454 }
455 *w = ww;
456 *p = para;
457 *c = cc;
458 *wN = wwN;
459 *pN = paraN;
460 *cN = ccN;
461 }
462
ReOrderText(ScribusDoc * currentDoc,ScribusView * view)463 void ReOrderText(ScribusDoc *currentDoc, ScribusView *view)
464 {
465 double savScale = view->scale();
466 view->setScale(1.0);
467 currentDoc->RePos = true;
468 QImage pgPix(10, 10, QImage::Format_ARGB32_Premultiplied);
469 QRect rd; // = QRect(0,0,9,9);
470 ScPainter *painter = new ScPainter(&pgPix, pgPix.width(), pgPix.height());
471 for (auto it = currentDoc->MasterItems.begin(); it != currentDoc->MasterItems.end(); ++it)
472 {
473 PageItem* currItem = *it;
474 if (currItem->itemType() == PageItem::PathText)
475 currItem->DrawObj(painter, rd);
476 }
477 for (auto it = currentDoc->Items->begin(); it != currentDoc->Items->end(); ++it)
478 {
479 PageItem* currItem = *it;
480 currItem->layout();
481 if (currItem->itemType() == PageItem::PathText)
482 currItem->DrawObj(painter, rd); //FIXME: this should be replaced by code in layout()
483 }
484 currentDoc->RePos = false;
485 view->setScale(savScale);
486 delete painter;
487 }
488
489 /*! \brief Helper function for sorting in sortQStringList.
490 \author 10/06/2004 - pv
491 \param s1 first string
492 \param s2 second string
493 \retval bool t/f related s1>s2
494 */
compareQStrings(const QString & s1,const QString & s2)495 bool compareQStrings(const QString& s1, const QString& s2)
496 {
497 return QString::localeAwareCompare(s1, s2) < 0;
498 }
499
sortQStringList(QStringList aList)500 QStringList sortQStringList(QStringList aList)
501 {
502 std::vector<QString> sortList;
503 QStringList retList;
504 QStringList::Iterator it;
505 for (it = aList.begin(); it != aList.end(); ++it)
506 sortList.push_back(*it);
507 std::sort(sortList.begin(), sortList.end(), compareQStrings);
508 for (uint i = 0; i < sortList.size(); i++)
509 retList.append(sortList[i]);
510 return retList;
511 }
512
sortingQPairOfStrings(QPair<QString,QString> aP,QPair<QString,QString> bP)513 bool sortingQPairOfStrings( QPair<QString, QString> aP, QPair<QString, QString> bP)
514 {
515 if (aP.first == bP.first)
516 return (aP.second < bP.second);
517 return (aP.first < bP.first);
518 }
519
checkFileExtension(const QString & currName,const QString & extension)520 QString checkFileExtension(const QString &currName, const QString &extension)
521 {
522 QString newName(currName);
523 //If filename ends with a period, just add the extension
524 if (newName.right(1)==".")
525 {
526 newName+=extension.toLower();
527 return newName;
528 }
529 //If filename doesn't end with the period+extension, add it on
530 QString dotExt("." + extension.toLower());
531 if (!newName.endsWith(dotExt, Qt::CaseInsensitive))
532 newName+=dotExt;
533 return newName;
534 }
535
getFileNameByPage(ScribusDoc * currDoc,uint pageNo,const QString & extension,const QString & prefix)536 QString getFileNameByPage(ScribusDoc* currDoc, uint pageNo, const QString& extension, const QString& prefix)
537 {
538 uint number = pageNo + currDoc->FirstPnum;
539 QString defaultName;
540 if (!prefix.isNull())
541 defaultName=prefix;
542 else
543 defaultName=currDoc->documentFileName();
544 if (defaultName.isNull())
545 defaultName = "export";
546 else
547 {
548 QFileInfo fi(defaultName);
549 defaultName = fi.completeBaseName();
550 }
551 return QString("%1-%2%3.%4").arg(defaultName, QObject::tr("page", "page export")).arg(number, 3, 10, QChar('0')).arg(extension);
552 }
553
getStringFromSequence(NumFormat type,uint position,const QString & asterix)554 QString getStringFromSequence(NumFormat type, uint position, const QString& asterix)
555 {
556 QString retVal;
557
558 const QString english("abcdefghijklmnopqrstuvwxyz");
559 const QString arabic("أبتثجحخدذرزسشصضطظعغفقكلمنهوي");
560 const QString abjad("أبجدهوزحطيكلمنسعفصقرشتثخذضظغ");
561
562 switch (type)
563 {
564 case Type_1_2_3:
565 retVal=QString::number(position);
566 break;
567 case Type_1_2_3_ar:
568 retVal=QLocale("ar").toString(position);
569 break;
570 case Type_A_B_C:
571 retVal = numberToLetterSequence(english, position).toUpper();
572 break;
573 case Type_a_b_c:
574 retVal = numberToLetterSequence(english, position);
575 break;
576 case Type_Alphabet_ar:
577 retVal = numberToLetterSequence(arabic, position);
578 break;
579 case Type_Abjad_ar:
580 retVal = numberToLetterSequence(abjad, position);
581 break;
582 case Type_Hebrew:
583 retVal = numberToHebrew(position);
584 break;
585 case Type_I_II_III:
586 retVal = numberToRoman(position);
587 break;
588 case Type_i_ii_iii:
589 //well, for lower case people will want that, even if its "wrong"
590 //ie, X=10, x=10000
591 retVal = numberToRoman(position).toLower();
592 break;
593 case Type_asterix:
594 for (uint a=1; a <= position; ++a)
595 retVal.append(asterix);
596 break;
597 case Type_CJK:
598 retVal = numberToCJK(position);
599 case Type_None:
600 break;
601 default:
602 break;
603 }
604 return retVal;
605 }
606
numberToLetterSequence(const QString & letters,uint num)607 QString numberToLetterSequence(const QString& letters, uint num)
608 {
609 QString retVal;
610 unsigned digits = 1;
611 unsigned offset = 0;
612 uint column = num - 1;
613
614 // FIXME: what is the heck is this?
615 if (column > 4058115285U)
616 return QString("@");
617
618 for (unsigned limit = 28; column >= limit+offset; limit *= letters.length(), digits++)
619 offset += limit;
620
621 for (unsigned c = column - offset; digits; --digits, c /= letters.length())
622 {
623 uint i = c % letters.length();
624 if (i < static_cast<uint>(letters.length()))
625 retVal.prepend(letters.at(i));
626 else
627 retVal.prepend(QChar::Null);
628 }
629 return retVal;
630 }
631
numberToRoman(uint i)632 QString numberToRoman(uint i)
633 {
634 QString roman("");
635 int arabic = i;
636 while (arabic - 1000000 >= 0){
637 roman += "m";
638 arabic -= 1000000;
639 }
640 while (arabic - 900000 >= 0){
641 roman += "cm";
642 arabic -= 900000;
643 }
644 while (arabic - 500000 >= 0){
645 roman += "d";
646 arabic -= 500000;
647 }
648 while (arabic - 400000 >= 0){
649 roman += "cd";
650 arabic -= 400000;
651 }
652 while (arabic - 100000 >= 0){
653 roman += "c";
654 arabic -= 100000;
655 }
656 while (arabic - 90000 >= 0){
657 roman += "xc";
658 arabic -= 90000;
659 }
660 while (arabic - 50000 >= 0){
661 roman += "l";
662 arabic -= 50000;
663 }
664 while (arabic - 40000 >= 0){
665 roman += "xl";
666 arabic -= 40000;
667 }
668 while (arabic - 10000 >= 0){
669 roman += "x";
670 arabic -= 10000;
671 }
672 while (arabic - 9000 >= 0){
673 roman += "Mx";
674 arabic -= 9000;
675 }
676 while (arabic - 5000 >= 0){
677 roman += "v";
678 arabic -= 5000;
679 }
680 while (arabic - 4000 >= 0){
681 roman += "Mv";
682 arabic -= 4000;
683 }
684 while (arabic - 1000 >= 0){
685 roman += "M";
686 arabic -= 1000;
687 }
688 while (arabic - 900 >= 0){
689 roman += "CM";
690 arabic -= 900;
691 }
692 while (arabic - 500 >= 0){
693 roman += "D";
694 arabic -= 500;
695 }
696 while (arabic - 400 >= 0){
697 roman += "CD";
698 arabic -= 400;
699 }
700 while (arabic - 100 >= 0){
701 roman += "C";
702 arabic -= 100;
703 }
704 while (arabic - 90 >= 0){
705 roman += "XC";
706 arabic -= 90;
707 }
708 while (arabic - 50 >= 0){
709 roman += "L";
710 arabic -= 50;
711 }
712 while (arabic - 40 >= 0){
713 roman += "XL";
714 arabic -= 40;
715 }
716 while (arabic - 10 >= 0){
717 roman += "X";
718 arabic -= 10;
719 }
720 while (arabic - 9 >= 0){
721 roman += "IX";
722 arabic -= 9;
723 }
724 while (arabic - 5 >= 0){
725 roman += "V";
726 arabic -= 5;
727 }
728 while (arabic - 4 >= 0){
729 roman += "IV";
730 arabic -= 4;
731 }
732 while (arabic - 1 >= 0){
733 roman += "I";
734 arabic -= 1;
735 }
736 return roman;
737 }
738
739 //CB Moved from scribus.cpp
parsePagesString(const QString & pages,std::vector<int> * pageNs,int sourcePageCount)740 void parsePagesString(const QString& pages, std::vector<int>* pageNs, int sourcePageCount)
741 {
742 QString tmp(pages);
743 QString token;
744 do
745 {
746 if (tmp.indexOf(",") == -1)
747 {
748 token = tmp;
749 tmp = "";
750 }
751 else
752 {
753 token = tmp.left(tmp.indexOf(","));
754 tmp = tmp.right(tmp.length() - tmp.indexOf(",") - 1);
755 }
756
757 token = token.trimmed();
758 if (token == "*") // Import all source doc pages
759 {
760 for (int i = 1; i <= sourcePageCount; ++i)
761 pageNs->push_back(i);
762 }
763 else if (token.indexOf("-") != -1) // import a range of source doc pages
764 {
765 int from = QStringRef(token.leftRef(token.indexOf("-"))).toInt();
766 int to = QStringRef(token.rightRef(token.length() - token.indexOf("-") - 1)).toInt();
767 if ((from != 0) && (to != 0))
768 {
769 if (from > sourcePageCount)
770 from = sourcePageCount;
771 if (to > sourcePageCount)
772 to = sourcePageCount;
773 if (from == to)
774 pageNs->push_back(to);
775 else if (from < to)
776 {
777 for (int i = from; i <= to; ++i)
778 pageNs->push_back(i);
779 }
780 else
781 {
782 for (int i = from; i >= to; --i)
783 pageNs->push_back(i);
784 }
785 }
786 }
787 else // import single source doc page
788 {
789 int pageNr = token.toInt();
790 if ((pageNr > 0) && (pageNr <= sourcePageCount))
791 pageNs->push_back(pageNr);
792 }
793 } while (!tmp.isEmpty());
794 }
795
796
readLineFromDataStream(QDataStream & s)797 QString readLineFromDataStream(QDataStream &s)
798 {
799 QString ret;
800 uchar charData;
801 while (!s.atEnd())
802 {
803 s >> charData;
804 if (charData == '\x0A')
805 break;
806 if (charData == '\x0D')
807 {
808 quint64 oldPos = s.device()->pos();
809 s >> charData;
810 if (charData != '\x0A')
811 s.device()->seek(oldPos);
812 break;
813 }
814 ret += QChar(charData);
815 }
816 return ret.trimmed();
817 }
818
setCurrentComboItem(QComboBox * box,const QString & text)819 void setCurrentComboItem(QComboBox *box, const QString& text)
820 {
821 QSignalBlocker signalBlocker(box);
822 int ind = box->findText(text);
823 if (ind > -1)
824 box->setCurrentIndex(ind);
825 }
826
setCurrentComboItemFromData(QComboBox * box,const QString & data)827 void setCurrentComboItemFromData(QComboBox *box, const QString& data)
828 {
829 QSignalBlocker signalBlocker(box);
830 int ind = box->findData(data);
831 if (ind > -1)
832 box->setCurrentIndex(ind);
833 }
834
removeComboItem(QComboBox * box,const QString & text)835 void removeComboItem(QComboBox *box, const QString& text)
836 {
837 QSignalBlocker signalBlocker(box);
838 int ind = box->findText(text);
839 if (ind > -1)
840 box->removeItem(ind);
841 }
842
readAdobeUniCodeString(QDataStream & s)843 QString readAdobeUniCodeString(QDataStream &s)
844 {
845 QString ret;
846 quint32 len;
847 s >> len;
848 for (quint32 i = 0; i < len; i++)
849 {
850 quint16 ch;
851 s >> ch;
852 if (ch != 0)
853 ret.append(QChar(ch));
854 }
855 return ret;
856 }
857
readAdobeUniCodeString16(QDataStream & s)858 QString readAdobeUniCodeString16(QDataStream &s)
859 {
860 QString ret;
861 quint16 len;
862 s >> len;
863 for (quint16 i = 0; i < len; i++)
864 {
865 quint16 ch;
866 s >> ch;
867 if (ch != 0)
868 ret.append(QChar(ch));
869 }
870 return ret;
871 }
872
getDashString(int dashtype,double linewidth)873 QString getDashString(int dashtype, double linewidth)
874 {
875 QString dashString;
876 QVector<double> dashArray;
877 getDashArray(dashtype, linewidth, dashArray);
878 for (int i = 0; i < dashArray.size(); ++i)
879 {
880 dashString += QString::number(dashArray.at(i));
881 if (i < (dashArray.size() - 1))
882 dashString += " ";
883 }
884 return dashString;
885 }
886
getDashArray(int dashtype,double linewidth,QVector<float> & dashArray)887 void getDashArray(int dashtype, double linewidth, QVector<float> &dashArray)
888 {
889 QVector<double> tmp;
890 getDashArray(dashtype, linewidth, tmp);
891 dashArray.clear();
892 for (int i = 0; i < tmp.count(); ++i)
893 dashArray << static_cast<float>(tmp[i]);
894 }
895
getDashArray(int dashtype,double linewidth,QVector<double> & dashArray)896 void getDashArray(int dashtype, double linewidth, QVector<double> &dashArray)
897 {
898 dashArray.clear();
899 if ((dashtype == 1) || (dashtype == 0))
900 return;
901 double Dt = qMax(1.0*linewidth, 0.1);
902 double Sp = qMax(2.0*linewidth, 0.1);
903 double Da = qMax(4.0*linewidth, 0.1);
904 switch (dashtype)
905 {
906 case 1:
907 break;
908 case 2:
909 dashArray << Da << Sp;
910 break;
911 case 3:
912 dashArray << Dt << Sp;
913 break;
914 case 4:
915 dashArray << Da << Sp << Dt << Sp;
916 break;
917 case 5:
918 dashArray << Da << Sp << Dt << Sp << Dt << Sp;
919 break;
920 // Additional line styles taken from Inkscape
921 case 6:
922 dashArray << qMax(1.0 * linewidth, 0.01) << qMax(1.0 * linewidth, 0.01);
923 break;
924 case 7:
925 dashArray << qMax(1.0 * linewidth, 0.01) << qMax(3.0 * linewidth, 0.01);
926 break;
927 case 8:
928 dashArray << qMax(1.0 * linewidth, 0.01) << qMax(4.0 * linewidth, 0.01);
929 break;
930 case 9:
931 dashArray << qMax(1.0 * linewidth, 0.01) << qMax(6.0 * linewidth, 0.01);
932 break;
933 case 10:
934 dashArray << qMax(1.0 * linewidth, 0.01) << qMax(8.0 * linewidth, 0.01);
935 break;
936 case 11:
937 dashArray << qMax(1.0 * linewidth, 0.01) << qMax(12.0 * linewidth, 0.01);
938 break;
939 case 12:
940 dashArray << qMax(1.0 * linewidth, 0.01) << qMax(24.0 * linewidth, 0.01);
941 break;
942 case 13:
943 dashArray << qMax(1.0 * linewidth, 0.01) << qMax(48.0 * linewidth, 0.01);
944 break;
945 case 14:
946 dashArray << qMax(2.0 * linewidth, 0.01) << qMax(1.0 * linewidth, 0.01);
947 break;
948 case 15:
949 dashArray << qMax(3.0 * linewidth, 0.01) << qMax(1.0 * linewidth, 0.01);
950 break;
951 case 16:
952 dashArray << qMax(4.0 * linewidth, 0.01) << qMax(1.0 * linewidth, 0.01);
953 break;
954 case 17:
955 dashArray << qMax(6.0 * linewidth, 0.01) << qMax(1.0 * linewidth, 0.01);
956 break;
957 case 18:
958 dashArray << qMax(8.0 * linewidth, 0.01) << qMax(1.0 * linewidth, 0.01);
959 break;
960 case 19:
961 dashArray << qMax(10.0 * linewidth, 0.01) << qMax(1.0 * linewidth, 0.01);
962 break;
963 case 20:
964 dashArray << qMax(12.0 * linewidth, 0.01) << qMax(1.0 * linewidth, 0.01);
965 break;
966 case 21:
967 dashArray << qMax(2.0 * linewidth, 0.01) << qMax(2.0 * linewidth, 0.01);
968 break;
969 case 22:
970 dashArray << qMax(3.0 * linewidth, 0.01) << qMax(3.0 * linewidth, 0.01);
971 break;
972 case 23:
973 dashArray << qMax(4.0 * linewidth, 0.01) << qMax(4.0 * linewidth, 0.01);
974 break;
975 case 24:
976 dashArray << qMax(6.0 * linewidth, 0.01) << qMax(6.0 * linewidth, 0.01);
977 break;
978 case 25:
979 dashArray << qMax(8.0 * linewidth, 0.01) << qMax(8.0 * linewidth, 0.01);
980 break;
981 case 26:
982 dashArray << qMax(10.0 * linewidth, 0.01) << qMax(10.0 * linewidth, 0.01);
983 break;
984 case 27:
985 dashArray << qMax(12.0 * linewidth, 0.01) << qMax(12.0 * linewidth, 0.01);
986 break;
987 case 28:
988 dashArray << qMax(2.0 * linewidth, 0.01) << qMax(4.0 * linewidth, 0.01);
989 break;
990 case 29:
991 dashArray << qMax(2.0 * linewidth, 0.01) << qMax(6.0 * linewidth, 0.01);
992 break;
993 case 30:
994 dashArray << qMax(6.0 * linewidth, 0.01) << qMax(2.0 * linewidth, 0.01);
995 break;
996 case 31:
997 dashArray << qMax(4.0 * linewidth, 0.01) << qMax(8.0 * linewidth, 0.01);
998 break;
999 case 32:
1000 dashArray << qMax(8.0 * linewidth, 0.01) << qMax(4.0 * linewidth, 0.01);
1001 break;
1002 case 33:
1003 dashArray << qMax(2.0 * linewidth, 0.01) << qMax(1.0 * linewidth, 0.01);
1004 dashArray << qMax(0.5 * linewidth, 0.01) << qMax(1.0 * linewidth, 0.01);
1005 break;
1006 case 34:
1007 dashArray << qMax(8.0 * linewidth, 0.01) << qMax(2.0 * linewidth, 0.01);
1008 dashArray << qMax(1.0 * linewidth, 0.01) << qMax(2.0 * linewidth, 0.01);
1009 break;
1010 case 35:
1011 dashArray << qMax(0.5 * linewidth, 0.01) << qMax(0.5 * linewidth, 0.01);
1012 break;
1013 case 36:
1014 dashArray << qMax(0.25 * linewidth, 0.01) << qMax(0.25 * linewidth, 0.01);
1015 break;
1016 case 37:
1017 dashArray << qMax(0.1 * linewidth, 0.01) << qMax(0.1 * linewidth, 0.01);
1018 break;
1019 default:
1020 break;
1021 }
1022 }
1023
convertOldTable(ScribusDoc * m_Doc,PageItem * gItem,QList<PageItem * > & gpL,QStack<QList<PageItem * >> * groupStackT,QList<PageItem * > * target)1024 bool convertOldTable(ScribusDoc *m_Doc, PageItem* gItem, QList<PageItem*> &gpL, QStack<QList<PageItem *> > *groupStackT, QList<PageItem *> *target)
1025 {
1026 QList<double> colWidths;
1027 QList<double> rowHeights;
1028
1029 // 1. Although this was not intended, legacy tables allowed user to link frames together
1030 // New table do not support that, so if one frame has any link, we stop the conversion
1031 // here, those frame will be converted to a standard group.
1032 // 2. Pre-1.4.3 versions had a bug where item TopLink/LeftLink/BottomLink/RightLink were
1033 // lost when copy/pasting tables. Exit conversion too so these broken tables can be
1034 // converted to standard groups (at least until we find a good way to process that case)
1035 bool hasTableLinks = false;
1036 bool hasTextLinks = false;
1037 for (int i = 0; i < gpL.count(); i++)
1038 {
1039 PageItem* it = gpL[i];
1040 it->isTableItem = false;
1041 if (it->nextInChain() || it->prevInChain())
1042 hasTextLinks = true;
1043 if (it->m_leftLink || it->m_rightLink || it->m_bottomLink || it->m_topLink)
1044 hasTableLinks = true;
1045 }
1046
1047 if (!hasTableLinks || hasTextLinks)
1048 return false;
1049
1050 PageItem *topLeft = nullptr;
1051 for (int i = 0; i < gpL.count(); i++)
1052 {
1053 PageItem* it = gpL[i];
1054 if ((it->m_topLink == nullptr) && (it->m_leftLink == nullptr)) // we got the topleft item
1055 {
1056 topLeft = it;
1057 PageItem *tl = it;
1058 while (tl->m_rightLink != nullptr)
1059 {
1060 colWidths.append(tl->width());
1061 tl = tl->m_rightLink;
1062 }
1063 colWidths.append(tl->width());
1064 while (tl->m_bottomLink != nullptr)
1065 {
1066 rowHeights.append(tl->height());
1067 tl = tl->m_bottomLink;
1068 }
1069 rowHeights.append(tl->height());
1070 break;
1071 }
1072 }
1073
1074 // Check we have found enough rows and columns so that no item will disappear
1075 if ((colWidths.count() * rowHeights.count()) < gpL.count())
1076 return false;
1077
1078 m_Doc->dontResize = true;
1079 int z = m_Doc->itemAdd(PageItem::Table, PageItem::Unspecified, gItem->xPos(), gItem->yPos(), gItem->width(), gItem->height(), 0.0, CommonStrings::None, CommonStrings::None);
1080 PageItem_Table* currItem = m_Doc->Items->takeAt(z)->asTable();
1081
1082 currItem->m_layerID = gItem->m_layerID;
1083 currItem->OwnPage = gItem->OwnPage;
1084 currItem->OnMasterPage = gItem->OnMasterPage;
1085
1086 currItem->insertRows(0, rowHeights.count()-1);
1087 m_Doc->dontResize = true;
1088 currItem->insertColumns(0, colWidths.count()-1);
1089 m_Doc->dontResize = true;
1090 for (int i = 0; i < rowHeights.count(); i++)
1091 {
1092 currItem->resizeRow(i, rowHeights[i]);
1093 }
1094 m_Doc->dontResize = true;
1095 for (int i = 0; i < colWidths.count(); i++)
1096 {
1097 currItem->resizeColumn(i, colWidths[i]);
1098 }
1099 TableBorder border(0.0, Qt::SolidLine, CommonStrings::None, 100);
1100 currItem->setLeftBorder(border);
1101 currItem->setTopBorder(border);
1102 currItem->setRightBorder(border);
1103 currItem->setBottomBorder(border);
1104 m_Doc->dontResize = true;
1105 PageItem *tr = topLeft;
1106 int rowCount = 0;
1107 int colCount = 0;
1108 while (rowCount < rowHeights.count())
1109 {
1110 PageItem *tl = tr;
1111 while (colCount < colWidths.count())
1112 {
1113 currItem->cellAt(rowCount, colCount).textFrame()->itemText = tl->itemText.copy();
1114 currItem->cellAt(rowCount, colCount).setFillColor(tl->fillColor());
1115 currItem->cellAt(rowCount, colCount).setFillShade(tl->fillShade());
1116 currItem->cellAt(rowCount, colCount).setLeftBorder(border);
1117 currItem->cellAt(rowCount, colCount).setTopBorder(border);
1118 currItem->cellAt(rowCount, colCount).setRightBorder(border);
1119 currItem->cellAt(rowCount, colCount).setBottomBorder(border);
1120 if ((tl->lineColor() != CommonStrings::None) && (tl->lineWidth() != 0.0))
1121 {
1122 TableBorder bb(tl->lineWidth(), tl->lineStyle(), tl->lineColor(), tl->lineShade());
1123 if (tl->LeftLine)
1124 currItem->cellAt(rowCount, colCount).setLeftBorder(bb);
1125 if (tl->TopLine)
1126 currItem->cellAt(rowCount, colCount).setTopBorder(bb);
1127 if (tl->RightLine)
1128 currItem->cellAt(rowCount, colCount).setRightBorder(bb);
1129 if (tl->BottomLine)
1130 currItem->cellAt(rowCount, colCount).setBottomBorder(bb);
1131 }
1132 if (colCount == colWidths.count()-1)
1133 break;
1134 colCount++;
1135 tl = tl->m_rightLink;
1136 }
1137 if (rowCount == rowHeights.count()-1)
1138 break;
1139 colCount = 0;
1140 rowCount++;
1141 tr = tr->m_bottomLink;
1142 }
1143 m_Doc->dontResize = true;
1144 currItem->setLayer(gItem->m_layerID);
1145 currItem->setMasterPage(gItem->OwnPage, gItem->OnMasterPage);
1146 currItem->adjustFrameToTable();
1147 if (target != nullptr)
1148 {
1149 int ind = target->indexOf(gItem);
1150 target->replace(ind, currItem);
1151 }
1152 else
1153 {
1154 int ind = m_Doc->FrameItems.key(gItem);
1155 m_Doc->FrameItems.remove(ind);
1156 m_Doc->FrameItems.insert(ind, currItem);
1157 }
1158 if (groupStackT != nullptr)
1159 {
1160 if (groupStackT->count() > 0)
1161 {
1162 int ii = groupStackT->top().indexOf(gItem);
1163 if (ii >= 0)
1164 groupStackT->top().replace(ii, currItem);
1165 }
1166 }
1167 while (!gpL.isEmpty())
1168 {
1169 PageItem* item = gpL.takeFirst();
1170 if (item->isTextFrame())
1171 item->dropLinks();
1172 delete item;
1173 }
1174 delete gItem;
1175
1176 return true;
1177 }
1178
setWidgetBoldFont(QWidget * w,bool wantBold)1179 void setWidgetBoldFont(QWidget* w, bool wantBold)
1180 {
1181 QFont f(w->font());
1182 f.setBold(wantBold);
1183 w->setFont(f);
1184 }
1185
1186
getUniqueName(QString & name,const QStringList & list,const QString & separator,bool prepend)1187 void getUniqueName(QString &name, const QStringList& list, const QString& separator, bool prepend)
1188 {
1189 if (!list.contains(name))
1190 return;
1191 int token = -1;
1192 QString newName;
1193 do {
1194 token++;
1195 if (prepend)
1196 newName = separator + QString::number(token) + " " + name;
1197 else
1198 newName = name + separator + QString::number(token);
1199 } while (list.contains(newName));
1200 name = newName;
1201 }
1202
numberToHebrew(uint i)1203 QString numberToHebrew(uint i)
1204 {
1205 const QString hebrew("אבגדהוזחטיכלמנסעפצקרשת");
1206 QString result;
1207
1208 if (i > 999)
1209 {
1210 result.append(numberToHebrew(i / 1000));
1211 result.append(QChar(0x05F3));
1212 i %= 1000;
1213 }
1214
1215 int hundreds = i / 100;
1216 int tens = (i - hundreds * 100) / 10;
1217 int ones = i % 10;
1218
1219 while (hundreds > 4)
1220 {
1221 result.append(hebrew.at(21));
1222 hundreds -= 4;
1223 }
1224
1225 if (hundreds)
1226 result.append(hebrew.at(hundreds + 17));
1227
1228 if (tens == 1 && ones == 5)
1229 result.append("טו");
1230 else if (tens == 1 && ones == 6)
1231 result.append("טז");
1232 else
1233 {
1234 if (tens)
1235 result.append(hebrew.at(tens + 8));
1236 if (ones)
1237 result.append(hebrew.at(ones - 1));
1238 }
1239
1240 return result;
1241 }
1242
numberToCJK(uint i)1243 QString numberToCJK(uint i)
1244 {
1245 QString result;
1246 if (i<10)
1247 result = QString(cjkDigit(i));
1248
1249 if (i>9 && i<=99)
1250 {
1251 int tens=i/10;
1252 int ones=i%10;
1253 if (tens!=1)
1254 result.append(cjkDigit(tens));
1255 result.append(cjkDigit(10));
1256 if (ones!=0)
1257 result.append(cjkDigit(ones));
1258 }
1259
1260 if (i>99 && i<=999)
1261 {
1262 int hundreds=i/100;
1263 int tens=(i-hundreds*100)/10;
1264 int ones=i%10;
1265 result.append(cjkDigit(hundreds));
1266 result.append(cjkDigit(100));
1267 if (tens!=0)
1268 {
1269 result.append(cjkDigit(tens));
1270 result.append(cjkDigit(10));
1271 }
1272 else if (ones!=0)
1273 result.append(cjkDigit(0));
1274 if (ones!=0)
1275 result.append(cjkDigit(ones));
1276 }
1277 return result;
1278 }
1279
1280
cjkDigit(uint i)1281 QChar cjkDigit(uint i)
1282 {
1283 switch (i)
1284 {
1285 case 0:
1286 return QChar(0x96f6);
1287 break;
1288 case 1:
1289 return QChar(0x4e00);
1290 break;
1291 case 2:
1292 return QChar(0x4e8c);
1293 break;
1294 case 3:
1295 return QChar(0x4e09);
1296 break;
1297 case 4:
1298 return QChar(0x56db);
1299 break;
1300 case 5:
1301 return QChar(0x4e94);
1302 break;
1303 case 6:
1304 return QChar(0x516d);
1305 break;
1306 case 7:
1307 return QChar(0x4e03);
1308 break;
1309 case 8:
1310 return QChar(0x516b);
1311 break;
1312 case 9:
1313 return QChar(0x4e5d);
1314 break;
1315 case 10:
1316 return QChar(0x5341);
1317 break;
1318 case 100:
1319 return QChar(0x767e);
1320 break;
1321 case 1000:
1322 return QChar(0x5343);
1323 break;
1324 case 10000:
1325 return QChar(0x842c);
1326 break;
1327 case 100000000:
1328 return QChar(0x5104);
1329 break;
1330 }
1331 return QChar::Null;
1332 }
1333