1 // xlsxdocument.cpp
2
3 #include <QtGlobal>
4 #include <QFile>
5 #include <QPointF>
6 #include <QBuffer>
7 #include <QDir>
8 #include <QTemporaryFile>
9 #include <QFile>
10 #include <QSharedPointer>
11 #include <QDebug>
12
13 #include "xlsxdocument.h"
14 #include "xlsxdocument_p.h"
15 #include "xlsxworkbook.h"
16 #include "xlsxworksheet.h"
17 #include "xlsxcontenttypes_p.h"
18 #include "xlsxrelationships_p.h"
19 #include "xlsxstyles_p.h"
20 #include "xlsxtheme_p.h"
21 #include "xlsxdocpropsapp_p.h"
22 #include "xlsxdocpropscore_p.h"
23 #include "xlsxsharedstrings_p.h"
24 #include "xlsxutility_p.h"
25 #include "xlsxworkbook_p.h"
26 #include "xlsxdrawing_p.h"
27 #include "xlsxmediafile_p.h"
28 #include "xlsxchart.h"
29 #include "xlsxzipreader_p.h"
30 #include "xlsxzipwriter_p.h"
31
32 /*
33 From Wikipedia: The Open Packaging Conventions (OPC) is a
34 container-file technology initially created by Microsoft to store
35 a combination of XML and non-XML files that together form a single
36 entity such as an Open XML Paper Specification (OpenXPS)
37 document. http://en.wikipedia.org/wiki/Open_Packaging_Conventions.
38
39 At its simplest an Excel XLSX file contains the following elements:
40
41 ____ [Content_Types].xml
42 |
43 |____ docProps
44 | |____ app.xml
45 | |____ core.xml
46 |
47 |____ xl
48 | |____ workbook.xml
49 | |____ worksheets
50 | | |____ sheet1.xml
51 | |
52 | |____ styles.xml
53 | |
54 | |____ theme
55 | | |____ theme1.xml
56 | |
57 | |_____rels
58 | |____ workbook.xml.rels
59 |
60 |_____rels
61 |____ .rels
62
63 The Packager class coordinates the classes that represent the
64 elements of the package and writes them into the XLSX file.
65 */
66
67 QT_BEGIN_NAMESPACE_XLSX
68
69 namespace xlsxDocumentCpp {
copyTag(const std::string & sFrom,const std::string & sTo,const std::string & tag)70 std::string copyTag(const std::string &sFrom, const std::string &sTo, const std::string &tag) {
71 const std::string tagToFindStart = "<" + tag;
72 const std::string tagToFindEnd = "</" + tag;
73 const std::string tagEnd = "</" + tag + ">";
74
75 // search all occurrences of tag in 'sFrom'
76 std::string sFromData = "";
77 size_t startIndex = 0;
78 while (true) {
79 std::size_t startPos = sFrom.find(tagToFindStart, startIndex);
80 if (startPos != std::string::npos) {
81 std::size_t endPos = sFrom.find(tagToFindEnd, startPos);
82 std::string tagEndTmp = tagEnd;
83 if (endPos == std::string::npos) { // second try to find the ending, maybe it is "/>"
84 endPos = sFrom.find("/>", startPos);
85 tagEndTmp = "/>";
86 }
87 if (endPos != std::string::npos) {
88 sFromData += sFrom.substr(startPos, endPos - startPos) + tagEndTmp;
89 startIndex = endPos + strlen(tagEndTmp.c_str());
90 }
91 else {
92 break;
93 }
94 }
95 else {
96 break;
97 }
98 }
99
100 std::string sOut = sTo; // copy 'sTo' in the output string
101
102 if (!sFromData.empty()) { // tag found in 'from'?
103 // search all occurrences of tag in 'sOut' and delete them
104 int firstPosTag = -1;
105 while (true) {
106 std::size_t startPos = sOut.find(tagToFindStart);
107 if (startPos != std::string::npos) {
108 std::size_t endPos = sOut.find(tagToFindEnd);
109 std::string tagEndTmp = tagEnd;
110 if (endPos == std::string::npos) { // second try to find the ending, maybe it is "/>"
111 endPos = sOut.find("/>", startPos);
112 tagEndTmp = "/>";
113 }
114 if (endPos != std::string::npos) {
115 if (firstPosTag < 0)
116 firstPosTag = startPos;
117 std::string stringBefore = sOut.substr(0, startPos);
118 endPos += strlen(tagEndTmp.c_str());
119 std::string stringAfter = sOut.substr(endPos, strlen(sOut.c_str()) - endPos);
120 sOut = stringBefore + stringAfter;
121 }
122 else {
123 break;
124 }
125 }
126 else {
127 break;
128 }
129 }
130
131 if (firstPosTag == -1) {
132 // tag not found in 'sTo' file
133 // try to find a default pos using standard tags
134 std::vector<std::string> defaultPos{ "</styleSheet>", "<pageMargins", "</workbook>" };
135 for (unsigned int i = 0; i < defaultPos.size(); ++i) {
136 std::size_t iDefaultPos = sOut.find(defaultPos[i]);
137 if (iDefaultPos != std::string::npos) {
138 firstPosTag = iDefaultPos;
139 break;
140 }
141 }
142 }
143
144 // add the tag extracted from 'sFrom' in 'sOut'
145 // add in the position of the first tag found in 'sOut' ('firstPosTag')
146 if (firstPosTag >= 0) {
147 std::string stringBefore = sOut.substr(0, firstPosTag);
148 std::string stringAfter = sOut.substr(firstPosTag, strlen(sOut.c_str()) - firstPosTag);
149 sOut = stringBefore + sFromData + stringAfter;
150 }
151 }
152
153 return sOut;
154 }
155 }
156
DocumentPrivate(Document * p)157 DocumentPrivate::DocumentPrivate(Document *p) :
158 q_ptr(p), defaultPackageName(QStringLiteral("Book1.xlsx")),
159 isLoad(false)
160 {
161 }
162
init()163 void DocumentPrivate::init()
164 {
165 if (contentTypes.isNull())
166 contentTypes = QSharedPointer<ContentTypes>(new ContentTypes(ContentTypes::F_NewFromScratch));
167
168 if (workbook.isNull())
169 workbook = QSharedPointer<Workbook>(new Workbook(Workbook::F_NewFromScratch));
170 }
171
loadPackage(QIODevice * device)172 bool DocumentPrivate::loadPackage(QIODevice *device)
173 {
174 Q_Q(Document);
175 ZipReader zipReader(device);
176 QStringList filePaths = zipReader.filePaths();
177
178 //Load the Content_Types file
179 if (!filePaths.contains(QLatin1String("[Content_Types].xml")))
180 return false;
181 contentTypes = QSharedPointer<ContentTypes>(new ContentTypes(ContentTypes::F_LoadFromExists));
182 contentTypes->loadFromXmlData(zipReader.fileData(QStringLiteral("[Content_Types].xml")));
183
184 //Load root rels file
185 if (!filePaths.contains(QLatin1String("_rels/.rels")))
186 return false;
187 Relationships rootRels;
188 rootRels.loadFromXmlData(zipReader.fileData(QStringLiteral("_rels/.rels")));
189
190 //load core property
191 QList<XlsxRelationship> rels_core = rootRels.packageRelationships(QStringLiteral("/metadata/core-properties"));
192 if (!rels_core.isEmpty()) {
193 //Get the core property file name if it exists.
194 //In normal case, this should be "docProps/core.xml"
195 QString docPropsCore_Name = rels_core[0].target;
196
197 DocPropsCore props(DocPropsCore::F_LoadFromExists);
198 props.loadFromXmlData(zipReader.fileData(docPropsCore_Name));
199 const auto propNames = props.propertyNames();
200 for (const QString &name : propNames)
201 q->setDocumentProperty(name, props.property(name));
202 }
203
204 //load app property
205 QList<XlsxRelationship> rels_app = rootRels.documentRelationships(QStringLiteral("/extended-properties"));
206 if (!rels_app.isEmpty()) {
207 //Get the app property file name if it exists.
208 //In normal case, this should be "docProps/app.xml"
209 QString docPropsApp_Name = rels_app[0].target;
210
211 DocPropsApp props(DocPropsApp::F_LoadFromExists);
212 props.loadFromXmlData(zipReader.fileData(docPropsApp_Name));
213 const auto propNames = props.propertyNames();
214 for (const QString &name : propNames)
215 q->setDocumentProperty(name, props.property(name));
216 }
217
218 //load workbook now, Get the workbook file path from the root rels file
219 //In normal case, this should be "xl/workbook.xml"
220 workbook = QSharedPointer<Workbook>(new Workbook(Workbook::F_LoadFromExists));
221 QList<XlsxRelationship> rels_xl = rootRels.documentRelationships(QStringLiteral("/officeDocument"));
222 if (rels_xl.isEmpty())
223 return false;
224 const QString xlworkbook_Path = rels_xl[0].target;
225 const QString xlworkbook_Dir = *( splitPath(xlworkbook_Path).begin() );
226 const QString relFilePath = getRelFilePath(xlworkbook_Path);
227
228 workbook->relationships()->loadFromXmlData( zipReader.fileData(relFilePath) );
229 workbook->setFilePath(xlworkbook_Path);
230 workbook->loadFromXmlData(zipReader.fileData(xlworkbook_Path));
231
232 //load styles
233 QList<XlsxRelationship> rels_styles = workbook->relationships()->documentRelationships(QStringLiteral("/styles"));
234 if (!rels_styles.isEmpty()) {
235 //In normal case this should be styles.xml which in xl
236 QString name = rels_styles[0].target;
237
238 // dev34
239 QString path;
240 if ( xlworkbook_Dir == QLatin1String(".") ) // root
241 {
242 path = name;
243 }
244 else
245 {
246 path = xlworkbook_Dir + QLatin1String("/") + name;
247 }
248
249 QSharedPointer<Styles> styles (new Styles(Styles::F_LoadFromExists));
250 styles->loadFromXmlData(zipReader.fileData(path));
251 workbook->d_func()->styles = styles;
252 }
253
254 //load sharedStrings
255 QList<XlsxRelationship> rels_sharedStrings = workbook->relationships()->documentRelationships(QStringLiteral("/sharedStrings"));
256 if (!rels_sharedStrings.isEmpty()) {
257 //In normal case this should be sharedStrings.xml which in xl
258 QString name = rels_sharedStrings[0].target;
259 QString path = xlworkbook_Dir + QLatin1String("/") + name;
260 workbook->d_func()->sharedStrings->loadFromXmlData(zipReader.fileData(path));
261 }
262
263 //load theme
264 QList<XlsxRelationship> rels_theme = workbook->relationships()->documentRelationships(QStringLiteral("/theme"));
265 if (!rels_theme.isEmpty()) {
266 //In normal case this should be theme/theme1.xml which in xl
267 QString name = rels_theme[0].target;
268 QString path = xlworkbook_Dir + QLatin1String("/") + name;
269 workbook->theme()->loadFromXmlData(zipReader.fileData(path));
270 }
271
272 //load sheets
273 for (int i=0; i<workbook->sheetCount(); ++i) {
274 AbstractSheet *sheet = workbook->sheet(i);
275 QString strFilePath = sheet->filePath();
276 QString rel_path = getRelFilePath(strFilePath);
277 //If the .rel file exists, load it.
278 if (zipReader.filePaths().contains(rel_path))
279 sheet->relationships()->loadFromXmlData(zipReader.fileData(rel_path));
280 sheet->loadFromXmlData(zipReader.fileData(sheet->filePath()));
281 }
282
283 //load external links
284 for (int i=0; i<workbook->d_func()->externalLinks.count(); ++i) {
285 SimpleOOXmlFile *link = workbook->d_func()->externalLinks[i].data();
286 QString rel_path = getRelFilePath(link->filePath());
287 //If the .rel file exists, load it.
288 if (zipReader.filePaths().contains(rel_path))
289 link->relationships()->loadFromXmlData(zipReader.fileData(rel_path));
290 link->loadFromXmlData(zipReader.fileData(link->filePath()));
291 }
292
293 //load drawings
294 for (int i=0; i<workbook->drawings().size(); ++i) {
295 Drawing *drawing = workbook->drawings()[i];
296 QString rel_path = getRelFilePath(drawing->filePath());
297 if (zipReader.filePaths().contains(rel_path))
298 drawing->relationships()->loadFromXmlData(zipReader.fileData(rel_path));
299 drawing->loadFromXmlData(zipReader.fileData(drawing->filePath()));
300 }
301
302 //load charts
303 QList<QSharedPointer<Chart> > chartFileToLoad = workbook->chartFiles();
304 for (int i=0; i<chartFileToLoad.size(); ++i) {
305 QSharedPointer<Chart> cf = chartFileToLoad[i];
306 cf->loadFromXmlData(zipReader.fileData(cf->filePath()));
307 }
308
309 //load media files
310 QList<QSharedPointer<MediaFile> > mediaFileToLoad = workbook->mediaFiles();
311 for (int i=0; i<mediaFileToLoad.size(); ++i) {
312 QSharedPointer<MediaFile> mf = mediaFileToLoad[i];
313 const QString path = mf->fileName();
314 const QString suffix = path.mid(path.lastIndexOf(QLatin1Char('.'))+1);
315 mf->set(zipReader.fileData(path), suffix);
316 }
317
318 isLoad = true;
319 return true;
320 }
321
savePackage(QIODevice * device) const322 bool DocumentPrivate::savePackage(QIODevice *device) const
323 {
324 Q_Q(const Document);
325
326 ZipWriter zipWriter(device);
327 if (zipWriter.error())
328 return false;
329
330 contentTypes->clearOverrides();
331
332 DocPropsApp docPropsApp(DocPropsApp::F_NewFromScratch);
333 DocPropsCore docPropsCore(DocPropsCore::F_NewFromScratch);
334
335 // save worksheet xml files
336 QList<QSharedPointer<AbstractSheet> > worksheets = workbook->getSheetsByTypes(AbstractSheet::ST_WorkSheet);
337 if (!worksheets.isEmpty())
338 docPropsApp.addHeadingPair(QStringLiteral("Worksheets"), worksheets.size());
339
340 for (int i = 0 ; i < worksheets.size(); ++i)
341 {
342 QSharedPointer<AbstractSheet> sheet = worksheets[i];
343 contentTypes->addWorksheetName(QStringLiteral("sheet%1").arg(i+1));
344 docPropsApp.addPartTitle(sheet->sheetName());
345
346 zipWriter.addFile(QStringLiteral("xl/worksheets/sheet%1.xml").arg(i+1), sheet->saveToXmlData());
347
348 Relationships *rel = sheet->relationships();
349 if (!rel->isEmpty())
350 zipWriter.addFile(QStringLiteral("xl/worksheets/_rels/sheet%1.xml.rels").arg(i+1), rel->saveToXmlData());
351 }
352
353 //save chartsheet xml files
354 QList<QSharedPointer<AbstractSheet> > chartsheets = workbook->getSheetsByTypes(AbstractSheet::ST_ChartSheet);
355 if (!chartsheets.isEmpty())
356 docPropsApp.addHeadingPair(QStringLiteral("Chartsheets"), chartsheets.size());
357 for (int i=0; i<chartsheets.size(); ++i)
358 {
359 QSharedPointer<AbstractSheet> sheet = chartsheets[i];
360 contentTypes->addWorksheetName(QStringLiteral("sheet%1").arg(i+1));
361 docPropsApp.addPartTitle(sheet->sheetName());
362
363 zipWriter.addFile(QStringLiteral("xl/chartsheets/sheet%1.xml").arg(i+1), sheet->saveToXmlData());
364 Relationships *rel = sheet->relationships();
365 if (!rel->isEmpty())
366 zipWriter.addFile(QStringLiteral("xl/chartsheets/_rels/sheet%1.xml.rels").arg(i+1), rel->saveToXmlData());
367 }
368
369 // save external links xml files
370 for (int i=0; i<workbook->d_func()->externalLinks.count(); ++i)
371 {
372 SimpleOOXmlFile *link = workbook->d_func()->externalLinks[i].data();
373 contentTypes->addExternalLinkName(QStringLiteral("externalLink%1").arg(i+1));
374
375 zipWriter.addFile(QStringLiteral("xl/externalLinks/externalLink%1.xml").arg(i+1), link->saveToXmlData());
376 Relationships *rel = link->relationships();
377 if (!rel->isEmpty())
378 zipWriter.addFile(QStringLiteral("xl/externalLinks/_rels/externalLink%1.xml.rels").arg(i+1), rel->saveToXmlData());
379 }
380
381 // save workbook xml file
382 contentTypes->addWorkbook();
383 zipWriter.addFile(QStringLiteral("xl/workbook.xml"), workbook->saveToXmlData());
384 zipWriter.addFile(QStringLiteral("xl/_rels/workbook.xml.rels"), workbook->relationships()->saveToXmlData());
385
386 // save drawing xml files
387 for (int i=0; i<workbook->drawings().size(); ++i)
388 {
389 contentTypes->addDrawingName(QStringLiteral("drawing%1").arg(i+1));
390
391 Drawing *drawing = workbook->drawings()[i];
392 zipWriter.addFile(QStringLiteral("xl/drawings/drawing%1.xml").arg(i+1), drawing->saveToXmlData());
393 if (!drawing->relationships()->isEmpty())
394 zipWriter.addFile(QStringLiteral("xl/drawings/_rels/drawing%1.xml.rels").arg(i+1), drawing->relationships()->saveToXmlData());
395 }
396
397 // save docProps app/core xml file
398 const auto docPropNames = q->documentPropertyNames();
399 for (const QString &name : docPropNames) {
400 docPropsApp.setProperty(name, q->documentProperty(name));
401 docPropsCore.setProperty(name, q->documentProperty(name));
402 }
403 contentTypes->addDocPropApp();
404 contentTypes->addDocPropCore();
405 zipWriter.addFile(QStringLiteral("docProps/app.xml"), docPropsApp.saveToXmlData());
406 zipWriter.addFile(QStringLiteral("docProps/core.xml"), docPropsCore.saveToXmlData());
407
408 // save sharedStrings xml file
409 if (!workbook->sharedStrings()->isEmpty()) {
410 contentTypes->addSharedString();
411 zipWriter.addFile(QStringLiteral("xl/sharedStrings.xml"), workbook->sharedStrings()->saveToXmlData());
412 }
413
414 // save calc chain [dev16]
415 contentTypes->addCalcChain();
416 zipWriter.addFile(QStringLiteral("xl/calcChain.xml"), workbook->styles()->saveToXmlData());
417
418 // save styles xml file
419 contentTypes->addStyles();
420 zipWriter.addFile(QStringLiteral("xl/styles.xml"), workbook->styles()->saveToXmlData());
421
422 // save theme xml file
423 contentTypes->addTheme();
424 zipWriter.addFile(QStringLiteral("xl/theme/theme1.xml"), workbook->theme()->saveToXmlData());
425
426 // save chart xml files
427 for (int i=0; i<workbook->chartFiles().size(); ++i)
428 {
429 contentTypes->addChartName(QStringLiteral("chart%1").arg(i+1));
430 QSharedPointer<Chart> cf = workbook->chartFiles()[i];
431 zipWriter.addFile(QStringLiteral("xl/charts/chart%1.xml").arg(i+1), cf->saveToXmlData());
432 }
433
434 // save image files
435 for (int i=0; i<workbook->mediaFiles().size(); ++i)
436 {
437 QSharedPointer<MediaFile> mf = workbook->mediaFiles()[i];
438 if (!mf->mimeType().isEmpty())
439 contentTypes->addDefault(mf->suffix(), mf->mimeType());
440
441 zipWriter.addFile(QStringLiteral("xl/media/image%1.%2").arg(i+1).arg(mf->suffix()), mf->contents());
442 }
443
444 // save root .rels xml file
445 Relationships rootrels;
446 rootrels.addDocumentRelationship(QStringLiteral("/officeDocument"), QStringLiteral("xl/workbook.xml"));
447 rootrels.addPackageRelationship(QStringLiteral("/metadata/core-properties"), QStringLiteral("docProps/core.xml"));
448 rootrels.addDocumentRelationship(QStringLiteral("/extended-properties"), QStringLiteral("docProps/app.xml"));
449 zipWriter.addFile(QStringLiteral("_rels/.rels"), rootrels.saveToXmlData());
450
451 // save content types xml file
452 zipWriter.addFile(QStringLiteral("[Content_Types].xml"), contentTypes->saveToXmlData());
453
454 zipWriter.close();
455 return true;
456 }
457
copyStyle(const QString & from,const QString & to)458 bool DocumentPrivate::copyStyle(const QString &from, const QString &to)
459 {
460 // create a temp file because the zip writer cannot modify already existing zips
461 QTemporaryFile tempFile;
462 tempFile.open();
463 tempFile.close();
464 QString temFilePath = QFileInfo(tempFile).absoluteFilePath();
465
466 ZipWriter temporalZip(temFilePath);
467
468 ZipReader zipReader(from);
469 QStringList filePaths = zipReader.filePaths();
470
471 QSharedPointer<ZipReader> toReader = QSharedPointer<ZipReader>(new ZipReader(to));
472
473 QStringList toFilePaths = toReader->filePaths();
474
475 // copy all files from "to" zip except those related to style
476 for (int i = 0; i < toFilePaths.size(); i++) {
477 if (toFilePaths[i].contains(QLatin1String("xl/styles"))) {
478 if (filePaths.contains(toFilePaths[i])) { // style file exist in 'from' as well
479 // modify style file
480 std::string fromData = QString::fromUtf8(zipReader.fileData(toFilePaths[i])).toStdString();
481 std::string toData = QString::fromUtf8(toReader->fileData(toFilePaths[i])).toStdString();
482 // copy default theme style from 'from' to 'to'
483 toData = xlsxDocumentCpp::copyTag(fromData, toData, "dxfs");
484 temporalZip.addFile(toFilePaths.at(i), QString::fromUtf8(toData.c_str()).toUtf8());
485
486 continue;
487 }
488 }
489
490 if (toFilePaths[i].contains(QLatin1String("xl/workbook"))) {
491 if (filePaths.contains(toFilePaths[i])) { // workbook file exist in 'from' as well
492 // modify workbook file
493 std::string fromData = QString::fromUtf8(zipReader.fileData(toFilePaths[i])).toStdString();
494 std::string toData = QString::fromUtf8(toReader->fileData(toFilePaths[i])).toStdString();
495 // copy default theme style from 'from' to 'to'
496 toData = xlsxDocumentCpp::copyTag(fromData, toData, "workbookPr");
497 temporalZip.addFile(toFilePaths.at(i), QString::fromUtf8(toData.c_str()).toUtf8());
498 continue;
499 }
500 }
501
502 if (toFilePaths[i].contains(QLatin1String("xl/worksheets/sheet"))) {
503 if (filePaths.contains(toFilePaths[i])) { // sheet file exist in 'from' as well
504 // modify sheet file
505 std::string fromData = QString::fromUtf8(zipReader.fileData(toFilePaths[i])).toStdString();
506 std::string toData = QString::fromUtf8(toReader->fileData(toFilePaths[i])).toStdString();
507 // copy "conditionalFormatting" from 'from' to 'to'
508 toData = xlsxDocumentCpp::copyTag(fromData, toData, "conditionalFormatting");
509 temporalZip.addFile(toFilePaths.at(i), QString::fromUtf8(toData.c_str()).toUtf8());
510 continue;
511 }
512 }
513
514 QByteArray data = toReader->fileData(toFilePaths.at(i));
515 temporalZip.addFile(toFilePaths.at(i), data);
516 }
517
518 temporalZip.close();
519
520 toReader.clear();
521
522 tempFile.close();
523
524 QFile::remove(to);
525 tempFile.copy(to);
526
527 return true;
528 }
529
530 /*!
531 \class Document
532 \inmodule QtXlsx
533 \brief The Document class provides a API that is used to handle the contents of .xlsx files.
534
535 */
536
537 /*!
538 * Creates a new empty xlsx document.
539 * The \a parent argument is passed to QObject's constructor.
540 */
Document(QObject * parent)541 Document::Document(QObject *parent) :
542 QObject(parent), d_ptr(new DocumentPrivate(this))
543 {
544 d_ptr->init();
545 }
546
547 /*!
548 * \overload
549 * Try to open an existing xlsx document named \a name.
550 * The \a parent argument is passed to QObject's constructor.
551 */
Document(const QString & name,QObject * parent)552 Document::Document(const QString &name,
553 QObject *parent) :
554 QObject(parent),
555 d_ptr(new DocumentPrivate(this))
556 {
557 d_ptr->packageName = name;
558
559 if (QFile::exists(name))
560 {
561 QFile xlsx(name);
562 if (xlsx.open(QFile::ReadOnly))
563 {
564 if (! d_ptr->loadPackage(&xlsx))
565 {
566 // NOTICE: failed to load package
567 }
568 }
569 }
570
571 d_ptr->init();
572 }
573
574 /*!
575 * \overload
576 * Try to open an existing xlsx document from \a device.
577 * The \a parent argument is passed to QObject's constructor.
578 */
Document(QIODevice * device,QObject * parent)579 Document::Document(QIODevice *device, QObject *parent) :
580 QObject(parent), d_ptr(new DocumentPrivate(this))
581 {
582 if (device && device->isReadable())
583 {
584 if (!d_ptr->loadPackage(device))
585 {
586 // NOTICE: failed to load package
587 }
588 }
589 d_ptr->init();
590 }
591
592 /*!
593 \overload
594
595 Write \a value to cell \a row_column with the given \a format.
596 */
write(const CellReference & row_column,const QVariant & value,const Format & format)597 bool Document::write(const CellReference &row_column, const QVariant &value, const Format &format)
598 {
599 if (Worksheet *sheet = currentWorksheet())
600 return sheet->write(row_column, value, format);
601 return false;
602 }
603
604 /*!
605 * Write \a value to cell (\a row, \a col) with the \a format.
606 * Returns true on success.
607 */
write(int row,int col,const QVariant & value,const Format & format)608 bool Document::write(int row, int col, const QVariant &value, const Format &format)
609 {
610 if (Worksheet *sheet = currentWorksheet())
611 return sheet->write(row, col, value, format);
612 return false;
613 }
614
615 /*!
616 \overload
617 Returns the contents of the cell \a cell.
618
619 \sa cellAt()
620 */
read(const CellReference & cell) const621 QVariant Document::read(const CellReference &cell) const
622 {
623 if (Worksheet *sheet = currentWorksheet())
624 return sheet->read(cell);
625 return QVariant();
626 }
627
628 /*!
629 Returns the contents of the cell (\a row, \a col).
630
631 \sa cellAt()
632 */
read(int row,int col) const633 QVariant Document::read(int row, int col) const
634 {
635 if (Worksheet *sheet = currentWorksheet())
636 return sheet->read(row, col);
637 return QVariant();
638 }
639
640 /*!
641 * Insert an \a image to current active worksheet at the position \a row, \a column
642 * Returns ture if success.
643 */
insertImage(int row,int column,const QImage & image)644 int Document::insertImage(int row, int column, const QImage &image)
645 {
646 if (Worksheet *sheet = currentWorksheet())
647 return sheet->insertImage(row, column, image);
648
649 return 0;
650 }
651
getImage(int imageIndex,QImage & img)652 bool Document::getImage(int imageIndex, QImage& img)
653 {
654 if (Worksheet *sheet = currentWorksheet())
655 return sheet->getImage(imageIndex, img);
656
657 return false;
658 }
659
getImage(int row,int col,QImage & img)660 bool Document::getImage(int row, int col, QImage &img)
661 {
662 if (Worksheet *sheet = currentWorksheet())
663 return sheet->getImage(row, col, img);
664
665 return false;
666 }
667
getImageCount()668 uint Document::getImageCount()
669 {
670 if (Worksheet *sheet = currentWorksheet())
671 return sheet->getImageCount();
672
673 return 0;
674 }
675
676
677 /*!
678 * Creates an chart with the given \a size and insert it to the current
679 * active worksheet at the position \a row, \a col.
680 * The chart will be returned.
681 */
insertChart(int row,int col,const QSize & size)682 Chart *Document::insertChart(int row, int col, const QSize &size)
683 {
684 if (Worksheet *sheet = currentWorksheet())
685 return sheet->insertChart(row, col, size);
686 return 0;
687 }
688
689 /*!
690 Merge a \a range of cells. The first cell should contain the data and the others should
691 be blank. All cells will be applied the same style if a valid \a format is given.
692 Returns true on success.
693
694 \note All cells except the top-left one will be cleared.
695 */
mergeCells(const CellRange & range,const Format & format)696 bool Document::mergeCells(const CellRange &range, const Format &format)
697 {
698 if (Worksheet *sheet = currentWorksheet())
699 return sheet->mergeCells(range, format);
700 return false;
701 }
702
703 /*!
704 Unmerge the cells in the \a range.
705 Returns true on success.
706 */
unmergeCells(const CellRange & range)707 bool Document::unmergeCells(const CellRange &range)
708 {
709 if (Worksheet *sheet = currentWorksheet())
710 return sheet->unmergeCells(range);
711 return false;
712 }
713
714 /*!
715 Sets width in characters of columns with the given \a range and \a width.
716 Returns true on success.
717 */
setColumnWidth(const CellRange & range,double width)718 bool Document::setColumnWidth(const CellRange &range, double width)
719 {
720 if (Worksheet *sheet = currentWorksheet())
721 return sheet->setColumnWidth(range, width);
722 return false;
723 }
724
725 /*!
726 Sets format property of columns with the gien \a range and \a format.
727 Returns true on success.
728 */
setColumnFormat(const CellRange & range,const Format & format)729 bool Document::setColumnFormat(const CellRange &range, const Format &format)
730 {
731 if (Worksheet *sheet = currentWorksheet())
732 return sheet->setColumnFormat(range, format);
733 return false;
734 }
735
736 /*!
737 Sets hidden property of columns \a range to \a hidden. Columns are 1-indexed.
738 Hidden columns are not visible.
739 Returns true on success.
740 */
setColumnHidden(const CellRange & range,bool hidden)741 bool Document::setColumnHidden(const CellRange &range, bool hidden)
742 {
743 if (Worksheet *sheet = currentWorksheet())
744 return sheet->setColumnWidth(range, hidden);
745 return false;
746 }
747
748 /*!
749 Sets width in characters \a column to \a width. Columns are 1-indexed.
750 Returns true on success.
751 */
setColumnWidth(int column,double width)752 bool Document::setColumnWidth(int column, double width)
753 {
754 return setColumnWidth(column,column,width);
755 }
756
757 /*!
758 Sets format property \a column to \a format. Columns are 1-indexed.
759 Returns true on success.
760 */
setColumnFormat(int column,const Format & format)761 bool Document::setColumnFormat(int column, const Format &format)
762 {
763 return setColumnFormat(column,column,format);
764 }
765
766 /*!
767 Sets hidden property of a \a column. Columns are 1-indexed.
768 Returns true on success.
769 */
setColumnHidden(int column,bool hidden)770 bool Document::setColumnHidden(int column, bool hidden)
771 {
772 return setColumnHidden(column,column,hidden);
773 }
774
775 /*!
776 Sets width in characters for columns [\a colFirst, \a colLast]. Columns are 1-indexed.
777 Returns true on success.
778 */
setColumnWidth(int colFirst,int colLast,double width)779 bool Document::setColumnWidth(int colFirst, int colLast, double width)
780 {
781 if (Worksheet *sheet = currentWorksheet())
782 return sheet->setColumnWidth(colFirst, colLast, width);
783 return false;
784 }
785
786 /*!
787 Sets format property of columns [\a colFirst, \a colLast] to \a format.
788 Columns are 1-indexed.
789 Returns true on success.
790 */
setColumnFormat(int colFirst,int colLast,const Format & format)791 bool Document::setColumnFormat(int colFirst, int colLast, const Format &format)
792 {
793 if (Worksheet *sheet = currentWorksheet())
794 return sheet->setColumnFormat(colFirst, colLast, format);
795 return false;
796 }
797
798
799 /*!
800 Sets hidden property of columns [\a colFirst, \a colLast] to \a hidden.
801 Columns are 1-indexed.
802 Returns true on success.
803 */
setColumnHidden(int colFirst,int colLast,bool hidden)804 bool Document::setColumnHidden(int colFirst, int colLast, bool hidden)
805 {
806 if (Worksheet *sheet = currentWorksheet())
807 return sheet->setColumnHidden(colFirst, colLast, hidden);
808 return false;
809 }
810
811 /*!
812 Returns width of the \a column in characters of the normal font.
813 Columns are 1-indexed.
814 Returns true on success.
815 */
columnWidth(int column)816 double Document::columnWidth(int column)
817 {
818 if (Worksheet *sheet = currentWorksheet())
819 return sheet->columnWidth(column);
820 return 0.0;
821 }
822
823 /*!
824 Returns formatting of the \a column. Columns are 1-indexed.
825 */
columnFormat(int column)826 Format Document::columnFormat(int column)
827 {
828 if (Worksheet *sheet = currentWorksheet())
829 return sheet->columnFormat(column);
830 return Format();
831 }
832
833 /*!
834 Returns true if \a column is hidden. Columns are 1-indexed.
835 */
isColumnHidden(int column)836 bool Document::isColumnHidden(int column)
837 {
838 if (Worksheet *sheet = currentWorksheet())
839 return sheet->isColumnHidden(column);
840 return false;
841 }
842
843 /*!
844 Sets the \a format of the \a row.
845 Rows are 1-indexed.
846
847 Returns true if success.
848 */
setRowFormat(int row,const Format & format)849 bool Document::setRowFormat(int row, const Format &format)
850 {
851 return setRowFormat(row,row, format);
852 }
853
854 /*!
855 Sets the \a format of the rows including and between \a rowFirst and \a rowLast.
856 Rows are 1-indexed.
857
858 Returns true if success.
859 */
setRowFormat(int rowFirst,int rowLast,const Format & format)860 bool Document::setRowFormat(int rowFirst, int rowLast, const Format &format)
861 {
862 if (Worksheet *sheet = currentWorksheet())
863 return sheet->setRowFormat(rowFirst, rowLast, format);
864 return false;
865 }
866
867 /*!
868 Sets the \a hidden property of the row \a row.
869 Rows are 1-indexed. If hidden is true rows will not be visible.
870
871 Returns true if success.
872 */
setRowHidden(int row,bool hidden)873 bool Document::setRowHidden(int row, bool hidden)
874 {
875 return setRowHidden(row,row,hidden);
876 }
877
878 /*!
879 Sets the \a hidden property of the rows including and between \a rowFirst and \a rowLast.
880 Rows are 1-indexed. If hidden is true rows will not be visible.
881
882 Returns true if success.
883 */
setRowHidden(int rowFirst,int rowLast,bool hidden)884 bool Document::setRowHidden(int rowFirst, int rowLast, bool hidden)
885 {
886 if (Worksheet *sheet = currentWorksheet())
887 return sheet->setRowHidden(rowFirst, rowLast, hidden);
888 return false;
889 }
890
891 /*!
892 Sets the \a height of the row \a row.
893 Row height measured in point size.
894 Rows are 1-indexed.
895
896 Returns true if success.
897 */
setRowHeight(int row,double height)898 bool Document::setRowHeight(int row, double height)
899 {
900 return setRowHeight(row,row,height);
901 }
902
903 /*!
904 Sets the \a height of the rows including and between \a rowFirst and \a rowLast.
905 Row height measured in point size.
906 Rows are 1-indexed.
907
908 Returns true if success.
909 */
setRowHeight(int rowFirst,int rowLast,double height)910 bool Document::setRowHeight(int rowFirst, int rowLast, double height)
911 {
912 if (Worksheet *sheet = currentWorksheet())
913 return sheet->setRowHeight(rowFirst, rowLast, height);
914 return false;
915 }
916
917 /*!
918 Returns height of \a row in points.
919 */
rowHeight(int row)920 double Document::rowHeight(int row)
921 {
922 if (Worksheet *sheet = currentWorksheet())
923 return sheet->rowHeight(row);
924 return 0.0;
925 }
926
927 /*!
928 Returns format of \a row.
929 */
rowFormat(int row)930 Format Document::rowFormat(int row)
931 {
932 if (Worksheet *sheet = currentWorksheet())
933 return sheet->rowFormat(row);
934 return Format();
935 }
936
937 /*!
938 Returns true if \a row is hidden.
939 */
isRowHidden(int row)940 bool Document::isRowHidden(int row)
941 {
942 if (Worksheet *sheet = currentWorksheet())
943 return sheet->isRowHidden(row);
944 return false;
945 }
946
947 /*!
948 Groups rows from \a rowFirst to \a rowLast with the given \a collapsed.
949 Returns false if error occurs.
950 */
groupRows(int rowFirst,int rowLast,bool collapsed)951 bool Document::groupRows(int rowFirst, int rowLast, bool collapsed)
952 {
953 if (Worksheet *sheet = currentWorksheet())
954 return sheet->groupRows(rowFirst, rowLast, collapsed);
955 return false;
956 }
957
958 /*!
959 Groups columns from \a colFirst to \a colLast with the given \a collapsed.
960 Returns false if error occurs.
961 */
groupColumns(int colFirst,int colLast,bool collapsed)962 bool Document::groupColumns(int colFirst, int colLast, bool collapsed)
963 {
964 if (Worksheet *sheet = currentWorksheet())
965 return sheet->groupColumns(colFirst, colLast, collapsed);
966 return false;
967 }
968
969 /*!
970 * Add a data \a validation rule for current worksheet. Returns true if successful.
971 */
addDataValidation(const DataValidation & validation)972 bool Document::addDataValidation(const DataValidation &validation)
973 {
974 if (Worksheet *sheet = currentWorksheet())
975 return sheet->addDataValidation(validation);
976 return false;
977 }
978
979 /*!
980 * Add a conditional formatting \a cf for current worksheet. Returns true if successful.
981 */
addConditionalFormatting(const ConditionalFormatting & cf)982 bool Document::addConditionalFormatting(const ConditionalFormatting &cf)
983 {
984 if (Worksheet *sheet = currentWorksheet())
985 return sheet->addConditionalFormatting(cf);
986 return false;
987 }
988
989 /*!
990 * \overload
991 * Returns the cell at the position \a pos. If there is no cell at
992 * the specified position, the function returns 0.
993 *
994 * \sa read()
995 */
cellAt(const CellReference & pos) const996 Cell *Document::cellAt(const CellReference &pos) const
997 {
998 if (Worksheet *sheet = currentWorksheet())
999 return sheet->cellAt(pos);
1000 return 0;
1001 }
1002
1003 /*!
1004 * Returns the cell at the given \a row and \a col. If there
1005 * is no cell at the specified position, the function returns 0.
1006 *
1007 * \sa read()
1008 */
cellAt(int row,int col) const1009 Cell *Document::cellAt(int row, int col) const
1010 {
1011 if (Worksheet *sheet = currentWorksheet())
1012 return sheet->cellAt(row, col);
1013 return 0;
1014 }
1015
1016 /*!
1017 * \brief Create a defined name in the workbook with the given \a name, \a formula, \a comment
1018 * and \a scope.
1019 *
1020 * \param name The defined name.
1021 * \param formula The cell or range that the defined name refers to.
1022 * \param scope The name of one worksheet, or empty which means golbal scope.
1023 * \return Return false if the name invalid.
1024 */
defineName(const QString & name,const QString & formula,const QString & comment,const QString & scope)1025 bool Document::defineName(const QString &name, const QString &formula, const QString &comment, const QString &scope)
1026 {
1027 Q_D(Document);
1028
1029 return d->workbook->defineName(name, formula, comment, scope);
1030 }
1031
1032 /*!
1033 Return the range that contains cell data.
1034 */
dimension() const1035 CellRange Document::dimension() const
1036 {
1037 if (Worksheet *sheet = currentWorksheet())
1038 return sheet->dimension();
1039 return CellRange();
1040 }
1041
1042 /*!
1043 * Returns the value of the document's \a key property.
1044 */
documentProperty(const QString & key) const1045 QString Document::documentProperty(const QString &key) const
1046 {
1047 Q_D(const Document);
1048 auto it = d->documentProperties.constFind(key);
1049 if (it != d->documentProperties.constEnd())
1050 return it.value();
1051 else
1052 return QString();
1053 }
1054
1055 /*!
1056 Set the document properties such as Title, Author etc.
1057
1058 The method can be used to set the document properties of the Excel
1059 file created by Qt Xlsx. These properties are visible when you use the
1060 Office Button -> Prepare -> Properties option in Excel and are also
1061 available to external applications that read or index windows files.
1062
1063 The \a property \a key that can be set are:
1064
1065 \list
1066 \li title
1067 \li subject
1068 \li creator
1069 \li manager
1070 \li company
1071 \li category
1072 \li keywords
1073 \li description
1074 \li status
1075 \endlist
1076 */
setDocumentProperty(const QString & key,const QString & property)1077 void Document::setDocumentProperty(const QString &key, const QString &property)
1078 {
1079 Q_D(Document);
1080 d->documentProperties[key] = property;
1081 }
1082
1083 /*!
1084 * Returns the names of all properties that were addedusing setDocumentProperty().
1085 */
documentPropertyNames() const1086 QStringList Document::documentPropertyNames() const
1087 {
1088 Q_D(const Document);
1089 return d->documentProperties.keys();
1090 }
1091
1092 /*!
1093 * Return the internal Workbook object.
1094 */
workbook() const1095 Workbook *Document::workbook() const
1096 {
1097 Q_D(const Document);
1098 return d->workbook.data();
1099 }
1100
1101 /*!
1102 * Returns the sheet object named \a sheetName.
1103 */
sheet(const QString & sheetName) const1104 AbstractSheet *Document::sheet(const QString &sheetName) const
1105 {
1106 Q_D(const Document);
1107 return d->workbook->sheet(sheetNames().indexOf(sheetName));
1108 }
1109
1110 /*!
1111 * Creates and append an sheet with the given \a name and \a type.
1112 * Return true if success.
1113 */
addSheet(const QString & name,AbstractSheet::SheetType type)1114 bool Document::addSheet(const QString &name, AbstractSheet::SheetType type)
1115 {
1116 Q_D(Document);
1117 return d->workbook->addSheet(name, type);
1118 }
1119
1120 /*!
1121 * Creates and inserts an document with the given \a name and \a type at the \a index.
1122 * Returns false if the \a name already used.
1123 */
insertSheet(int index,const QString & name,AbstractSheet::SheetType type)1124 bool Document::insertSheet(int index, const QString &name, AbstractSheet::SheetType type)
1125 {
1126 Q_D(Document);
1127 return d->workbook->insertSheet(index, name, type);
1128 }
1129
1130 /*!
1131 Rename the worksheet from \a oldName to \a newName.
1132 Returns true if the success.
1133 */
renameSheet(const QString & oldName,const QString & newName)1134 bool Document::renameSheet(const QString &oldName, const QString &newName)
1135 {
1136 Q_D(Document);
1137 if (oldName == newName)
1138 return false;
1139 return d->workbook->renameSheet(sheetNames().indexOf(oldName), newName);
1140 }
1141
1142 /*!
1143 Make a copy of the worksheet \a srcName with the new name \a distName.
1144 Returns true if the success.
1145 */
copySheet(const QString & srcName,const QString & distName)1146 bool Document::copySheet(const QString &srcName, const QString &distName)
1147 {
1148 Q_D(Document);
1149 if (srcName == distName)
1150 return false;
1151 return d->workbook->copySheet(sheetNames().indexOf(srcName), distName);
1152 }
1153
1154 /*!
1155 Move the worksheet \a srcName to the new pos \a distIndex.
1156 Returns true if the success.
1157 */
moveSheet(const QString & srcName,int distIndex)1158 bool Document::moveSheet(const QString &srcName, int distIndex)
1159 {
1160 Q_D(Document);
1161 return d->workbook->moveSheet(sheetNames().indexOf(srcName), distIndex);
1162 }
1163
1164 /*!
1165 Delete the worksheet \a name.
1166 Returns true if current sheet was deleted successfully.
1167 */
deleteSheet(const QString & name)1168 bool Document::deleteSheet(const QString &name)
1169 {
1170 Q_D(Document);
1171 return d->workbook->deleteSheet(sheetNames().indexOf(name));
1172 }
1173
1174 /*!
1175 * \brief Return pointer of current sheet.
1176 */
currentSheet() const1177 AbstractSheet *Document::currentSheet() const
1178 {
1179 Q_D(const Document);
1180
1181 return d->workbook->activeSheet();
1182 }
1183
1184 /*!
1185 * \brief Return pointer of current worksheet.
1186 * If the type of sheet is not AbstractSheet::ST_WorkSheet, then 0 will be returned.
1187 */
currentWorksheet() const1188 Worksheet *Document::currentWorksheet() const
1189 {
1190 AbstractSheet *st = currentSheet();
1191 if (st && st->sheetType() == AbstractSheet::ST_WorkSheet)
1192 return static_cast<Worksheet *>(st);
1193 else
1194 return 0;
1195 }
1196
1197 /*!
1198 * \brief Set worksheet named \a name to be active sheet.
1199 * Returns true if success.
1200 */
selectSheet(const QString & name)1201 bool Document::selectSheet(const QString &name)
1202 {
1203 Q_D(Document);
1204 return d->workbook->setActiveSheet(sheetNames().indexOf(name));
1205 }
1206
1207 /*!
1208 * Returns the names of worksheets contained in current document.
1209 */
sheetNames() const1210 QStringList Document::sheetNames() const
1211 {
1212 Q_D(const Document);
1213 return d->workbook->worksheetNames();
1214 }
1215
1216 /*!
1217 * Save current document to the filesystem. If no name specified when
1218 * the document constructed, a default name "book1.xlsx" will be used.
1219 * Returns true if saved successfully.
1220 */
save() const1221 bool Document::save() const
1222 {
1223 Q_D(const Document);
1224 QString name = d->packageName.isEmpty() ? d->defaultPackageName : d->packageName;
1225
1226 return saveAs(name);
1227 }
1228
1229 /*!
1230 * Saves the document to the file with the given \a name.
1231 * Returns true if saved successfully.
1232 */
saveAs(const QString & name) const1233 bool Document::saveAs(const QString &name) const
1234 {
1235 QFile file(name);
1236 if (file.open(QIODevice::WriteOnly))
1237 return saveAs(&file);
1238 return false;
1239 }
1240
1241 /*!
1242 * \overload
1243 * This function writes a document to the given \a device.
1244 *
1245 * \warning The \a device will be closed when this function returned.
1246 */
saveAs(QIODevice * device) const1247 bool Document::saveAs(QIODevice *device) const
1248 {
1249 Q_D(const Document);
1250 return d->savePackage(device);
1251 }
1252
isLoadPackage() const1253 bool Document::isLoadPackage() const
1254 {
1255 Q_D(const Document);
1256 return d->isLoad;
1257 }
1258
load() const1259 bool Document::load() const
1260 {
1261 return isLoadPackage();
1262 }
1263
copyStyle(const QString & from,const QString & to)1264 bool Document::copyStyle(const QString &from, const QString &to) {
1265 return DocumentPrivate::copyStyle(from, to);
1266 }
1267
1268 /*!
1269 * Destroys the document and cleans up.
1270 */
~Document()1271 Document::~Document()
1272 {
1273 delete d_ptr;
1274 }
1275
1276 // add by liufeijin 20181025 {{
changeimage(int filenoinmidea,QString newfile)1277 bool Document::changeimage(int filenoinmidea, QString newfile)
1278 {
1279 Q_D(const Document);
1280 QImage newpic;
1281
1282 newpic=QImage(newfile);
1283
1284 QList<QSharedPointer<MediaFile> > mediaFileToLoad = d->workbook->mediaFiles();
1285 QSharedPointer<MediaFile> mf = mediaFileToLoad[filenoinmidea];
1286
1287 const QString suffix = newfile.mid(newfile.lastIndexOf(QLatin1Char('.'))+1);
1288 QString mimetypemy;
1289 if(QString::compare(QLatin1String("jpg"), suffix, Qt::CaseInsensitive)==0)
1290 mimetypemy=QStringLiteral("image/jpeg");
1291 if(QString::compare(QLatin1String("bmp"), suffix, Qt::CaseInsensitive)==0)
1292 mimetypemy=QStringLiteral("image/bmp");
1293 if(QString::compare(QLatin1String("gif"), suffix, Qt::CaseInsensitive)==0)
1294 mimetypemy=QStringLiteral("image/gif");
1295 if(QString::compare(QLatin1String("png"), suffix, Qt::CaseInsensitive)==0)
1296 mimetypemy=QStringLiteral("image/png");
1297
1298 QByteArray ba;
1299 QBuffer buffer(&ba);
1300 buffer.setBuffer(&ba);
1301 buffer.open(QIODevice::WriteOnly);
1302 newpic.save(&buffer,suffix.toLocal8Bit().data());
1303
1304 mf->set(ba,suffix,mimetypemy);
1305 mediaFileToLoad[filenoinmidea]=mf;
1306
1307 return true;
1308 }
1309 // liufeijin }}
1310
1311
1312 /*!
1313 Returns map of columns with there maximal width
1314 */
getMaximalColumnWidth(int firstRow,int lastRow)1315 QMap<int, int> Document::getMaximalColumnWidth(int firstRow, int lastRow)
1316 {
1317 const int defaultPixelSize = 11; //Default font pixel size of excel?
1318 int maxRows = -1;
1319 int maxCols = -1;
1320 QVector<CellLocation> cellLocation = currentWorksheet()->getFullCells(&maxRows, &maxCols);
1321
1322 QMap<int, int> colWidth;
1323
1324 for(int i=0; i < cellLocation.size(); i++)
1325 {
1326 int col = cellLocation.at(i).col;
1327 int row = cellLocation.at(i).row;
1328 int fs = cellLocation.at(i).cell->format().fontSize();
1329 if( fs <= 0)
1330 {
1331 fs = defaultPixelSize;
1332 }
1333
1334 // QString str = cellLocation.at(i).cell.data()->value().toString();
1335 QString str = read(row, col).toString();
1336
1337 double w = str.length() * double(fs) / defaultPixelSize + 1; // width not perfect, but works reasonably well
1338
1339 if( (row >= firstRow) && (row <= lastRow))
1340 {
1341 if( w > colWidth.value(col))
1342 {
1343 colWidth.insert(col, int(w));
1344 }
1345 }
1346 }
1347
1348 return colWidth;
1349 }
1350
1351
1352 /*!
1353 Auto ets width in characters of columns with the given \a range.
1354 Returns true on success.
1355 */
autosizeColumnWidth(const CellRange & range)1356 bool Document::autosizeColumnWidth(const CellRange &range)
1357 {
1358 bool erg = false;
1359
1360 if( !range.isValid())
1361 {
1362 return false;
1363 }
1364
1365 const QMap<int, int> colWidth = getMaximalColumnWidth(range.firstRow(), range.lastRow());
1366 auto it = colWidth.constBegin();
1367 while (it != colWidth.constEnd()) {
1368 if( (it.key() >= range.firstColumn()) && (it.key() <= range.lastColumn()) )
1369 {
1370 erg |= setColumnWidth(it.key(), it.value());
1371 }
1372 ++it;
1373 }
1374
1375 return erg;
1376 }
1377
1378
1379 /*!
1380 Auto sets width in characters \a column . Columns are 1-indexed.
1381 Returns true on success.
1382 */
autosizeColumnWidth(int column)1383 bool Document::autosizeColumnWidth(int column)
1384 {
1385 bool erg = false;
1386
1387 const QMap<int, int> colWidth = getMaximalColumnWidth();
1388 auto it = colWidth.constBegin();
1389 while (it != colWidth.constEnd()) {
1390 if( it.key() == column)
1391 {
1392 erg |= setColumnWidth(it.key(), it.value());
1393 }
1394 ++it;
1395 }
1396
1397 return erg;
1398 }
1399
1400
1401 /*!
1402 Auto sets width in characters for columns [\a colFirst, \a colLast]. Columns are 1-indexed.
1403 Returns true on success.
1404 */
autosizeColumnWidth(int colFirst,int colLast)1405 bool Document::autosizeColumnWidth(int colFirst, int colLast)
1406 {
1407 Q_UNUSED(colFirst)
1408 Q_UNUSED(colLast)
1409 bool erg = false;
1410
1411 const QMap<int, int> colWidth = getMaximalColumnWidth();
1412 auto it = colWidth.constBegin();
1413 while (it != colWidth.constEnd()) {
1414 if( (it.key() >= colFirst) && (it.key() <= colLast) )
1415 {
1416 erg |= setColumnWidth(it.key(), it.value());
1417 }
1418 ++it;
1419 }
1420
1421 return erg;
1422 }
1423
1424
1425 /*!
1426 Auto sets width in characters for all columns.
1427 Returns true on success.
1428 */
autosizeColumnWidth(void)1429 bool Document::autosizeColumnWidth(void)
1430 {
1431 bool erg = false;
1432
1433 const QMap<int, int> colWidth = getMaximalColumnWidth();
1434 auto it = colWidth.constBegin();
1435 while (it != colWidth.constEnd()) {
1436 erg |= setColumnWidth(it.key(), it.value());
1437 ++it;
1438 }
1439
1440 return erg;
1441 }
1442
1443
1444 QT_END_NAMESPACE_XLSX
1445