File indexing completed on 2025-03-23 03:32:49
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