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