Subversion Repositories Scribus

Rev

Rev 24430 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
For general Scribus (>=1.3.2) copyright and licensing information please refer
to the COPYING file provided with the program. Following this notice may exist
a copyright and/or license notice that predates the release of Scribus 1.3.2
for which a new license (GPL+exception) is in place.
*/

/***************************************************************************
                          util.cpp  -  description
                             -------------------
    begin                : Fri Sep 14 2001
    copyright            : (C) 2001 by Franz Schmid
    email                : Franz.Schmid@altmuehlnet.de
 ***************************************************************************/


/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/


#include <algorithm>
#include "util.h"
#include <zlib.h>
#include <qglobal.h>
#include <QApplication>
#include <QCryptographicHash>
#include <QDomElement>
#include <QMessageBox>
#include <QProcess>
#include <QSignalBlocker>

#include "pageitem.h"
#include "pageitem_table.h"
#include "scribusview.h"
#include "scribusdoc.h"
#include "scpainter.h"
#include "ui/scmessagebox.h"

#include <csignal>

#if !defined(_WIN32) && !defined(Q_OS_MAC)
#include <execinfo.h>
#include <cxxabi.h>
#endif
#if defined(_WIN32)
#include <windows.h>
#endif

using namespace std;

QString cleanupLang(const QString& lang)
{
        int dotIndex = lang.indexOf(QChar('.'));
        if (dotIndex < 0)
                return lang;

        return lang.left(dotIndex);
}

int System(const QString& exename, const QStringList & args, const QString& fileStdErr, const QString& fileStdOut, const bool* cancel)
{
        QProcess proc;
        if (!fileStdOut.isEmpty())
                proc.setStandardOutputFile(fileStdOut);
        if (!fileStdErr.isEmpty())
                proc.setStandardErrorFile(fileStdErr);
        proc.start(exename, args);
        if (proc.waitForStarted(15000))
        {
                while (!proc.waitForFinished(15000))
                {
                        qApp->processEvents();
                        if (cancel && (*cancel))
                        {
                                proc.kill();
                                break;
                        }
                }
        }
        if (cancel && (*cancel))
                return -1;
        return proc.exitCode();
}

// On Windows, return short path name, else return longPath;
QString getShortPathName(const QString & longPath)
{
        QString shortPath(longPath);
#if defined _WIN32
        QFileInfo fInfo(longPath);
        if (fInfo.exists())
        {
                WCHAR shortName[MAX_PATH + 1];
                // An error should not be blocking as ERROR_INVALID_PARAMETER can simply mean
                // that volume does not support 8.3 filenames, so return longPath in this case
                QString nativePath = QDir::toNativeSeparators(longPath);
                int ret = GetShortPathNameW((LPCWSTR) nativePath.utf16(), shortName, MAX_PATH);
                if (ret != ERROR_INVALID_PARAMETER && ret < MAX_PATH)
                        shortPath = QString::fromUtf16((const ushort*) shortName);
        }
#endif
        return shortPath;
}

// On Windows, return short path name, else return longPath;
QString getLongPathName(const QString & shortPath)
{
        QString longPath(shortPath);
#if defined _WIN32
        QFileInfo fInfo(longPath);
        if (fInfo.exists())
        {
                WCHAR longName[MAX_PATH + 1];
                // An error should not be blocking as ERROR_INVALID_PARAMETER can simply mean
                // that volume does not support long filenames, so return shortPath in this case
                QString nativePath = QDir::toNativeSeparators(shortPath);
                int ret = GetLongPathNameW((LPCWSTR) nativePath.utf16(), longName, MAX_PATH);
                if (ret != ERROR_INVALID_PARAMETER && ret < MAX_PATH)
                        longPath = QString::fromUtf16((const ushort*) longName);
        }
#endif
        return longPath;
}

// Legacy implementation of LoadText with incorrect
// handling of unicode data. This should be retired.
// Use loadRawText instead.
// FIXME XXX
//
bool loadText(const QString& filename, QString *buffer)
{
        QFile f(filename);
        QFileInfo fi(f);
        if (!fi.exists())
                return false;
        QByteArray bb(f.size(), ' ');
        if (!f.open(QIODevice::ReadOnly))
                return false;
        f.read(bb.data(), f.size());
        f.close();
        for (int i = 0; i < bb.size(); ++i)
                *buffer += QChar(bb[i]);
        /*
                int len = bb.size();
                int oldLen = Buffer->length();
                Buffer->setLength( oldLen + len + 1);
                // digged into Qt 3.3 sources to find that. Might break in Qt 4 -- AV
                unsigned short * ucsString = const_cast<unsigned short *>(Buffer->ucs2()) + oldLen;
                char * data = bb.data();
                for (uint posi = 0; posi < len; ++posi)
                *ucsString++ = *data++;
                *ucsString = 0;
                */

        return true;
}

