File indexing completed on 2024-05-26 03:50:50

0001 // xlsxdocument.cpp
0002 
0003 #include <QtGlobal>
0004 #include <QFile>
0005 #include <QPointF>
0006 #include <QBuffer>
0007 #include <QDir>
0008 #include <QTemporaryFile>
0009 #include <QFile>
0010 #include <QSharedPointer>
0011 #include <QDebug>
0012 
0013 #include "xlsxdocument.h"
0014 #include "xlsxdocument_p.h"
0015 #include "xlsxworkbook.h"
0016 #include "xlsxworksheet.h"
0017 #include "xlsxcontenttypes_p.h"
0018 #include "xlsxrelationships_p.h"
0019 #include "xlsxstyles_p.h"
0020 #include "xlsxtheme_p.h"
0021 #include "xlsxdocpropsapp_p.h"
0022 #include "xlsxdocpropscore_p.h"
0023 #include "xlsxsharedstrings_p.h"
0024 #include "xlsxutility_p.h"
0025 #include "xlsxworkbook_p.h"
0026 #include "xlsxdrawing_p.h"
0027 #include "xlsxmediafile_p.h"
0028 #include "xlsxchart.h"
0029 #include "xlsxzipreader_p.h"
0030 #include "xlsxzipwriter_p.h"
0031 
0032 /*
0033     From Wikipedia: The Open Packaging Conventions (OPC) is a
0034     container-file technology initially created by Microsoft to store
0035     a combination of XML and non-XML files that together form a single
0036     entity such as an Open XML Paper Specification (OpenXPS)
0037     document. http://en.wikipedia.org/wiki/Open_Packaging_Conventions.
0038 
0039     At its simplest an Excel XLSX file contains the following elements:
0040 
0041          ____ [Content_Types].xml
0042         |
0043         |____ docProps
0044         | |____ app.xml
0045         | |____ core.xml
0046         |
0047         |____ xl
0048         | |____ workbook.xml
0049         | |____ worksheets
0050         | | |____ sheet1.xml
0051         | |
0052         | |____ styles.xml
0053         | |
0054         | |____ theme
0055         | | |____ theme1.xml
0056         | |
0057         | |_____rels
0058         | |____ workbook.xml.rels
0059         |
0060         |_____rels
0061           |____ .rels
0062 
0063     The Packager class coordinates the classes that represent the
0064     elements of the package and writes them into the XLSX file.
0065 */
0066 
0067 QT_BEGIN_NAMESPACE_XLSX
0068 
0069 namespace xlsxDocumentCpp {
0070     std::string copyTag(const std::string &sFrom, const std::string &sTo, const std::string &tag) {
0071         const std::string tagToFindStart = "<" + tag;
0072         const std::string tagToFindEnd = "</" + tag;
0073         const std::string tagEnd = "</" + tag + ">";
0074 
0075         // search all occurrences of tag in 'sFrom'
0076         std::string sFromData = "";
0077         size_t startIndex = 0;
0078         while (true) {
0079             std::size_t startPos = sFrom.find(tagToFindStart, startIndex);
0080             if (startPos != std::string::npos) {
0081                 std::size_t endPos = sFrom.find(tagToFindEnd, startPos);
0082                 std::string tagEndTmp = tagEnd;
0083                 if (endPos == std::string::npos) {  // second try to find the ending, maybe it is "/>" 
0084                     endPos = sFrom.find("/>", startPos);
0085                     tagEndTmp = "/>";
0086                 }
0087                 if (endPos != std::string::npos) {
0088                     sFromData += sFrom.substr(startPos, endPos - startPos) + tagEndTmp;
0089                     startIndex = endPos + strlen(tagEndTmp.c_str());
0090                 }
0091                 else {
0092                     break;
0093                 }
0094             }
0095             else {
0096                 break;
0097             }
0098         }
0099 
0100         std::string sOut = sTo; // copy 'sTo' in the output string
0101 
0102         if (!sFromData.empty()) { // tag found in 'from'?
0103                                   // search all occurrences of tag in 'sOut' and delete them
0104             int firstPosTag = -1;
0105             while (true) {
0106                 std::size_t startPos = sOut.find(tagToFindStart);
0107                 if (startPos != std::string::npos) {
0108                     std::size_t endPos = sOut.find(tagToFindEnd);
0109                     std::string tagEndTmp = tagEnd;
0110                     if (endPos == std::string::npos) {  // second try to find the ending, maybe it is "/>" 
0111                         endPos = sOut.find("/>", startPos);
0112                         tagEndTmp = "/>";
0113                     }
0114                     if (endPos != std::string::npos) {
0115                         if (firstPosTag < 0)
0116                             firstPosTag = startPos;
0117                         std::string stringBefore = sOut.substr(0, startPos);
0118                         endPos += strlen(tagEndTmp.c_str());
0119                         std::string stringAfter = sOut.substr(endPos, strlen(sOut.c_str()) - endPos);
0120                         sOut = stringBefore + stringAfter;
0121                     }
0122                     else {
0123                         break;
0124                     }
0125                 }
0126                 else {
0127                     break;
0128                 }
0129             }
0130 
0131             if (firstPosTag == -1) {
0132                 // tag not found in 'sTo' file
0133                 // try to find a default pos using standard tags
0134                 std::vector<std::string> defaultPos{ "</styleSheet>", "<pageMargins", "</workbook>" };
0135                 for (unsigned int i = 0; i < defaultPos.size(); ++i) {
0136                     std::size_t iDefaultPos = sOut.find(defaultPos[i]);
0137                     if (iDefaultPos != std::string::npos) {
0138                         firstPosTag = iDefaultPos;
0139                         break;
0140                     }
0141                 }
0142             }
0143 
0144             // add the tag extracted from 'sFrom' in 'sOut'
0145             // add in the position of the first tag found in 'sOut' ('firstPosTag')
0146             if (firstPosTag >= 0) {
0147                 std::string stringBefore = sOut.substr(0, firstPosTag);
0148                 std::string stringAfter = sOut.substr(firstPosTag, strlen(sOut.c_str()) - firstPosTag);
0149                 sOut = stringBefore + sFromData + stringAfter;
0150             }
0151         }
0152 
0153         return sOut;
0154     }
0155 }
0156 
0157 DocumentPrivate::DocumentPrivate(Document *p) :
0158     q_ptr(p), defaultPackageName(QStringLiteral("Book1.xlsx")),
0159     isLoad(false)
0160 {
0161 }
0162 
0163 void DocumentPrivate::init()
0164 {
0165     if (!contentTypes)
0166         contentTypes = std::make_shared<ContentTypes>(ContentTypes::F_NewFromScratch);
0167 
0168     if (workbook.isNull())
0169         workbook = QSharedPointer<Workbook>(new Workbook(Workbook::F_NewFromScratch));
0170 }
0171 
0172 bool DocumentPrivate::loadPackage(QIODevice *device)
0173 {
0174     Q_Q(Document);
0175     ZipReader zipReader(device);
0176     QStringList filePaths = zipReader.filePaths();
0177 
0178     //Load the Content_Types file
0179     if (!filePaths.contains(QLatin1String("[Content_Types].xml")))
0180         return false;
0181     contentTypes = std::make_shared<ContentTypes>(ContentTypes::F_LoadFromExists);
0182     contentTypes->loadFromXmlData(zipReader.fileData(QStringLiteral("[Content_Types].xml")));
0183 
0184     //Load root rels file
0185     if (!filePaths.contains(QLatin1String("_rels/.rels")))
0186         return false;
0187     Relationships rootRels;
0188     rootRels.loadFromXmlData(zipReader.fileData(QStringLiteral("_rels/.rels")));
0189 
0190     //load core property
0191     QList<XlsxRelationship> rels_core = rootRels.packageRelationships(QStringLiteral("/metadata/core-properties"));
0192     if (!rels_core.isEmpty()) {
0193         //Get the core property file name if it exists.
0194         //In normal case, this should be "docProps/core.xml"
0195         QString docPropsCore_Name = rels_core[0].target;
0196 
0197         DocPropsCore props(DocPropsCore::F_LoadFromExists);
0198         props.loadFromXmlData(zipReader.fileData(docPropsCore_Name));
0199         const auto propNames = props.propertyNames();
0200         for (const QString &name : propNames)
0201             q->setDocumentProperty(name, props.property(name));
0202     }
0203 
0204     //load app property
0205     QList<XlsxRelationship> rels_app = rootRels.documentRelationships(QStringLiteral("/extended-properties"));
0206     if (!rels_app.isEmpty()) {
0207         //Get the app property file name if it exists.
0208         //In normal case, this should be "docProps/app.xml"
0209         QString docPropsApp_Name = rels_app[0].target;
0210 
0211         DocPropsApp props(DocPropsApp::F_LoadFromExists);
0212         props.loadFromXmlData(zipReader.fileData(docPropsApp_Name));
0213         const auto propNames = props.propertyNames();
0214         for (const QString &name : propNames)
0215             q->setDocumentProperty(name, props.property(name));
0216     }
0217 
0218     //load workbook now, Get the workbook file path from the root rels file
0219     //In normal case, this should be "xl/workbook.xml"
0220     workbook = QSharedPointer<Workbook>(new Workbook(Workbook::F_LoadFromExists));
0221     QList<XlsxRelationship> rels_xl = rootRels.documentRelationships(QStringLiteral("/officeDocument"));
0222     if (rels_xl.isEmpty())
0223         return false;
0224     const QString xlworkbook_Path = rels_xl[0].target;
0225     const auto parts = splitPath(xlworkbook_Path);
0226     const QString xlworkbook_Dir = parts.first();
0227     const QString relFilePath = getRelFilePath(xlworkbook_Path);
0228 
0229     workbook->relationships()->loadFromXmlData( zipReader.fileData(relFilePath) );
0230     workbook->setFilePath(xlworkbook_Path);
0231     workbook->loadFromXmlData(zipReader.fileData(xlworkbook_Path));
0232 
0233     //load styles
0234     QList<XlsxRelationship> rels_styles = workbook->relationships()->documentRelationships(QStringLiteral("/styles"));
0235     if (!rels_styles.isEmpty()) {
0236         //In normal case this should be styles.xml which in xl
0237         QString name = rels_styles[0].target;
0238 
0239         // dev34
0240         QString path;
0241         if ( xlworkbook_Dir == QLatin1String(".") ) // root
0242         {
0243             path = name;
0244         }
0245         else
0246         {
0247             path = xlworkbook_Dir + QLatin1String("/") + name;
0248         }
0249 
0250         QSharedPointer<Styles> styles (new Styles(Styles::F_LoadFromExists));
0251         styles->loadFromXmlData(zipReader.fileData(path));
0252         workbook->d_func()->styles = styles;
0253     }
0254 
0255     //load sharedStrings
0256     QList<XlsxRelationship> rels_sharedStrings = workbook->relationships()->documentRelationships(QStringLiteral("/sharedStrings"));
0257     if (!rels_sharedStrings.isEmpty()) {
0258         //In normal case this should be sharedStrings.xml which in xl
0259         QString name = rels_sharedStrings[0].target;
0260         QString path = xlworkbook_Dir + QLatin1String("/") + name;
0261         workbook->d_func()->sharedStrings->loadFromXmlData(zipReader.fileData(path));
0262     }
0263 
0264     //load theme
0265     QList<XlsxRelationship> rels_theme = workbook->relationships()->documentRelationships(QStringLiteral("/theme"));
0266     if (!rels_theme.isEmpty()) {
0267         //In normal case this should be theme/theme1.xml which in xl
0268         QString name = rels_theme[0].target;
0269         QString path = xlworkbook_Dir + QLatin1String("/") + name;
0270         workbook->theme()->loadFromXmlData(zipReader.fileData(path));
0271     }
0272 
0273     //load sheets
0274     for (int i=0; i<workbook->sheetCount(); ++i) {
0275         AbstractSheet *sheet = workbook->sheet(i);
0276         QString strFilePath = sheet->filePath();
0277         QString rel_path = getRelFilePath(strFilePath);
0278         //If the .rel file exists, load it.
0279         if (zipReader.filePaths().contains(rel_path))
0280             sheet->relationships()->loadFromXmlData(zipReader.fileData(rel_path));
0281         sheet->loadFromXmlData(zipReader.fileData(sheet->filePath()));
0282     }
0283 
0284     //load external links
0285     for (int i=0; i<workbook->d_func()->externalLinks.count(); ++i) {
0286         SimpleOOXmlFile *link = workbook->d_func()->externalLinks[i].data();
0287         QString rel_path = getRelFilePath(link->filePath());
0288         //If the .rel file exists, load it.
0289         if (zipReader.filePaths().contains(rel_path))
0290             link->relationships()->loadFromXmlData(zipReader.fileData(rel_path));
0291         link->loadFromXmlData(zipReader.fileData(link->filePath()));
0292     }
0293 
0294     //load drawings
0295     for (int i=0; i<workbook->drawings().size(); ++i) {
0296         Drawing *drawing = workbook->drawings()[i];
0297         QString rel_path = getRelFilePath(drawing->filePath());
0298         if (zipReader.filePaths().contains(rel_path))
0299             drawing->relationships()->loadFromXmlData(zipReader.fileData(rel_path));
0300         drawing->loadFromXmlData(zipReader.fileData(drawing->filePath()));
0301     }
0302 
0303     //load charts
0304     QList<QSharedPointer<Chart> > chartFileToLoad = workbook->chartFiles();
0305     for (int i=0; i<chartFileToLoad.size(); ++i) {
0306         QSharedPointer<Chart> cf = chartFileToLoad[i];
0307         cf->loadFromXmlData(zipReader.fileData(cf->filePath()));
0308     }
0309 
0310     //load media files
0311     const auto mediaFileToLoad = workbook->mediaFiles();
0312     for (const auto &mf : mediaFileToLoad) {
0313         const QString path = mf->fileName();
0314         const QString suffix = path.mid(path.lastIndexOf(QLatin1Char('.'))+1);
0315         mf->set(zipReader.fileData(path), suffix);
0316     }
0317 
0318     isLoad = true; 
0319     return true;
0320 }
0321 
0322 bool DocumentPrivate::savePackage(QIODevice *device) const
0323 {
0324     Q_Q(const Document);
0325 
0326     ZipWriter zipWriter(device);
0327     if (zipWriter.error())
0328         return false;
0329 
0330     contentTypes->clearOverrides();
0331 
0332     DocPropsApp docPropsApp(DocPropsApp::F_NewFromScratch);
0333     DocPropsCore docPropsCore(DocPropsCore::F_NewFromScratch);
0334 
0335     // save worksheet xml files
0336     QList<QSharedPointer<AbstractSheet> > worksheets = workbook->getSheetsByTypes(AbstractSheet::ST_WorkSheet);
0337     if (!worksheets.isEmpty())
0338         docPropsApp.addHeadingPair(QStringLiteral("Worksheets"), worksheets.size());
0339 
0340     for (int i = 0 ; i < worksheets.size(); ++i)
0341     {
0342         QSharedPointer<AbstractSheet> sheet = worksheets[i];
0343         contentTypes->addWorksheetName(QStringLiteral("sheet%1").arg(i+1));
0344         docPropsApp.addPartTitle(sheet->sheetName());
0345 
0346         zipWriter.addFile(QStringLiteral("xl/worksheets/sheet%1.xml").arg(i+1), sheet->saveToXmlData());
0347 
0348         Relationships *rel = sheet->relationships();
0349         if (!rel->isEmpty())
0350             zipWriter.addFile(QStringLiteral("xl/worksheets/_rels/sheet%1.xml.rels").arg(i+1), rel->saveToXmlData());
0351     }
0352 
0353     //save chartsheet xml files
0354     QList<QSharedPointer<AbstractSheet> > chartsheets = workbook->getSheetsByTypes(AbstractSheet::ST_ChartSheet);
0355     if (!chartsheets.isEmpty())
0356         docPropsApp.addHeadingPair(QStringLiteral("Chartsheets"), chartsheets.size());
0357     for (int i=0; i<chartsheets.size(); ++i)
0358     {
0359         QSharedPointer<AbstractSheet> sheet = chartsheets[i];
0360         contentTypes->addWorksheetName(QStringLiteral("sheet%1").arg(i+1));
0361         docPropsApp.addPartTitle(sheet->sheetName());
0362 
0363         zipWriter.addFile(QStringLiteral("xl/chartsheets/sheet%1.xml").arg(i+1), sheet->saveToXmlData());
0364         Relationships *rel = sheet->relationships();
0365         if (!rel->isEmpty())
0366             zipWriter.addFile(QStringLiteral("xl/chartsheets/_rels/sheet%1.xml.rels").arg(i+1), rel->saveToXmlData());
0367     }
0368 
0369     // save external links xml files
0370     for (int i=0; i<workbook->d_func()->externalLinks.count(); ++i)
0371     {
0372         SimpleOOXmlFile *link = workbook->d_func()->externalLinks[i].data();
0373         contentTypes->addExternalLinkName(QStringLiteral("externalLink%1").arg(i+1));
0374 
0375         zipWriter.addFile(QStringLiteral("xl/externalLinks/externalLink%1.xml").arg(i+1), link->saveToXmlData());
0376         Relationships *rel = link->relationships();
0377         if (!rel->isEmpty())
0378             zipWriter.addFile(QStringLiteral("xl/externalLinks/_rels/externalLink%1.xml.rels").arg(i+1), rel->saveToXmlData());
0379     }
0380 
0381     // save workbook xml file
0382     contentTypes->addWorkbook();
0383     zipWriter.addFile(QStringLiteral("xl/workbook.xml"), workbook->saveToXmlData());
0384     zipWriter.addFile(QStringLiteral("xl/_rels/workbook.xml.rels"), workbook->relationships()->saveToXmlData());
0385 
0386     // save drawing xml files
0387     for (int i=0; i<workbook->drawings().size(); ++i)
0388     {
0389         contentTypes->addDrawingName(QStringLiteral("drawing%1").arg(i+1));
0390 
0391         Drawing *drawing = workbook->drawings()[i];
0392         zipWriter.addFile(QStringLiteral("xl/drawings/drawing%1.xml").arg(i+1), drawing->saveToXmlData());
0393         if (!drawing->relationships()->isEmpty())
0394             zipWriter.addFile(QStringLiteral("xl/drawings/_rels/drawing%1.xml.rels").arg(i+1), drawing->relationships()->saveToXmlData());
0395     }
0396 
0397     // save docProps app/core xml file
0398     const auto docPropNames = q->documentPropertyNames();
0399     for (const QString &name : docPropNames) {
0400         docPropsApp.setProperty(name, q->documentProperty(name));
0401         docPropsCore.setProperty(name, q->documentProperty(name));
0402     }
0403     contentTypes->addDocPropApp();
0404     contentTypes->addDocPropCore();
0405     zipWriter.addFile(QStringLiteral("docProps/app.xml"), docPropsApp.saveToXmlData());
0406     zipWriter.addFile(QStringLiteral("docProps/core.xml"), docPropsCore.saveToXmlData());
0407 
0408     // save sharedStrings xml file
0409     if (!workbook->sharedStrings()->isEmpty()) {
0410         contentTypes->addSharedString();
0411         zipWriter.addFile(QStringLiteral("xl/sharedStrings.xml"), workbook->sharedStrings()->saveToXmlData());
0412     }
0413 
0414     // save calc chain [dev16]
0415     contentTypes->addCalcChain();
0416     zipWriter.addFile(QStringLiteral("xl/calcChain.xml"), workbook->styles()->saveToXmlData());
0417 
0418     // save styles xml file
0419     contentTypes->addStyles();
0420     zipWriter.addFile(QStringLiteral("xl/styles.xml"), workbook->styles()->saveToXmlData());
0421 
0422     // save theme xml file
0423     contentTypes->addTheme();
0424     zipWriter.addFile(QStringLiteral("xl/theme/theme1.xml"), workbook->theme()->saveToXmlData());
0425 
0426     // save chart xml files
0427     for (int i=0; i<workbook->chartFiles().size(); ++i)
0428     {
0429         contentTypes->addChartName(QStringLiteral("chart%1").arg(i+1));
0430         QSharedPointer<Chart> cf = workbook->chartFiles()[i];
0431         zipWriter.addFile(QStringLiteral("xl/charts/chart%1.xml").arg(i+1), cf->saveToXmlData());
0432     }
0433 
0434     // save image files
0435     const auto mfs = workbook->mediaFiles();
0436     for (int i=0; i < mfs.size(); ++i)
0437     {
0438         auto mf = mfs[i];
0439         if (!mf->mimeType().isEmpty())
0440             contentTypes->addDefault(mf->suffix(), mf->mimeType());
0441 
0442         zipWriter.addFile(QStringLiteral("xl/media/image%1.%2").arg(i+1).arg(mf->suffix()), mf->contents());
0443     }
0444 
0445     // save root .rels xml file
0446     Relationships rootrels;
0447     rootrels.addDocumentRelationship(QStringLiteral("/officeDocument"), QStringLiteral("xl/workbook.xml"));
0448     rootrels.addPackageRelationship(QStringLiteral("/metadata/core-properties"), QStringLiteral("docProps/core.xml"));
0449     rootrels.addDocumentRelationship(QStringLiteral("/extended-properties"), QStringLiteral("docProps/app.xml"));
0450     zipWriter.addFile(QStringLiteral("_rels/.rels"), rootrels.saveToXmlData());
0451 
0452     // save content types xml file
0453     zipWriter.addFile(QStringLiteral("[Content_Types].xml"), contentTypes->saveToXmlData());
0454 
0455     zipWriter.close();
0456     return true;
0457 }
0458 
0459 bool DocumentPrivate::copyStyle(const QString &from, const QString &to)
0460 {
0461     // create a temp file because the zip writer cannot modify already existing zips
0462     QTemporaryFile tempFile;
0463     tempFile.open();
0464     tempFile.close();
0465     QString temFilePath = QFileInfo(tempFile).absoluteFilePath();
0466 
0467     ZipWriter temporalZip(temFilePath);
0468 
0469     ZipReader zipReader(from);
0470     QStringList filePaths = zipReader.filePaths();
0471 
0472     QSharedPointer<ZipReader> toReader = QSharedPointer<ZipReader>(new ZipReader(to));
0473 
0474     QStringList toFilePaths = toReader->filePaths();
0475 
0476     // copy all files from "to" zip except those related to style
0477     for (int i = 0; i < toFilePaths.size(); i++) {
0478         if (toFilePaths[i].contains(QLatin1String("xl/styles"))) {
0479             if (filePaths.contains(toFilePaths[i])) {   // style file exist in 'from' as well
0480                 // modify style file
0481                 std::string fromData = QString::fromUtf8(zipReader.fileData(toFilePaths[i])).toStdString();
0482                 std::string toData = QString::fromUtf8(toReader->fileData(toFilePaths[i])).toStdString();
0483                 // copy default theme style from 'from' to 'to'
0484                 toData = xlsxDocumentCpp::copyTag(fromData, toData, "dxfs");
0485                 temporalZip.addFile(toFilePaths.at(i), QString::fromUtf8(toData.c_str()).toUtf8());
0486 
0487                 continue;
0488             }
0489         }
0490 
0491         if (toFilePaths[i].contains(QLatin1String("xl/workbook"))) {
0492             if (filePaths.contains(toFilePaths[i])) {   // workbook file exist in 'from' as well
0493                 // modify workbook file
0494                 std::string fromData = QString::fromUtf8(zipReader.fileData(toFilePaths[i])).toStdString();
0495                 std::string toData = QString::fromUtf8(toReader->fileData(toFilePaths[i])).toStdString();
0496                 // copy default theme style from 'from' to 'to'
0497                 toData = xlsxDocumentCpp::copyTag(fromData, toData, "workbookPr");
0498                 temporalZip.addFile(toFilePaths.at(i), QString::fromUtf8(toData.c_str()).toUtf8());
0499                 continue;
0500             }
0501         }
0502 
0503         if (toFilePaths[i].contains(QLatin1String("xl/worksheets/sheet"))) {
0504             if (filePaths.contains(toFilePaths[i])) {   // sheet file exist in 'from' as well
0505                 // modify sheet file
0506                 std::string fromData = QString::fromUtf8(zipReader.fileData(toFilePaths[i])).toStdString();
0507                 std::string toData = QString::fromUtf8(toReader->fileData(toFilePaths[i])).toStdString();
0508                 // copy "conditionalFormatting" from 'from' to 'to'
0509                 toData = xlsxDocumentCpp::copyTag(fromData, toData, "conditionalFormatting");
0510                 temporalZip.addFile(toFilePaths.at(i), QString::fromUtf8(toData.c_str()).toUtf8());
0511                 continue;
0512             }
0513         }
0514 
0515         QByteArray data = toReader->fileData(toFilePaths.at(i));
0516         temporalZip.addFile(toFilePaths.at(i), data);
0517     }
0518 
0519     temporalZip.close();
0520 
0521     toReader.clear();
0522 
0523     tempFile.close();
0524 
0525     QFile::remove(to);
0526     tempFile.copy(to);
0527 
0528     return true;
0529 }
0530 
0531 /*!
0532   \class Document
0533   \inmodule QtXlsx
0534   \brief The Document class provides a API that is used to handle the contents of .xlsx files.
0535 
0536 */
0537 
0538 /*!
0539  * Creates a new empty xlsx document.
0540  * The \a parent argument is passed to QObject's constructor.
0541  */
0542 Document::Document(QObject *parent) :
0543     QObject(parent), d_ptr(new DocumentPrivate(this))
0544 {
0545     d_ptr->init();
0546 }
0547 
0548 /*!
0549  * \overload
0550  * Try to open an existing xlsx document named \a name.
0551  * The \a parent argument is passed to QObject's constructor.
0552  */
0553 Document::Document(const QString &name, 
0554                     QObject *parent) :
0555     QObject(parent), 
0556     d_ptr(new DocumentPrivate(this))
0557 {
0558     d_ptr->packageName = name; 
0559 
0560     if (QFile::exists(name)) 
0561     {
0562         QFile xlsx(name);
0563         if (xlsx.open(QFile::ReadOnly))
0564         {
0565             if (! d_ptr->loadPackage(&xlsx))
0566             {
0567                 // NOTICE: failed to load package 
0568             }
0569         }
0570     }
0571 
0572     d_ptr->init();
0573 }
0574 
0575 /*!
0576  * \overload
0577  * Try to open an existing xlsx document from \a device.
0578  * The \a parent argument is passed to QObject's constructor.
0579  */
0580 Document::Document(QIODevice *device, QObject *parent) :
0581     QObject(parent), d_ptr(new DocumentPrivate(this))
0582 {
0583     if (device && device->isReadable())
0584     {
0585         if (!d_ptr->loadPackage(device))
0586         {
0587             // NOTICE: failed to load package 
0588         }
0589     }
0590     d_ptr->init();
0591 }
0592 
0593 /*!
0594     \overload
0595 
0596     Write \a value to cell \a row_column with the given \a format.
0597  */
0598 bool Document::write(const CellReference &row_column, const QVariant &value, const Format &format)
0599 {
0600     if (Worksheet *sheet = currentWorksheet())
0601         return sheet->write(row_column, value, format);
0602     return false;
0603 }
0604 
0605 /*!
0606  * Write \a value to cell (\a row, \a col) with the \a format.
0607  * Returns true on success.
0608  */
0609 bool Document::write(int row, int col, const QVariant &value, const Format &format)
0610 {
0611     if (Worksheet *sheet = currentWorksheet())
0612         return sheet->write(row, col, value, format);
0613     return false;
0614 }
0615 
0616 /*!
0617     \overload
0618     Returns the contents of the cell \a cell.
0619 
0620     \sa cellAt()
0621 */
0622 QVariant Document::read(const CellReference &cell) const
0623 {
0624     if (Worksheet *sheet = currentWorksheet())
0625         return sheet->read(cell);
0626     return QVariant();
0627 }
0628 
0629 /*!
0630     Returns the contents of the cell (\a row, \a col).
0631 
0632     \sa cellAt()
0633  */
0634 QVariant Document::read(int row, int col) const
0635 {
0636     if (Worksheet *sheet = currentWorksheet())
0637         return sheet->read(row, col);
0638     return QVariant();
0639 }
0640 
0641 /*!
0642  * Insert an \a image to current active worksheet at the position \a row, \a column
0643  * Returns ture if success.
0644  */
0645 int Document::insertImage(int row, int column, const QImage &image)
0646 {
0647     if (Worksheet *sheet = currentWorksheet())
0648         return sheet->insertImage(row, column, image);
0649 
0650     return 0;
0651 }
0652 
0653 bool Document::getImage(int imageIndex, QImage& img)
0654 {
0655     if (Worksheet *sheet = currentWorksheet())
0656         return sheet->getImage(imageIndex, img);
0657 
0658     return  false;
0659 }
0660 
0661 bool Document::getImage(int row, int col, QImage &img)
0662 {
0663     if (Worksheet *sheet = currentWorksheet())
0664         return sheet->getImage(row, col, img);
0665 
0666     return  false;
0667 }
0668 
0669 uint Document::getImageCount()
0670 {
0671     if (Worksheet *sheet = currentWorksheet())
0672         return sheet->getImageCount();
0673 
0674     return 0;
0675 }
0676 
0677 
0678 /*!
0679  * Creates an chart with the given \a size and insert it to the current
0680  * active worksheet at the position \a row, \a col.
0681  * The chart will be returned.
0682  */
0683 Chart *Document::insertChart(int row, int col, const QSize &size)
0684 {
0685     if (Worksheet *sheet = currentWorksheet())
0686         return sheet->insertChart(row, col, size);
0687     return nullptr;
0688 }
0689 
0690 /*!
0691   Merge a \a range of cells. The first cell should contain the data and the others should
0692   be blank. All cells will be applied the same style if a valid \a format is given.
0693   Returns true on success.
0694 
0695   \note All cells except the top-left one will be cleared.
0696  */
0697 bool Document::mergeCells(const CellRange &range, const Format &format)
0698 {
0699     if (Worksheet *sheet = currentWorksheet())
0700         return sheet->mergeCells(range, format);
0701     return false;
0702 }
0703 
0704 /*!
0705   Unmerge the cells in the \a range.
0706   Returns true on success.
0707 */
0708 bool Document::unmergeCells(const CellRange &range)
0709 {
0710     if (Worksheet *sheet = currentWorksheet())
0711         return sheet->unmergeCells(range);
0712     return false;
0713 }
0714 
0715 /*!
0716   Sets width in characters of columns with the given \a range and \a width.
0717   Returns true on success.
0718  */
0719 bool Document::setColumnWidth(const CellRange &range, double width)
0720 {
0721     if (Worksheet *sheet = currentWorksheet())
0722         return sheet->setColumnWidth(range, width);
0723     return false;
0724 }
0725 
0726 /*!
0727   Sets format property of columns with the gien \a range and \a format.
0728   Returns true on success.
0729  */
0730 bool Document::setColumnFormat(const CellRange &range, const Format &format)
0731 {
0732     if (Worksheet *sheet = currentWorksheet())
0733         return sheet->setColumnFormat(range, format);
0734     return false;
0735 }
0736 
0737 /*!
0738   Sets hidden property of columns \a range to \a hidden. Columns are 1-indexed.
0739   Hidden columns are not visible.
0740   Returns true on success.
0741  */
0742 bool Document::setColumnHidden(const CellRange &range, bool hidden)
0743 {
0744     if (Worksheet *sheet = currentWorksheet())
0745         return sheet->setColumnWidth(range, hidden);
0746     return false;
0747 }
0748 
0749 /*!
0750   Sets width in characters \a column to \a width. Columns are 1-indexed.
0751   Returns true on success.
0752  */
0753 bool Document::setColumnWidth(int column, double width)
0754 {
0755     return setColumnWidth(column,column,width);
0756 }
0757 
0758 /*!
0759   Sets format property \a column to \a format. Columns are 1-indexed.
0760   Returns true on success.
0761  */
0762 bool Document::setColumnFormat(int column, const Format &format)
0763 {
0764     return setColumnFormat(column,column,format);
0765 }
0766 
0767 /*!
0768   Sets hidden property of a \a column. Columns are 1-indexed.
0769   Returns true on success.
0770  */
0771 bool Document::setColumnHidden(int column, bool hidden)
0772 {
0773     return setColumnHidden(column,column,hidden);
0774 }
0775 
0776 /*!
0777   Sets width in characters for columns [\a colFirst, \a colLast]. Columns are 1-indexed.
0778   Returns true on success.
0779  */
0780 bool Document::setColumnWidth(int colFirst, int colLast, double width)
0781 {
0782     if (Worksheet *sheet = currentWorksheet())
0783         return sheet->setColumnWidth(colFirst, colLast, width);
0784     return false;
0785 }
0786 
0787 /*!
0788   Sets format property of columns [\a colFirst, \a colLast] to \a format.
0789   Columns are 1-indexed.
0790   Returns true on success.
0791  */
0792 bool Document::setColumnFormat(int colFirst, int colLast, const Format &format)
0793 {
0794     if (Worksheet *sheet = currentWorksheet())
0795         return sheet->setColumnFormat(colFirst, colLast, format);
0796     return false;
0797 }
0798 
0799 
0800 /*!
0801   Sets hidden property of columns [\a colFirst, \a colLast] to \a hidden.
0802   Columns are 1-indexed.
0803   Returns true on success.
0804  */
0805 bool Document::setColumnHidden(int colFirst, int colLast, bool hidden)
0806 {
0807     if (Worksheet *sheet = currentWorksheet())
0808         return sheet->setColumnHidden(colFirst, colLast, hidden);
0809     return false;
0810 }
0811 
0812 /*!
0813   Returns width of the \a column in characters of the normal font.
0814   Columns are 1-indexed.
0815   Returns true on success.
0816  */
0817 double Document::columnWidth(int column)
0818 {
0819     if (Worksheet *sheet = currentWorksheet())
0820       return sheet->columnWidth(column);
0821     return 0.0;
0822 }
0823 
0824 /*!
0825   Returns formatting of the \a column. Columns are 1-indexed.
0826  */
0827 Format Document::columnFormat(int column)
0828 {
0829     if (Worksheet *sheet = currentWorksheet())
0830        return sheet->columnFormat(column);
0831     return Format();
0832 }
0833 
0834 /*!
0835   Returns true if \a column is hidden. Columns are 1-indexed.
0836  */
0837 bool Document::isColumnHidden(int column)
0838 {
0839     if (Worksheet *sheet = currentWorksheet())
0840        return sheet->isColumnHidden(column);
0841     return false;
0842 }
0843 
0844 /*!
0845   Sets the \a format of the \a row.
0846   Rows are 1-indexed.
0847 
0848   Returns true if success.
0849 */
0850 bool Document::setRowFormat(int row, const Format &format)
0851 {
0852     return setRowFormat(row,row, format);
0853 }
0854 
0855 /*!
0856   Sets the \a format of the rows including and between \a rowFirst and \a rowLast.
0857   Rows are 1-indexed.
0858 
0859   Returns true if success.
0860 */
0861 bool Document::setRowFormat(int rowFirst, int rowLast, const Format &format)
0862 {
0863     if (Worksheet *sheet = currentWorksheet())
0864        return sheet->setRowFormat(rowFirst, rowLast, format);
0865     return false;
0866 }
0867 
0868 /*!
0869   Sets the \a hidden property of the row \a row.
0870   Rows are 1-indexed. If hidden is true rows will not be visible.
0871 
0872   Returns true if success.
0873 */
0874 bool Document::setRowHidden(int row, bool hidden)
0875 {
0876     return setRowHidden(row,row,hidden);
0877 }
0878 
0879 /*!
0880   Sets the \a hidden property of the rows including and between \a rowFirst and \a rowLast.
0881   Rows are 1-indexed. If hidden is true rows will not be visible.
0882 
0883   Returns true if success.
0884 */
0885 bool Document::setRowHidden(int rowFirst, int rowLast, bool hidden)
0886 {
0887     if (Worksheet *sheet = currentWorksheet())
0888        return sheet->setRowHidden(rowFirst, rowLast, hidden);
0889     return false;
0890 }
0891 
0892 /*!
0893   Sets the \a height of the row \a row.
0894   Row height measured in point size.
0895   Rows are 1-indexed.
0896 
0897   Returns true if success.
0898 */
0899 bool Document::setRowHeight(int row, double height)
0900 {
0901     return setRowHeight(row,row,height);
0902 }
0903 
0904 /*!
0905   Sets the \a height of the rows including and between \a rowFirst and \a rowLast.
0906   Row height measured in point size.
0907   Rows are 1-indexed.
0908 
0909   Returns true if success.
0910 */
0911 bool Document::setRowHeight(int rowFirst, int rowLast, double height)
0912 {
0913     if (Worksheet *sheet = currentWorksheet())
0914        return sheet->setRowHeight(rowFirst, rowLast, height);
0915     return false;
0916 }
0917 
0918 /*!
0919  Returns height of \a row in points.
0920 */
0921 double Document::rowHeight(int row)
0922 {
0923    if (Worksheet *sheet = currentWorksheet())
0924       return sheet->rowHeight(row);
0925     return 0.0;
0926 }
0927 
0928 /*!
0929  Returns format of \a row.
0930 */
0931 Format Document::rowFormat(int row)
0932 {
0933     if (Worksheet *sheet = currentWorksheet())
0934        return sheet->rowFormat(row);
0935      return Format();
0936 }
0937 
0938 /*!
0939  Returns true if \a row is hidden.
0940 */
0941 bool Document::isRowHidden(int row)
0942 {
0943     if (Worksheet *sheet = currentWorksheet())
0944        return sheet->isRowHidden(row);
0945      return false;
0946 }
0947 
0948 /*!
0949    Groups rows from \a rowFirst to \a rowLast with the given \a collapsed.
0950    Returns false if error occurs.
0951  */
0952 bool Document::groupRows(int rowFirst, int rowLast, bool collapsed)
0953 {
0954     if (Worksheet *sheet = currentWorksheet())
0955         return sheet->groupRows(rowFirst, rowLast, collapsed);
0956     return false;
0957 }
0958 
0959 /*!
0960    Groups columns from \a colFirst to \a colLast with the given \a collapsed.
0961    Returns false if error occurs.
0962  */
0963 bool Document::groupColumns(int colFirst, int colLast, bool collapsed)
0964 {
0965     if (Worksheet *sheet = currentWorksheet())
0966         return sheet->groupColumns(colFirst, colLast, collapsed);
0967     return false;
0968 }
0969 
0970 /*!
0971  *  Add a data \a validation rule for current worksheet. Returns true if successful.
0972  */
0973 bool Document::addDataValidation(const DataValidation &validation)
0974 {
0975     if (Worksheet *sheet = currentWorksheet())
0976         return sheet->addDataValidation(validation);
0977     return false;
0978 }
0979 
0980 /*!
0981  *  Add a  conditional formatting \a cf for current worksheet. Returns true if successful.
0982  */
0983 bool Document::addConditionalFormatting(const ConditionalFormatting &cf)
0984 {
0985     if (Worksheet *sheet = currentWorksheet())
0986         return sheet->addConditionalFormatting(cf);
0987     return false;
0988 }
0989 
0990 /*!
0991  * \overload
0992  * Returns the cell at the position \a pos. If there is no cell at
0993  * the specified position, the function returns 0.
0994  *
0995  * \sa read()
0996  */
0997 Cell *Document::cellAt(const CellReference &pos) const
0998 {
0999     if (Worksheet *sheet = currentWorksheet())
1000         return sheet->cellAt(pos);
1001     return nullptr;
1002 }
1003 
1004 /*!
1005  * Returns the cell at the given \a row and \a col. If there
1006  * is no cell at the specified position, the function returns 0.
1007  *
1008  * \sa read()
1009  */
1010 Cell *Document::cellAt(int row, int col) const
1011 {
1012     if (Worksheet *sheet = currentWorksheet())
1013         return sheet->cellAt(row, col);
1014     return nullptr;
1015 }
1016 
1017 /*!
1018  * \brief Create a defined name in the workbook with the given \a name, \a formula, \a comment
1019  *  and \a scope.
1020  *
1021  * \param name The defined name.
1022  * \param formula The cell or range that the defined name refers to.
1023  * \param scope The name of one worksheet, or empty which means golbal scope.
1024  * \return Return false if the name invalid.
1025  */
1026 bool Document::defineName(const QString &name, const QString &formula, const QString &comment, const QString &scope)
1027 {
1028     Q_D(Document);
1029 
1030     return d->workbook->defineName(name, formula, comment, scope);
1031 }
1032 
1033 /*!
1034     Return the range that contains cell data.
1035  */
1036 CellRange Document::dimension() const
1037 {
1038     if (Worksheet *sheet = currentWorksheet())
1039         return sheet->dimension();
1040     return CellRange();
1041 }
1042 
1043 /*!
1044  * Returns the value of the document's \a key property.
1045  */
1046 QString Document::documentProperty(const QString &key) const
1047 {
1048     Q_D(const Document);
1049     auto it = d->documentProperties.constFind(key);
1050     if (it != d->documentProperties.constEnd())
1051         return it.value();
1052     else
1053         return QString();
1054 }
1055 
1056 /*!
1057     Set the document properties such as Title, Author etc.
1058 
1059     The method can be used to set the document properties of the Excel
1060     file created by Qt Xlsx. These properties are visible when you use the
1061     Office Button -> Prepare -> Properties option in Excel and are also
1062     available to external applications that read or index windows files.
1063 
1064     The \a property \a key that can be set are:
1065 
1066     \list
1067     \li title
1068     \li subject
1069     \li creator
1070     \li manager
1071     \li company
1072     \li category
1073     \li keywords
1074     \li description
1075     \li status
1076     \endlist
1077 */
1078 void Document::setDocumentProperty(const QString &key, const QString &property)
1079 {
1080     Q_D(Document);
1081     d->documentProperties[key] = property;
1082 }
1083 
1084 /*!
1085  * Returns the names of all properties that were addedusing setDocumentProperty().
1086  */
1087 QStringList Document::documentPropertyNames() const
1088 {
1089     Q_D(const Document);
1090     return d->documentProperties.keys();
1091 }
1092 
1093 /*!
1094  * Return the internal Workbook object.
1095  */
1096 Workbook *Document::workbook() const
1097 {
1098     Q_D(const Document);
1099     return d->workbook.data();
1100 }
1101 
1102 /*!
1103  * Returns the sheet object named \a sheetName.
1104  */
1105 AbstractSheet *Document::sheet(const QString &sheetName) const
1106 {
1107     Q_D(const Document);
1108     return d->workbook->sheet(sheetNames().indexOf(sheetName));
1109 }
1110 
1111 /*!
1112  * Creates and append an sheet with the given \a name and \a type.
1113  * Return true if success.
1114  */
1115 bool Document::addSheet(const QString &name, AbstractSheet::SheetType type)
1116 {
1117     Q_D(Document);
1118     return d->workbook->addSheet(name, type);
1119 }
1120 
1121 /*!
1122  * Creates and inserts an document with the given \a name and \a type at the \a index.
1123  * Returns false if the \a name already used.
1124  */
1125 bool Document::insertSheet(int index, const QString &name, AbstractSheet::SheetType type)
1126 {
1127     Q_D(Document);
1128     return d->workbook->insertSheet(index, name, type);
1129 }
1130 
1131 /*!
1132    Rename the worksheet from \a oldName to \a newName.
1133    Returns true if the success.
1134  */
1135 bool Document::renameSheet(const QString &oldName, const QString &newName)
1136 {
1137     Q_D(Document);
1138     if (oldName == newName)
1139         return false;
1140     return d->workbook->renameSheet(sheetNames().indexOf(oldName), newName);
1141 }
1142 
1143 /*!
1144    Make a copy of the worksheet \a srcName with the new name \a distName.
1145    Returns true if the success.
1146  */
1147 bool Document::copySheet(const QString &srcName, const QString &distName)
1148 {
1149     Q_D(Document);
1150     if (srcName == distName)
1151         return false;
1152     return d->workbook->copySheet(sheetNames().indexOf(srcName), distName);
1153 }
1154 
1155 /*!
1156    Move the worksheet \a srcName to the new pos \a distIndex.
1157    Returns true if the success.
1158  */
1159 bool Document::moveSheet(const QString &srcName, int distIndex)
1160 {
1161     Q_D(Document);
1162     return d->workbook->moveSheet(sheetNames().indexOf(srcName), distIndex);
1163 }
1164 
1165 /*!
1166    Delete the worksheet \a name.
1167    Returns true if current sheet was deleted successfully.
1168  */
1169 bool Document::deleteSheet(const QString &name)
1170 {
1171     Q_D(Document);
1172     return d->workbook->deleteSheet(sheetNames().indexOf(name));
1173 }
1174 
1175 /*!
1176  * \brief Return pointer of current sheet.
1177  */
1178 AbstractSheet *Document::currentSheet() const
1179 {
1180     Q_D(const Document);
1181 
1182     return d->workbook->activeSheet();
1183 }
1184 
1185 /*!
1186  * \brief Return pointer of current worksheet.
1187  * If the type of sheet is not AbstractSheet::ST_WorkSheet, then 0 will be returned.
1188  */
1189 Worksheet *Document::currentWorksheet() const
1190 {
1191     AbstractSheet *st = currentSheet();
1192     if (st && st->sheetType() == AbstractSheet::ST_WorkSheet)
1193         return static_cast<Worksheet *>(st);
1194     else
1195         return nullptr;
1196 }
1197 
1198 /*!
1199  * \brief Set worksheet named \a name to be active sheet.
1200  * Returns true if success.
1201  */
1202 bool Document::selectSheet(const QString &name)
1203 {
1204     Q_D(Document);
1205     return d->workbook->setActiveSheet(sheetNames().indexOf(name));
1206 }
1207 
1208 /*!
1209  * \brief Set worksheet whose index is \a index to be active sheet.
1210  * Returns true if success.
1211  */
1212 bool Document::selectSheet(int index)
1213 {
1214     Q_D(Document);
1215     return d->workbook->setActiveSheet(index);
1216 }
1217 
1218 /*!
1219  * Returns the names of worksheets contained in current document.
1220  */
1221 QStringList Document::sheetNames() const
1222 {
1223     Q_D(const Document);
1224     return d->workbook->worksheetNames();
1225 }
1226 
1227 /*!
1228  * Save current document to the filesystem. If no name specified when
1229  * the document constructed, a default name "book1.xlsx" will be used.
1230  * Returns true if saved successfully.
1231  */
1232 bool Document::save() const
1233 {
1234     Q_D(const Document);
1235     QString name = d->packageName.isEmpty() ? d->defaultPackageName : d->packageName;
1236 
1237     return saveAs(name);
1238 }
1239 
1240 /*!
1241  * Saves the document to the file with the given \a name.
1242  * Returns true if saved successfully.
1243  */
1244 bool Document::saveAs(const QString &name) const
1245 {
1246     QFile file(name);
1247     if (file.open(QIODevice::WriteOnly))
1248         return saveAs(&file);
1249     return false;
1250 }
1251 
1252 /*!
1253  * \overload
1254  * This function writes a document to the given \a device.
1255  *
1256  * \warning The \a device will be closed when this function returned.
1257  */
1258 bool Document::saveAs(QIODevice *device) const
1259 {
1260     Q_D(const Document);
1261     return d->savePackage(device);
1262 }
1263 
1264 bool Document::isLoadPackage() const
1265 {
1266     Q_D(const Document);
1267     return d->isLoad; 
1268 }
1269 
1270 bool Document::load() const
1271 {
1272     return isLoadPackage();
1273 }
1274 
1275 bool Document::copyStyle(const QString &from, const QString &to) {
1276     return DocumentPrivate::copyStyle(from, to);
1277 }
1278 
1279 /*!
1280  * Destroys the document and cleans up.
1281  */
1282 Document::~Document()
1283 {
1284     delete d_ptr;
1285 }
1286 
1287 //  add by liufeijin 20181025 {{
1288 bool Document::changeimage(int filenoinmidea, QString newfile)
1289 {
1290     Q_D(const Document);
1291 
1292     QImage newpic(newfile);
1293     
1294     auto mediaFileToLoad = d->workbook->mediaFiles();
1295     const auto mf = mediaFileToLoad[filenoinmidea];
1296     
1297     const QString suffix = newfile.mid(newfile.lastIndexOf(QLatin1Char('.'))+1);
1298     QString mimetypemy;
1299     if(QString::compare(QLatin1String("jpg"), suffix, Qt::CaseInsensitive)==0)
1300        mimetypemy=QStringLiteral("image/jpeg");
1301     if(QString::compare(QLatin1String("bmp"), suffix, Qt::CaseInsensitive)==0)
1302        mimetypemy=QStringLiteral("image/bmp");
1303     if(QString::compare(QLatin1String("gif"), suffix, Qt::CaseInsensitive)==0)
1304        mimetypemy=QStringLiteral("image/gif");
1305     if(QString::compare(QLatin1String("png"), suffix, Qt::CaseInsensitive)==0)
1306        mimetypemy=QStringLiteral("image/png");
1307     
1308     QByteArray ba;
1309     QBuffer buffer(&ba);
1310     buffer.setBuffer(&ba);
1311     buffer.open(QIODevice::WriteOnly);
1312     newpic.save(&buffer,suffix.toLocal8Bit().data());
1313     
1314     mf->set(ba,suffix,mimetypemy);
1315     mediaFileToLoad[filenoinmidea]=mf;
1316     
1317     return true;
1318 }
1319 // liufeijin }}
1320 
1321 
1322 /*!
1323   Returns map of columns with there maximal width
1324  */
1325 QMap<int, int> Document::getMaximalColumnWidth(int firstRow, int lastRow)
1326 {
1327     const int defaultPixelSize = 11;    //Default font pixel size of excel?
1328     int maxRows = -1;
1329     int maxCols = -1;
1330     QMap<int, int> colWidth;
1331     if (!currentWorksheet()) return colWidth;
1332     QVector<CellLocation> cellLocation = currentWorksheet()->getFullCells(&maxRows, &maxCols);
1333     
1334     for(int i=0; i < cellLocation.size(); i++)
1335     {
1336         int col = cellLocation.at(i).col;
1337         int row = cellLocation.at(i).row;
1338         int fs = cellLocation.at(i).cell->format().fontSize();
1339         if( fs <= 0)
1340         {
1341             fs = defaultPixelSize;
1342         }
1343 
1344 //        QString str = cellLocation.at(i).cell.data()->value().toString();
1345         QString str = read(row, col).toString();
1346 
1347         double w = str.length() * double(fs) / defaultPixelSize + 1; // width not perfect, but works reasonably well
1348 
1349         if( (row >= firstRow) && (row <= lastRow))
1350         {
1351             if( w > colWidth.value(col))
1352             {
1353                 colWidth.insert(col, int(w));
1354             }
1355         }
1356     }
1357 
1358     return colWidth;
1359 }
1360 
1361 
1362 /*!
1363   Auto ets width in characters of columns with the given \a range.
1364   Returns true on success.
1365  */
1366 bool Document::autosizeColumnWidth(const CellRange &range)
1367 {
1368     bool erg = false;
1369 
1370     if( !range.isValid())
1371     {
1372         return false;
1373     }
1374 
1375     const QMap<int, int> colWidth = getMaximalColumnWidth(range.firstRow(), range.lastRow());
1376     auto it = colWidth.constBegin();
1377     while (it != colWidth.constEnd()) {
1378         if( (it.key() >= range.firstColumn()) && (it.key() <= range.lastColumn()) )
1379         {
1380             erg |= setColumnWidth(it.key(), it.value());
1381         }
1382         ++it;
1383     }
1384 
1385     return erg;
1386 }
1387 
1388 
1389 /*!
1390   Auto sets width in characters \a column . Columns are 1-indexed.
1391   Returns true on success.
1392  */
1393 bool Document::autosizeColumnWidth(int column)
1394 {
1395     bool erg = false;
1396 
1397     const QMap<int, int> colWidth = getMaximalColumnWidth();
1398     auto it = colWidth.constBegin();
1399     while (it != colWidth.constEnd()) {
1400         if( it.key() == column)
1401         {
1402             erg |= setColumnWidth(it.key(), it.value());
1403         }
1404         ++it;
1405     }
1406 
1407     return erg;
1408 }
1409 
1410 
1411 /*!
1412   Auto sets width in characters for columns [\a colFirst, \a colLast]. Columns are 1-indexed.
1413   Returns true on success.
1414  */
1415 bool Document::autosizeColumnWidth(int colFirst, int colLast)
1416 {
1417     Q_UNUSED(colFirst)
1418     Q_UNUSED(colLast)
1419     bool erg = false;
1420 
1421     const QMap<int, int> colWidth = getMaximalColumnWidth();
1422     auto it = colWidth.constBegin();
1423     while (it != colWidth.constEnd()) {
1424         if( (it.key() >= colFirst) && (it.key() <= colLast) )
1425         {
1426             erg |= setColumnWidth(it.key(), it.value());
1427         }
1428         ++it;
1429     }
1430 
1431     return erg;
1432 }
1433 
1434 
1435 /*!
1436   Auto sets width in characters for all columns.
1437   Returns true on success.
1438  */
1439 bool Document::autosizeColumnWidth(void)
1440 {
1441     bool erg = false;
1442 
1443     const QMap<int, int> colWidth = getMaximalColumnWidth();
1444     auto it = colWidth.constBegin();
1445     while (it != colWidth.constEnd()) {
1446         erg |= setColumnWidth(it.key(), it.value());
1447         ++it;
1448     }
1449 
1450     return erg;
1451 }
1452 
1453 
1454 QT_END_NAMESPACE_XLSX