bool loadRawText(const QString & filename, QByteArray & buf)
{
        bool ret = false;
        QFile f(filename);
        QFileInfo fi(f);
        if (fi.exists())
        {
                // Allocating one more bytes for null terminator is unneeded with Qt4
                // as QByteArray ensure null termination automatically
                // Triggers also QDomDocument parsing errors
                QByteArray tempBuf(f.size() /*+ 1*/, ' ');
                if (f.open(QIODevice::ReadOnly))
                {
                        qint64 bytesRead = f.read(tempBuf.data(), f.size());
                        ret = bytesRead == f.size();
                        if (ret)
                                buf = tempBuf; // sharing makes this efficient
                }
        }
        if (f.isOpen())
                f.close();
        return ret;
}

bool loadRawBytes(const QString & filename, QByteArray & buf)
{
        bool ret = false;
        QFile f(filename);
        QFileInfo fi(f);
        if (fi.exists())
        {
                QByteArray tempBuf(f.size(), ' ');
                if (f.open(QIODevice::ReadOnly))
                {
                        qint64 bytesRead = f.read(tempBuf.data(), f.size());
                        ret = bytesRead == f.size();
                        if (ret)
                                buf = tempBuf; // sharing makes this efficient
                }
        }
        if (f.isOpen())
                f.close();
        return ret;
}

bool loadRawBytes(const QString & filename, QByteArray & buf, int maxLength)
{
        if (maxLength < 0)
                return false;
        bool ret = false;

        QFile f(filename);
        QFileInfo fi(f);
        if (fi.exists())
        {
                QByteArray tempBuf(maxLength, ' ');
                if (f.open(QIODevice::ReadOnly))
                {
                        qint64 bytesRead = f.read(tempBuf.data(), (qint64) maxLength);
                        ret = (bytesRead > 0);
                        if (ret)
                        {
                                tempBuf.resize((int) bytesRead);
                                buf = tempBuf; // sharing makes this efficient
                        }
                }
        }
        if (f.isOpen())
                f.close();
        return ret;
}

QString CompressStr(QString *in)
{
        QString out;
        QByteArray bb(in->length(), ' ');
        if (bb.size() == in->length())
        {
                for (int i = 0; i < in->length(); ++i)
                {
                        // bb.insert(ax, in->at(ax)); JG monstruously inefficient due to frequent memory reallocation
                        bb[i] = in->at(i).cell();
                        assert(in->at(i).row() == 0);
                }
                uLong exlen = (uLong)(bb.size() * 0.001 + 16) + bb.size();
                QByteArray bc(exlen, ' ');
                if( bc.size() == static_cast<qint32>(exlen) )
                {
                        int errcode = compress2((Byte *)bc.data(), &exlen, (Byte *)bb.data(), uLong(bb.size()), 9);
                        if (errcode != Z_OK)
                        {
                                qDebug("compress2 failed with code %i", errcode);
                                out = *in;
                        }
                        else {
                                for (uint cl = 0; cl < exlen; ++cl)
                                        out += QChar(bc[cl]);
                        }
                }
                else
                {
                        qDebug("insufficient memory to allocate %i bytes", in->length());
                        out = *in;
                }
        }
        else
        {
                qDebug("insufficient memory to allocate %i bytes", in->length());
                out = *in;
        }
        return out;
}

QByteArray CompressArray(const QByteArray& in)
{
        QByteArray out;
        uLong exlen = uint(in.size() * 0.001 + 16) + in.size();
        QByteArray temp(exlen, ' ');
        int errcode = compress2((Byte *)temp.data(), &exlen, (Byte *)in.data(), uLong(in.size()), 9);
        if (errcode == Z_OK)
        {
                temp.resize(exlen);
                out = temp;
        }
        else
                qDebug("compress2 failed with code %i", errcode);
        return out;
}

char *toAscii85( quint32 value, bool& allZero )
{
        int digit;
        static char asciiVal[6];
        allZero = true;
        for (int i = 0; i < 5; ++i)
        {
                digit = value % 85;
                if (digit != 0)
                        allZero = false;
                asciiVal[4-i] = digit + 33;
                value = (value - digit) / 85;
        }
        asciiVal[5] = 0;
        return asciiVal;
}

char *toHex( uchar u )
{
        static char hexVal[3];
        int i = 1;
        while ( i >= 0 )
        {
                ushort hex = (u & 0x000f);
                if ( hex < 0x0a )
                        hexVal[i] = '0'+hex;
                else
                        hexVal[i] = 'A'+(hex-0x0a);
                u = u >> 4;
                i--;
        }
        hexVal[2] = '\0';
        return hexVal;
}

QString String2Hex(QString *in, bool lang)
{
        int i = 0;
        QString out;
        for (int j = 0; j < in->length(); ++j)
        {
                // Qt4 .cell() added ???
                out += toHex(QChar(in->at(j)).cell());
                ++i;
                if ((i > 40) && (lang))
                {
                        out += '\n';
                        i=0;
                }
        }
        return out;
}

QString Path2Relative(const QString& Path, const QString& baseDir)
{
        QDir d(baseDir);
        return d.relativeFilePath(Path);
}

QString Relative2Path(const QString& File, const QString& baseDir)
{
        QString absPath;
        QFileInfo fi(File);
        if (File.isEmpty())
                absPath = File;
        else if (fi.isRelative())
        {
                QDir d(baseDir);
                absPath = d.absoluteFilePath(File);
                absPath = QDir::cleanPath(absPath);
        }
        else
                absPath = File;
        return absPath;
}

/***************************************************************************
    begin                : Wed Oct 29 2003
    copyright            : (C) 2003 The Scribus Team
    email                : paul@all-the-johnsons.co.uk
 ***************************************************************************/

// check if the file exists, if it does, ask if they're sure
// return true if they're sure, else return false;

bool overwrite(QWidget *parent, const QString& filename)
{
        bool retval = true;
        QFileInfo fi(filename);
        if (fi.exists())
        {
                QString fn = QDir::toNativeSeparators(filename);
                int t = ScMessageBox::warning(parent, QObject::tr("File exists"),
                                                                         "<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>",
                                                                                         QMessageBox::Ok | QMessageBox::Cancel,
                                                                                         QMessageBox::Cancel,   // GUI default
                                                                                         QMessageBox::Ok);      // batch default
                if (t == QMessageBox::Cancel)
                        retval = false;
        }
        return retval;
}

void WordAndPara(PageItem* currItem, int *w, int *p, int *c, int *wN, int *pN, int *cN)
{
        QChar Dat = QChar(32);
        int para = 0;
        int ww = 0;
        int cc = 0;
        int paraN = 0;
        int wwN = 0;
        int ccN = 0;
        bool first = true;
        PageItem *nextItem = currItem;
        PageItem *nbl = currItem;
        while (nextItem != nullptr)
        {
                if (nextItem->prevInChain() != nullptr)
                        nextItem = nextItem->prevInChain();
                else
                        break;
        }
        while (nextItem != nullptr)
        {
                for (int a = qMax(nextItem->firstInFrame(),0); a <= nextItem->lastInFrame() && a < nextItem->itemText.length(); ++a)
                {
                        QChar b = nextItem->itemText.text(a);
                        if (b == SpecialChars::PARSEP)
                                para++;
                        if ((!b.isLetterOrNumber()) && (Dat.isLetterOrNumber()) && (!first))
                                ww++;
                        if (b.isSurrogate())
                                ++a;
                        cc++;
                        Dat = b;
                        first = false;
                }
                nbl = nextItem;
                nextItem = nextItem->nextInChain();
        }
        if (nbl->frameOverflows())
        {
                paraN++;
                for (int a = nbl->lastInFrame()+1; a < nbl->itemText.length(); ++a)
                {
                        QChar b = nbl->itemText.text(a);
                        if (b == SpecialChars::PARSEP)
                                paraN++;
                        if ((!b.isLetterOrNumber()) && (Dat.isLetterOrNumber()) && (!first))
                                wwN++;
                        if (b.isSurrogate())
                                ++a;
                        ccN++;
                        Dat = b;
                        first = false;
                }
        }              
        else {
                para++;
        }
        if (Dat.isLetterOrNumber())
        {
                if (nbl->frameOverflows())
                        wwN++;
                else
                        ww++;
        }
        *w = ww;
        *p = para;
        *c = cc;
        *wN = wwN;
        *pN = paraN;
        *cN = ccN;
}

void ReOrderText(ScribusDoc *currentDoc, ScribusView *view)
{
        double savScale = view->scale();
        view->setScale(1.0);
        currentDoc->RePos = true;
        QImage pgPix(10, 10, QImage::Format_ARGB32_Premultiplied);
        QRect rd; // = QRect(0,0,9,9);
        ScPainter *painter = new ScPainter(&pgPix, pgPix.width(), pgPix.height());
        for (auto it = currentDoc->MasterItems.begin(); it != currentDoc->MasterItems.end(); ++it)
        {
                PageItem* currItem = *it;
                if (currItem->itemType() == PageItem::PathText)
                        currItem->DrawObj(painter, rd);
        }
        for (auto it = currentDoc->Items->begin(); it != currentDoc->Items->end(); ++it)
        {
                PageItem* currItem = *it;
                currItem->layout();
                if (currItem->itemType() == PageItem::PathText)
                        currItem->DrawObj(painter, rd); //FIXME: this should be replaced by code in layout()
        }
        currentDoc->RePos = false;
        view->setScale(savScale);
        delete painter;
}

/*! \brief Helper function for sorting in sortQStringList.
\author 10/06/2004 - pv
\param s1 first string
\param s2 second string
\retval bool t/f related s1>s2
 */

bool compareQStrings(const QString& s1, const QString& s2)
{
        return QString::localeAwareCompare(s1, s2) < 0;
}

QStringList sortQStringList(QStringList aList)
{
        std::vector<QString> sortList;
        QStringList retList;
        QStringList::Iterator it;
        for (it = aList.begin(); it != aList.end(); ++it)
                sortList.push_back(*it);
        std::sort(sortList.begin(), sortList.end(), compareQStrings);
        for (uint i = 0; i < sortList.size(); i++)
                retList.append(sortList[i]);
        return retList;
}

bool sortingQPairOfStrings( QPair<QString, QString> aP, QPair<QString, QString> bP)
{
        if (aP.first == bP.first)
                return (aP.second < bP.second);
        return (aP.first < bP.first);
}

QString checkFileExtension(const QString &currName, const QString &extension)
{
        QString newName(currName);
        //If filename ends with a period, just add the extension
        if (newName.right(1)==".")
        {
                newName+=extension.toLower();
                return newName;
        }
        //If filename doesn't end with the period+extension, add it on
        QString dotExt("." + extension.toLower());
        if (!newName.endsWith(dotExt, Qt::CaseInsensitive))
                newName+=dotExt;
        return newName;
}

QString getFileNameByPage(ScribusDoc* currDoc, uint pageNo, const QString& extension, const QString& prefix)
{
        uint number = pageNo + currDoc->FirstPnum;
        QString defaultName;
        if (!prefix.isNull())
                defaultName=prefix;
        else
                defaultName=currDoc->documentFileName();
        if (defaultName.isNull())
                defaultName = "export";
        else
        {
                QFileInfo fi(defaultName);
                defaultName = fi.completeBaseName();
        }
        return QString("%1-%2%3.%4").arg(defaultName, QObject::tr("page", "page export")).arg(number, 3, 10, QChar('0')).arg(extension);
}

QString getStringFromSequence(NumFormat type, uint position, const QString& asterix)
{
        QString retVal;

        const QString english("abcdefghijklmnopqrstuvwxyz");
        const QString arabic("أبتثجحخدذرزسشصضطظعغفقكلمنهوي");
        const QString abjad("أبجدهوزحطيكلمنسعفصقرشتثخذضظغ");

        switch (type)
        {
                case Type_1_2_3:
                        retVal=QString::number(position);
                        break;
                case Type_1_2_3_ar:
                        retVal=QLocale("ar").toString(position);
                        break;
                case Type_A_B_C:
                        retVal = numberToLetterSequence(english, position).toUpper();
                        break;
                case Type_a_b_c:
                        retVal = numberToLetterSequence(english, position);
                        break;
                case Type_Alphabet_ar:
                        retVal = numberToLetterSequence(arabic, position);
                        break;
                case Type_Abjad_ar:
                        retVal = numberToLetterSequence(abjad, position);
                        break;
                case Type_Hebrew:
                        retVal = numberToHebrew(position);
                        break;
                case Type_I_II_III:
                        retVal = numberToRoman(position);
                        break;
                case Type_i_ii_iii:
                        //well, for lower case people will want that, even if its "wrong"
                        //ie, X=10, x=10000
                        retVal = numberToRoman(position).toLower();
                        break;
                case Type_asterix:
                        for (uint a=1; a <= position; ++a)
                                retVal.append(asterix);
                        break;
                case Type_CJK:
                        retVal = numberToCJK(position);
                case Type_None:
                        break;
                default:
                        break;
        }
        return retVal;
}

QString numberToLetterSequence(const QString& letters, uint num)
{
        QString retVal;
        unsigned digits = 1;
        unsigned offset = 0;
        uint column = num - 1;

        // FIXME: what is the heck is this?
        if (column > 4058115285U)
                return  QString("@");

        for (unsigned limit = 28; column >= limit+offset; limit *= letters.length(), digits++)
                offset += limit;

        for (unsigned c = column - offset; digits; --digits, c /= letters.length())
        {
                uint i = c % letters.length();
                if (i < static_cast<uint>(letters.length()))
                        retVal.prepend(letters.at(i));
                else
                        retVal.prepend(QChar::Null);
        }
        return retVal;
}

QString numberToRoman(uint i)
{
        QString roman("");
        int arabic = i;
        while (arabic - 1000000 >= 0){
                roman += "m";
                arabic -= 1000000;
        }
        while (arabic - 900000 >= 0){
                roman += "cm";
                arabic -= 900000;
        }
        while (arabic - 500000 >= 0){
                roman += "d";
                arabic -= 500000;
        }
        while (arabic - 400000 >= 0){
                roman += "cd";
                arabic -= 400000;
        }
        while (arabic - 100000 >= 0){
                roman += "c";
                arabic -= 100000;
        }
        while (arabic - 90000 >= 0){
                roman += "xc";
                arabic -= 90000;
        }
        while (arabic - 50000 >= 0){
                roman += "l";
                arabic -= 50000;
        }
        while (arabic - 40000 >= 0){
                roman += "xl";
                arabic -= 40000;
        }
        while (arabic - 10000 >= 0){
                roman += "x";
                arabic -= 10000;
        }
        while (arabic - 9000 >= 0){
                roman += "Mx";
                arabic -= 9000;
        }
        while (arabic - 5000 >= 0){
                roman += "v";
                arabic -= 5000;
        }
        while (arabic - 4000 >= 0){
                roman += "Mv";
                arabic -= 4000;
        }
        while (arabic - 1000 >= 0){
                roman += "M";
                arabic -= 1000;
        }
        while (arabic - 900 >= 0){
                roman += "CM";
                arabic -= 900;
        }
        while (arabic - 500 >= 0){
                roman += "D";
                arabic -= 500;
        }
        while (arabic - 400 >= 0){
                roman += "CD";
                arabic -= 400;
        }
        while (arabic - 100 >= 0){
                roman += "C";
                arabic -= 100;
        }
        while (arabic - 90 >= 0){
                roman += "XC";
                arabic -= 90;
        }
        while (arabic - 50 >= 0){
                roman += "L";
                arabic -= 50;
        }
        while (arabic - 40 >= 0){
                roman += "XL";
                arabic -= 40;
        }
        while (arabic - 10 >= 0){
                roman += "X";
                arabic -= 10;
        }
        while (arabic - 9 >= 0){
                roman += "IX";
                arabic -= 9;
        }
        while (arabic - 5 >= 0){
                roman += "V";
                arabic -= 5;
        }
        while (arabic - 4 >= 0){
                roman += "IV";
                arabic -= 4;
        }
        while (arabic - 1 >= 0){
                roman += "I";
                arabic -= 1;
        }
        return roman;
}

//CB Moved from scribus.cpp
void parsePagesString(const QString& pages, std::vector<int>* pageNs, int sourcePageCount)
{
        QString tmp(pages);
        QString token;
        do
        {
                if (tmp.indexOf(",") == -1)
                {
                        token = tmp;
                        tmp = "";
                }
                else
                {
                        token = tmp.left(tmp.indexOf(","));
                        tmp = tmp.right(tmp.length() - tmp.indexOf(",") - 1);
                }

                token = token.trimmed();
                if (token == "*") // Import all source doc pages
                {
                        for (int i = 1; i <= sourcePageCount; ++i)
                                pageNs->push_back(i);
                }
                else if (token.indexOf("-") != -1) // import a range of source doc pages
                {
                        int from = QStringRef(token.leftRef(token.indexOf("-"))).toInt();
                        int to = QStringRef(token.rightRef(token.length() - token.indexOf("-") - 1)).toInt();
                        if ((from != 0) && (to != 0))
                        {
                                if (from > sourcePageCount)
                                        from = sourcePageCount;
                                if (to > sourcePageCount)
                                        to = sourcePageCount;
                                if (from == to)
                                        pageNs->push_back(to);
                                else if (from < to)
                                {
                                        for (int i = from; i <= to; ++i)
                                                pageNs->push_back(i);
                                }
                                else
                                {
                                        for (int i = from; i >= to; --i)
                                                pageNs->push_back(i);
                                }
                        }
                }
                else // import single source doc page
                {
                        int pageNr = token.toInt();
                        if ((pageNr > 0) && (pageNr <= sourcePageCount))
                                pageNs->push_back(pageNr);
                }
        } while (!tmp.isEmpty());
}


QString readLineFromDataStream(QDataStream &s)
{
        QString ret;
        uchar charData;
        while (!s.atEnd())
        {
                s >> charData;
                if (charData == '\x0A')
                        break;
                if (charData == '\x0D')
                {
                        quint64 oldPos = s.device()->pos();
                        s >> charData;
                        if (charData != '\x0A')
                                s.device()->seek(oldPos);
                        break;
                }
                ret += QChar(charData);
        }
        return ret.trimmed();
}

void setCurrentComboItem(QComboBox *box, const QString& text)
{
        QSignalBlocker signalBlocker(box);
        int ind = box->findText(text);
        if (ind > -1)
                box->setCurrentIndex(ind);
}

void setCurrentComboItemFromData(QComboBox *box, const QString& data)
{
        QSignalBlocker signalBlocker(box);
        int ind = box->findData(data);
        if (ind > -1)
                box->setCurrentIndex(ind);
}

void removeComboItem(QComboBox *box, const QString& text)
{
        QSignalBlocker signalBlocker(box);
        int ind = box->findText(text);
        if (ind > -1)
                box->removeItem(ind);
}

QString readAdobeUniCodeString(QDataStream &s)
{
        QString ret;
        quint32 len;
        s >> len;
        for (quint32 i = 0; i < len; i++)
        {
                quint16 ch;
                s >> ch;
                if (ch != 0)
                        ret.append(QChar(ch));
        }
        return ret;
}

QString readAdobeUniCodeString16(QDataStream &s)
{
        QString ret;
        quint16 len;
        s >> len;
        for (quint16 i = 0; i < len; i++)
        {
                quint16 ch;
                s >> ch;
                if (ch != 0)
                        ret.append(QChar(ch));
        }
        return ret;
}

QString getDashString(int dashtype, double linewidth)
{
        QString dashString;
        QVector<double> dashArray;
        getDashArray(dashtype, linewidth, dashArray);
        for (int i = 0; i < dashArray.size(); ++i)
        {
                dashString += QString::number(dashArray.at(i));
                if (i < (dashArray.size() - 1))
                        dashString += " ";
        }
        return dashString;
}

void getDashArray(int dashtype, double linewidth, QVector<float> &dashArray)
{
        QVector<double> tmp;
        getDashArray(dashtype, linewidth, tmp);
        dashArray.clear();
        for (int i = 0; i < tmp.count(); ++i)
                dashArray << static_cast<float>(tmp[i]);
}

void getDashArray(int dashtype, double linewidth, QVector<double> &dashArray)
{
        dashArray.clear();
        if ((dashtype == 1) || (dashtype == 0))
                return;
        double Dt = qMax(1.0*linewidth, 0.1);
        double Sp = qMax(2.0*linewidth, 0.1);
        double Da = qMax(4.0*linewidth, 0.1);
        switch (dashtype)
        {
                case 1:
                        break;
                case 2:
                        dashArray << Da << Sp;
                        break;
                case 3:
                        dashArray << Dt << Sp;
                        break;
                case 4:
                        dashArray << Da << Sp << Dt << Sp;
                        break;
                case 5:
                        dashArray << Da << Sp << Dt << Sp << Dt << Sp;
                        break;
// Additional line styles taken from Inkscape
                case 6:
                        dashArray << qMax(1.0 * linewidth, 0.01) << qMax(1.0 * linewidth, 0.01);
                        break;
                case 7:
                        dashArray << qMax(1.0 * linewidth, 0.01) << qMax(3.0 * linewidth, 0.01);
                        break;
                case 8:
                        dashArray << qMax(1.0 * linewidth, 0.01) << qMax(4.0 * linewidth, 0.01);
                        break;
                case 9:
                        dashArray << qMax(1.0 * linewidth, 0.01) << qMax(6.0 * linewidth, 0.01);
                        break;
                case 10:
                        dashArray << qMax(1.0 * linewidth, 0.01) << qMax(8.0 * linewidth, 0.01);
                        break;
                case 11:
                        dashArray << qMax(1.0 * linewidth, 0.01) << qMax(12.0 * linewidth, 0.01);
                        break;
                case 12:
                        dashArray << qMax(1.0 * linewidth, 0.01) << qMax(24.0 * linewidth, 0.01);
                        break;
                case 13:
                        dashArray << qMax(1.0 * linewidth, 0.01) << qMax(48.0 * linewidth, 0.01);
                        break;
                case 14:
                        dashArray << qMax(2.0 * linewidth, 0.01) << qMax(1.0 * linewidth, 0.01);
                        break;
                case 15:
                        dashArray << qMax(3.0 * linewidth, 0.01) << qMax(1.0 * linewidth, 0.01);
                        break;
                case 16:
                        dashArray << qMax(4.0 * linewidth, 0.01) << qMax(1.0 * linewidth, 0.01);
                        break;
                case 17:
                        dashArray << qMax(6.0 * linewidth, 0.01) << qMax(1.0 * linewidth, 0.01);
                        break;
                case 18:
                        dashArray << qMax(8.0 * linewidth, 0.01) << qMax(1.0 * linewidth, 0.01);
                        break;
                case 19:
                        dashArray << qMax(10.0 * linewidth, 0.01) << qMax(1.0 * linewidth, 0.01);
                        break;
                case 20:
                        dashArray << qMax(12.0 * linewidth, 0.01) << qMax(1.0 * linewidth, 0.01);
                        break;
                case 21:
                        dashArray << qMax(2.0 * linewidth, 0.01) << qMax(2.0 * linewidth, 0.01);
                        break;
                case 22:
                        dashArray << qMax(3.0 * linewidth, 0.01) << qMax(3.0 * linewidth, 0.01);
                        break;
                case 23:
                        dashArray << qMax(4.0 * linewidth, 0.01) << qMax(4.0 * linewidth, 0.01);
                        break;
                case 24:
                        dashArray << qMax(6.0 * linewidth, 0.01) << qMax(6.0 * linewidth, 0.01);
                        break;
                case 25:
                        dashArray << qMax(8.0 * linewidth, 0.01) << qMax(8.0 * linewidth, 0.01);
                        break;
                case 26:
                        dashArray << qMax(10.0 * linewidth, 0.01) << qMax(10.0 * linewidth, 0.01);
                        break;
                case 27:
                        dashArray << qMax(12.0 * linewidth, 0.01) << qMax(12.0 * linewidth, 0.01);
                        break;
                case 28:
                        dashArray << qMax(2.0 * linewidth, 0.01) << qMax(4.0 * linewidth, 0.01);
                        break;
                case 29:
                        dashArray << qMax(2.0 * linewidth, 0.01) << qMax(6.0 * linewidth, 0.01);
                        break;
                case 30:
                        dashArray << qMax(6.0 * linewidth, 0.01) << qMax(2.0 * linewidth, 0.01);
                        break;
                case 31:
                        dashArray << qMax(4.0 * linewidth, 0.01) << qMax(8.0 * linewidth, 0.01);
                        break;
                case 32:
                        dashArray << qMax(8.0 * linewidth, 0.01) << qMax(4.0 * linewidth, 0.01);
                        break;
                case 33:
                        dashArray << qMax(2.0 * linewidth, 0.01) << qMax(1.0 * linewidth, 0.01);
                        dashArray << qMax(0.5 * linewidth, 0.01) << qMax(1.0 * linewidth, 0.01);
                        break;
                case 34:
                        dashArray << qMax(8.0 * linewidth, 0.01) << qMax(2.0 * linewidth, 0.01);
                        dashArray << qMax(1.0 * linewidth, 0.01) << qMax(2.0 * linewidth, 0.01);
                        break;
                case 35:
                        dashArray << qMax(0.5 * linewidth, 0.01) << qMax(0.5 * linewidth, 0.01);
                        break;
                case 36:
                        dashArray << qMax(0.25 * linewidth, 0.01) << qMax(0.25 * linewidth, 0.01);
                        break;
                case 37:
                        dashArray << qMax(0.1 * linewidth, 0.01) << qMax(0.1 * linewidth, 0.01);
                        break;
                default:
                        break;
        }
}

bool convertOldTable(ScribusDoc *m_Doc, PageItem* gItem, QList<PageItem*> &gpL, QStack<QList<PageItem *> > *groupStackT, QList<PageItem *> *target)
{
        QList<double> colWidths;
        QList<double> rowHeights;

        // 1. Although this was not intended, legacy tables allowed user to link frames together
        // New table do not support that, so if one frame has any link, we stop the conversion
        // here, those frame will be converted to a standard group.
        // 2. Pre-1.4.3 versions had a bug where item TopLink/LeftLink/BottomLink/RightLink were
        // lost when copy/pasting tables. Exit conversion too so these broken tables can be
        // converted to standard groups (at least until we find a good way to process that case)
        bool hasTableLinks = false;
        bool hasTextLinks = false;
        for (int i = 0; i < gpL.count(); i++)
        {
                PageItem* it = gpL[i];
                it->isTableItem = false;
                if (it->nextInChain() || it->prevInChain())
                        hasTextLinks = true;
                if (it->m_leftLink || it->m_rightLink || it->m_bottomLink || it->m_topLink)
                        hasTableLinks = true;
        }

        if (!hasTableLinks || hasTextLinks)
                return false;

        PageItem *topLeft = nullptr;
        for (int i = 0; i < gpL.count(); i++)
        {
                PageItem* it = gpL[i];
                if ((it->m_topLink == nullptr) && (it->m_leftLink == nullptr))  // we got the topleft item
                {
                        topLeft = it;
                        PageItem *tl = it;
                        while (tl->m_rightLink != nullptr)
                        {
                                colWidths.append(tl->width());
                                tl = tl->m_rightLink;
                        }
                        colWidths.append(tl->width());
                        while (tl->m_bottomLink != nullptr)
                        {
                                rowHeights.append(tl->height());
                                tl = tl->m_bottomLink;
                        }
                        rowHeights.append(tl->height());
                        break;
                }
        }

        // Check we have found enough rows and columns so that no item will disappear
        if ((colWidths.count() * rowHeights.count()) < gpL.count())
                return false;

        m_Doc->dontResize = true;
        int z = m_Doc->itemAdd(PageItem::Table, PageItem::Unspecified, gItem->xPos(), gItem->yPos(), gItem->width(), gItem->height(), 0.0, CommonStrings::None, CommonStrings::None);
        PageItem_Table* currItem = m_Doc->Items->takeAt(z)->asTable();

        currItem->m_layerID = gItem->m_layerID;
        currItem->OwnPage = gItem->OwnPage;
        currItem->OnMasterPage = gItem->OnMasterPage;

        currItem->insertRows(0, rowHeights.count()-1);
        m_Doc->dontResize = true;
        currItem->insertColumns(0, colWidths.count()-1);
        m_Doc->dontResize = true;
        for (int i = 0; i < rowHeights.count(); i++)
        {
                currItem->resizeRow(i, rowHeights[i]);
        }
        m_Doc->dontResize = true;
        for (int i = 0; i < colWidths.count(); i++)
        {
                currItem->resizeColumn(i, colWidths[i]);
        }
        TableBorder border(0.0, Qt::SolidLine, CommonStrings::None, 100);
        currItem->setLeftBorder(border);
        currItem->setTopBorder(border);
        currItem->setRightBorder(border);
        currItem->setBottomBorder(border);
        m_Doc->dontResize = true;
        PageItem *tr = topLeft;
        int rowCount = 0;
        int colCount = 0;
        while (rowCount < rowHeights.count())
        {
                PageItem *tl = tr;
                while (colCount < colWidths.count())
                {
                        currItem->cellAt(rowCount, colCount).textFrame()->itemText = tl->itemText.copy();
                        currItem->cellAt(rowCount, colCount).setFillColor(tl->fillColor());
                        currItem->cellAt(rowCount, colCount).setFillShade(tl->fillShade());
                        currItem->cellAt(rowCount, colCount).setLeftBorder(border);
                        currItem->cellAt(rowCount, colCount).setTopBorder(border);
                        currItem->cellAt(rowCount, colCount).setRightBorder(border);
                        currItem->cellAt(rowCount, colCount).setBottomBorder(border);
                        if ((tl->lineColor() != CommonStrings::None) && (tl->lineWidth() != 0.0))
                        {
                                TableBorder bb(tl->lineWidth(), tl->lineStyle(), tl->lineColor(), tl->lineShade());
                                if (tl->LeftLine)
                                        currItem->cellAt(rowCount, colCount).setLeftBorder(bb);
                                if (tl->TopLine)
                                        currItem->cellAt(rowCount, colCount).setTopBorder(bb);
                                if (tl->RightLine)
                                        currItem->cellAt(rowCount, colCount).setRightBorder(bb);
                                if (tl->BottomLine)
                                        currItem->cellAt(rowCount, colCount).setBottomBorder(bb);
                        }
                        if (colCount == colWidths.count()-1)
                                break;
                        colCount++;
                        tl = tl->m_rightLink;
                }
                if (rowCount == rowHeights.count()-1)
                        break;
                colCount = 0;
                rowCount++;
                tr = tr->m_bottomLink;
        }
        m_Doc->dontResize = true;
        currItem->setLayer(gItem->m_layerID);
        currItem->setMasterPage(gItem->OwnPage, gItem->OnMasterPage);
        currItem->adjustFrameToTable();
        if (target != nullptr)
        {
                int ind = target->indexOf(gItem);
                target->replace(ind, currItem);
        }
        else
        {
                int ind = m_Doc->FrameItems.key(gItem);
                m_Doc->FrameItems.remove(ind);
                m_Doc->FrameItems.insert(ind, currItem);
        }
        if (groupStackT != nullptr)
        {
                if (groupStackT->count() > 0)
                {
                        int ii = groupStackT->top().indexOf(gItem);
                        if (ii >= 0)
                                groupStackT->top().replace(ii, currItem);
                }
        }
        while (!gpL.isEmpty())
        {
                PageItem* item = gpL.takeFirst();
                if (item->isTextFrame())
                        item->dropLinks();
                delete item;
        }
        delete gItem;

        return true;
}

void setWidgetBoldFont(QWidget* w, bool wantBold)
{
        QFont f(w->font());
        f.setBold(wantBold);
        w->setFont(f);
}


void getUniqueName(QString &name, const QStringList& list, const QString& separator, bool prepend)
{
        if (!list.contains(name))
                return;
        int token = -1;
        QString newName;
        do {
                token++;
                if (prepend)
                        newName = separator + QString::number(token) + " " + name;
                else
                        newName = name + separator + QString::number(token);
        } while (list.contains(newName));
        name = newName;
}

QString numberToHebrew(uint i)
{
        const QString hebrew("אבגדהוזחטיכלמנסעפצקרשת");
        QString result;

        if (i > 999)
        {
                result.append(numberToHebrew(i / 1000));
                result.append(QChar(0x05F3));
                i %= 1000;
        }

        int hundreds = i / 100;
        int tens = (i - hundreds * 100) / 10;
        int ones = i % 10;

        while (hundreds > 4)
        {
                result.append(hebrew.at(21));
                hundreds -= 4;
        }

        if (hundreds)
                result.append(hebrew.at(hundreds + 17));

        if (tens == 1 && ones == 5)
                result.append("טו");
        else if (tens == 1 && ones == 6)
                result.append("טז");
        else
        {
                if (tens)
                        result.append(hebrew.at(tens + 8));
                if (ones)
                        result.append(hebrew.at(ones - 1));
        }

        return result;
}

QString numberToCJK(uint i)
{
        QString result;
        if (i<10)
                result = QString(cjkDigit(i));

        if (i>9 && i<=99)
        {
                int tens=i/10;
                int ones=i%10;
                if (tens!=1)
                        result.append(cjkDigit(tens));
                result.append(cjkDigit(10));
                if (ones!=0)
                        result.append(cjkDigit(ones));
        }

        if (i>99 && i<=999)
        {
                int hundreds=i/100;
                int tens=(i-hundreds*100)/10;
                int ones=i%10;
                result.append(cjkDigit(hundreds));
                result.append(cjkDigit(100));
                if (tens!=0)
                {
                        result.append(cjkDigit(tens));
                        result.append(cjkDigit(10));
                }
                else if (ones!=0)
                        result.append(cjkDigit(0));
                if (ones!=0)
                        result.append(cjkDigit(ones));
        }
        return result;
}


QChar cjkDigit(uint i)
{
        switch (i)
        {
                case 0:
                        return QChar(0x96f6);
                        break;
                case 1:
                        return QChar(0x4e00);
                        break;
                case 2:
                        return QChar(0x4e8c);
                        break;
                case 3:
                        return QChar(0x4e09);
                        break;
                case 4:
                        return QChar(0x56db);
                        break;
                case 5:
                        return QChar(0x4e94);
                        break;
                case 6:
                        return QChar(0x516d);
                        break;
                case 7:
                        return QChar(0x4e03);
                        break;
                case 8:
                        return QChar(0x516b);
                        break;
                case 9:
                        return QChar(0x4e5d);
                        break;
                case 10:
                        return QChar(0x5341);
                        break;
                case 100:
                        return QChar(0x767e);
                        break;
                case 1000:
                        return QChar(0x5343);
                        break;
                case 10000:
                        return QChar(0x842c);
                        break;
                case 100000000:
                        return QChar(0x5104);
                        break;
        }
        return QChar::Null;
}