xlsxdocument.cpp 32 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046
  1. /****************************************************************************
  2. ** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me>
  3. ** All right reserved.
  4. **
  5. ** Permission is hereby granted, free of charge, to any person obtaining
  6. ** a copy of this software and associated documentation files (the
  7. ** "Software"), to deal in the Software without restriction, including
  8. ** without limitation the rights to use, copy, modify, merge, publish,
  9. ** distribute, sublicense, and/or sell copies of the Software, and to
  10. ** permit persons to whom the Software is furnished to do so, subject to
  11. ** the following conditions:
  12. **
  13. ** The above copyright notice and this permission notice shall be
  14. ** included in all copies or substantial portions of the Software.
  15. **
  16. ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  17. ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  18. ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  19. ** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  20. ** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  21. ** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  22. ** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  23. **
  24. ****************************************************************************/
  25. #include "xlsxdocument.h"
  26. #include "xlsxdocument_p.h"
  27. #include "xlsxworkbook.h"
  28. #include "xlsxworksheet.h"
  29. #include "xlsxcontenttypes_p.h"
  30. #include "xlsxrelationships_p.h"
  31. #include "xlsxstyles_p.h"
  32. #include "xlsxtheme_p.h"
  33. #include "xlsxdocpropsapp_p.h"
  34. #include "xlsxdocpropscore_p.h"
  35. #include "xlsxsharedstrings_p.h"
  36. #include "xlsxutility_p.h"
  37. #include "xlsxworkbook_p.h"
  38. #include "xlsxdrawing_p.h"
  39. #include "xlsxmediafile_p.h"
  40. #include "xlsxchart.h"
  41. #include "xlsxzipreader_p.h"
  42. #include "xlsxzipwriter_p.h"
  43. #include <QFile>
  44. #include <QPointF>
  45. #include <QBuffer>
  46. #include <QDir>
  47. QT_BEGIN_NAMESPACE_XLSX
  48. /*
  49. From Wikipedia: The Open Packaging Conventions (OPC) is a
  50. container-file technology initially created by Microsoft to store
  51. a combination of XML and non-XML files that together form a single
  52. entity such as an Open XML Paper Specification (OpenXPS)
  53. document. http://en.wikipedia.org/wiki/Open_Packaging_Conventions.
  54. At its simplest an Excel XLSX file contains the following elements:
  55. ____ [Content_Types].xml
  56. |
  57. |____ docProps
  58. | |____ app.xml
  59. | |____ core.xml
  60. |
  61. |____ xl
  62. | |____ workbook.xml
  63. | |____ worksheets
  64. | | |____ sheet1.xml
  65. | |
  66. | |____ styles.xml
  67. | |
  68. | |____ theme
  69. | | |____ theme1.xml
  70. | |
  71. | |_____rels
  72. | |____ workbook.xml.rels
  73. |
  74. |_____rels
  75. |____ .rels
  76. The Packager class coordinates the classes that represent the
  77. elements of the package and writes them into the XLSX file.
  78. */
  79. DocumentPrivate::DocumentPrivate(Document *p) :
  80. q_ptr(p), defaultPackageName(QStringLiteral("Book1.xlsx"))
  81. {
  82. }
  83. void DocumentPrivate::init()
  84. {
  85. if (contentTypes.isNull())
  86. contentTypes = QSharedPointer<ContentTypes>(new ContentTypes(ContentTypes::F_NewFromScratch));
  87. if (workbook.isNull())
  88. workbook = QSharedPointer<Workbook>(new Workbook(Workbook::F_NewFromScratch));
  89. }
  90. bool DocumentPrivate::loadPackage(QIODevice *device)
  91. {
  92. Q_Q(Document);
  93. ZipReader zipReader(device);
  94. QStringList filePaths = zipReader.filePaths();
  95. //Load the Content_Types file
  96. if (!filePaths.contains(QLatin1String("[Content_Types].xml")))
  97. return false;
  98. contentTypes = QSharedPointer<ContentTypes>(new ContentTypes(ContentTypes::F_LoadFromExists));
  99. contentTypes->loadFromXmlData(zipReader.fileData(QStringLiteral("[Content_Types].xml")));
  100. //Load root rels file
  101. if (!filePaths.contains(QLatin1String("_rels/.rels")))
  102. return false;
  103. Relationships rootRels;
  104. rootRels.loadFromXmlData(zipReader.fileData(QStringLiteral("_rels/.rels")));
  105. //load core property
  106. QList<XlsxRelationship> rels_core = rootRels.packageRelationships(QStringLiteral("/metadata/core-properties"));
  107. if (!rels_core.isEmpty()) {
  108. //Get the core property file name if it exists.
  109. //In normal case, this should be "docProps/core.xml"
  110. QString docPropsCore_Name = rels_core[0].target;
  111. DocPropsCore props(DocPropsCore::F_LoadFromExists);
  112. props.loadFromXmlData(zipReader.fileData(docPropsCore_Name));
  113. foreach (QString name, props.propertyNames())
  114. q->setDocumentProperty(name, props.property(name));
  115. }
  116. //load app property
  117. QList<XlsxRelationship> rels_app = rootRels.documentRelationships(QStringLiteral("/extended-properties"));
  118. if (!rels_app.isEmpty()) {
  119. //Get the app property file name if it exists.
  120. //In normal case, this should be "docProps/app.xml"
  121. QString docPropsApp_Name = rels_app[0].target;
  122. DocPropsApp props(DocPropsApp::F_LoadFromExists);
  123. props.loadFromXmlData(zipReader.fileData(docPropsApp_Name));
  124. foreach (QString name, props.propertyNames())
  125. q->setDocumentProperty(name, props.property(name));
  126. }
  127. //load workbook now, Get the workbook file path from the root rels file
  128. //In normal case, this should be "xl/workbook.xml"
  129. workbook = QSharedPointer<Workbook>(new Workbook(Workbook::F_LoadFromExists));
  130. QList<XlsxRelationship> rels_xl = rootRels.documentRelationships(QStringLiteral("/officeDocument"));
  131. if (rels_xl.isEmpty())
  132. return false;
  133. QString xlworkbook_Path = rels_xl[0].target;
  134. QString xlworkbook_Dir = splitPath(xlworkbook_Path)[0];
  135. workbook->relationships()->loadFromXmlData(zipReader.fileData(getRelFilePath(xlworkbook_Path)));
  136. workbook->setFilePath(xlworkbook_Path);
  137. workbook->loadFromXmlData(zipReader.fileData(xlworkbook_Path));
  138. //load styles
  139. QList<XlsxRelationship> rels_styles = workbook->relationships()->documentRelationships(QStringLiteral("/styles"));
  140. if (!rels_styles.isEmpty()) {
  141. //In normal case this should be styles.xml which in xl
  142. QString name = rels_styles[0].target;
  143. QString path = xlworkbook_Dir + QLatin1String("/") + name;
  144. QSharedPointer<Styles> styles (new Styles(Styles::F_LoadFromExists));
  145. styles->loadFromXmlData(zipReader.fileData(path));
  146. workbook->d_func()->styles = styles;
  147. }
  148. //load sharedStrings
  149. QList<XlsxRelationship> rels_sharedStrings = workbook->relationships()->documentRelationships(QStringLiteral("/sharedStrings"));
  150. if (!rels_sharedStrings.isEmpty()) {
  151. //In normal case this should be sharedStrings.xml which in xl
  152. QString name = rels_sharedStrings[0].target;
  153. QString path = xlworkbook_Dir + QLatin1String("/") + name;
  154. workbook->d_func()->sharedStrings->loadFromXmlData(zipReader.fileData(path));
  155. }
  156. //load theme
  157. QList<XlsxRelationship> rels_theme = workbook->relationships()->documentRelationships(QStringLiteral("/theme"));
  158. if (!rels_theme.isEmpty()) {
  159. //In normal case this should be theme/theme1.xml which in xl
  160. QString name = rels_theme[0].target;
  161. QString path = xlworkbook_Dir + QLatin1String("/") + name;
  162. workbook->theme()->loadFromXmlData(zipReader.fileData(path));
  163. }
  164. //load sheets
  165. for (int i=0; i<workbook->sheetCount(); ++i) {
  166. AbstractSheet *sheet = workbook->sheet(i);
  167. QString rel_path = getRelFilePath(sheet->filePath());
  168. //If the .rel file exists, load it.
  169. if (zipReader.filePaths().contains(rel_path))
  170. sheet->relationships()->loadFromXmlData(zipReader.fileData(rel_path));
  171. sheet->loadFromXmlData(zipReader.fileData(sheet->filePath()));
  172. }
  173. //load external links
  174. for (int i=0; i<workbook->d_func()->externalLinks.count(); ++i) {
  175. SimpleOOXmlFile *link = workbook->d_func()->externalLinks[i].data();
  176. QString rel_path = getRelFilePath(link->filePath());
  177. //If the .rel file exists, load it.
  178. if (zipReader.filePaths().contains(rel_path))
  179. link->relationships()->loadFromXmlData(zipReader.fileData(rel_path));
  180. link->loadFromXmlData(zipReader.fileData(link->filePath()));
  181. }
  182. //load drawings
  183. for (int i=0; i<workbook->drawings().size(); ++i) {
  184. Drawing *drawing = workbook->drawings()[i];
  185. QString rel_path = getRelFilePath(drawing->filePath());
  186. if (zipReader.filePaths().contains(rel_path))
  187. drawing->relationships()->loadFromXmlData(zipReader.fileData(rel_path));
  188. drawing->loadFromXmlData(zipReader.fileData(drawing->filePath()));
  189. }
  190. //load charts
  191. QList<QSharedPointer<Chart> > chartFileToLoad = workbook->chartFiles();
  192. for (int i=0; i<chartFileToLoad.size(); ++i) {
  193. QSharedPointer<Chart> cf = chartFileToLoad[i];
  194. cf->loadFromXmlData(zipReader.fileData(cf->filePath()));
  195. }
  196. //load media files
  197. QList<QSharedPointer<MediaFile> > mediaFileToLoad = workbook->mediaFiles();
  198. for (int i=0; i<mediaFileToLoad.size(); ++i) {
  199. QSharedPointer<MediaFile> mf = mediaFileToLoad[i];
  200. const QString path = mf->fileName();
  201. const QString suffix = path.mid(path.lastIndexOf(QLatin1Char('.'))+1);
  202. mf->set(zipReader.fileData(path), suffix);
  203. }
  204. return true;
  205. }
  206. bool DocumentPrivate::savePackage(QIODevice *device) const
  207. {
  208. Q_Q(const Document);
  209. ZipWriter zipWriter(device);
  210. if (zipWriter.error())
  211. return false;
  212. contentTypes->clearOverrides();
  213. DocPropsApp docPropsApp(DocPropsApp::F_NewFromScratch);
  214. DocPropsCore docPropsCore(DocPropsCore::F_NewFromScratch);
  215. // save worksheet xml files
  216. QList<QSharedPointer<AbstractSheet> > worksheets = workbook->getSheetsByTypes(AbstractSheet::ST_WorkSheet);
  217. if (!worksheets.isEmpty())
  218. docPropsApp.addHeadingPair(QStringLiteral("Worksheets"), worksheets.size());
  219. for (int i=0; i<worksheets.size(); ++i) {
  220. QSharedPointer<AbstractSheet> sheet = worksheets[i];
  221. contentTypes->addWorksheetName(QStringLiteral("sheet%1").arg(i+1));
  222. docPropsApp.addPartTitle(sheet->sheetName());
  223. zipWriter.addFile(QStringLiteral("xl/worksheets/sheet%1.xml").arg(i+1), sheet->saveToXmlData());
  224. Relationships *rel = sheet->relationships();
  225. if (!rel->isEmpty())
  226. zipWriter.addFile(QStringLiteral("xl/worksheets/_rels/sheet%1.xml.rels").arg(i+1), rel->saveToXmlData());
  227. }
  228. //save chartsheet xml files
  229. QList<QSharedPointer<AbstractSheet> > chartsheets = workbook->getSheetsByTypes(AbstractSheet::ST_ChartSheet);
  230. if (!chartsheets.isEmpty())
  231. docPropsApp.addHeadingPair(QStringLiteral("Chartsheets"), chartsheets.size());
  232. for (int i=0; i<chartsheets.size(); ++i) {
  233. QSharedPointer<AbstractSheet> sheet = chartsheets[i];
  234. contentTypes->addWorksheetName(QStringLiteral("sheet%1").arg(i+1));
  235. docPropsApp.addPartTitle(sheet->sheetName());
  236. zipWriter.addFile(QStringLiteral("xl/chartsheets/sheet%1.xml").arg(i+1), sheet->saveToXmlData());
  237. Relationships *rel = sheet->relationships();
  238. if (!rel->isEmpty())
  239. zipWriter.addFile(QStringLiteral("xl/chartsheets/_rels/sheet%1.xml.rels").arg(i+1), rel->saveToXmlData());
  240. }
  241. // save external links xml files
  242. for (int i=0; i<workbook->d_func()->externalLinks.count(); ++i) {
  243. SimpleOOXmlFile *link = workbook->d_func()->externalLinks[i].data();
  244. contentTypes->addExternalLinkName(QStringLiteral("externalLink%1").arg(i+1));
  245. zipWriter.addFile(QStringLiteral("xl/externalLinks/externalLink%1.xml").arg(i+1), link->saveToXmlData());
  246. Relationships *rel = link->relationships();
  247. if (!rel->isEmpty())
  248. zipWriter.addFile(QStringLiteral("xl/externalLinks/_rels/externalLink%1.xml.rels").arg(i+1), rel->saveToXmlData());
  249. }
  250. // save workbook xml file
  251. contentTypes->addWorkbook();
  252. zipWriter.addFile(QStringLiteral("xl/workbook.xml"), workbook->saveToXmlData());
  253. zipWriter.addFile(QStringLiteral("xl/_rels/workbook.xml.rels"), workbook->relationships()->saveToXmlData());
  254. // save drawing xml files
  255. for (int i=0; i<workbook->drawings().size(); ++i) {
  256. contentTypes->addDrawingName(QStringLiteral("drawing%1").arg(i+1));
  257. Drawing *drawing = workbook->drawings()[i];
  258. zipWriter.addFile(QStringLiteral("xl/drawings/drawing%1.xml").arg(i+1), drawing->saveToXmlData());
  259. if (!drawing->relationships()->isEmpty())
  260. zipWriter.addFile(QStringLiteral("xl/drawings/_rels/drawing%1.xml.rels").arg(i+1), drawing->relationships()->saveToXmlData());
  261. }
  262. // save docProps app/core xml file
  263. foreach (QString name, q->documentPropertyNames()) {
  264. docPropsApp.setProperty(name, q->documentProperty(name));
  265. docPropsCore.setProperty(name, q->documentProperty(name));
  266. }
  267. contentTypes->addDocPropApp();
  268. contentTypes->addDocPropCore();
  269. zipWriter.addFile(QStringLiteral("docProps/app.xml"), docPropsApp.saveToXmlData());
  270. zipWriter.addFile(QStringLiteral("docProps/core.xml"), docPropsCore.saveToXmlData());
  271. // save sharedStrings xml file
  272. if (!workbook->sharedStrings()->isEmpty()) {
  273. contentTypes->addSharedString();
  274. zipWriter.addFile(QStringLiteral("xl/sharedStrings.xml"), workbook->sharedStrings()->saveToXmlData());
  275. }
  276. // save styles xml file
  277. contentTypes->addStyles();
  278. zipWriter.addFile(QStringLiteral("xl/styles.xml"), workbook->styles()->saveToXmlData());
  279. // save theme xml file
  280. contentTypes->addTheme();
  281. zipWriter.addFile(QStringLiteral("xl/theme/theme1.xml"), workbook->theme()->saveToXmlData());
  282. // save chart xml files
  283. for (int i=0; i<workbook->chartFiles().size(); ++i) {
  284. contentTypes->addChartName(QStringLiteral("chart%1").arg(i+1));
  285. QSharedPointer<Chart> cf = workbook->chartFiles()[i];
  286. zipWriter.addFile(QStringLiteral("xl/charts/chart%1.xml").arg(i+1), cf->saveToXmlData());
  287. }
  288. // save image files
  289. for (int i=0; i<workbook->mediaFiles().size(); ++i) {
  290. QSharedPointer<MediaFile> mf = workbook->mediaFiles()[i];
  291. if (!mf->mimeType().isEmpty())
  292. contentTypes->addDefault(mf->suffix(), mf->mimeType());
  293. zipWriter.addFile(QStringLiteral("xl/media/image%1.%2").arg(i+1).arg(mf->suffix()), mf->contents());
  294. }
  295. // save root .rels xml file
  296. Relationships rootrels;
  297. rootrels.addDocumentRelationship(QStringLiteral("/officeDocument"), QStringLiteral("xl/workbook.xml"));
  298. rootrels.addPackageRelationship(QStringLiteral("/metadata/core-properties"), QStringLiteral("docProps/core.xml"));
  299. rootrels.addDocumentRelationship(QStringLiteral("/extended-properties"), QStringLiteral("docProps/app.xml"));
  300. zipWriter.addFile(QStringLiteral("_rels/.rels"), rootrels.saveToXmlData());
  301. // save content types xml file
  302. zipWriter.addFile(QStringLiteral("[Content_Types].xml"), contentTypes->saveToXmlData());
  303. zipWriter.close();
  304. return true;
  305. }
  306. /*!
  307. \class Document
  308. \inmodule QtXlsx
  309. \brief The Document class provides a API that is used to handle the contents of .xlsx files.
  310. */
  311. /*!
  312. * Creates a new empty xlsx document.
  313. * The \a parent argument is passed to QObject's constructor.
  314. */
  315. Document::Document(QObject *parent) :
  316. QObject(parent), d_ptr(new DocumentPrivate(this))
  317. {
  318. d_ptr->init();
  319. }
  320. /*!
  321. * \overload
  322. * Try to open an existing xlsx document named \a name.
  323. * The \a parent argument is passed to QObject's constructor.
  324. */
  325. Document::Document(const QString &name, QObject *parent) :
  326. QObject(parent), d_ptr(new DocumentPrivate(this))
  327. {
  328. d_ptr->packageName = name;
  329. if (QFile::exists(name)) {
  330. QFile xlsx(name);
  331. if (xlsx.open(QFile::ReadOnly))
  332. d_ptr->loadPackage(&xlsx);
  333. }
  334. d_ptr->init();
  335. }
  336. /*!
  337. * \overload
  338. * Try to open an existing xlsx document from \a device.
  339. * The \a parent argument is passed to QObject's constructor.
  340. */
  341. Document::Document(QIODevice *device, QObject *parent) :
  342. QObject(parent), d_ptr(new DocumentPrivate(this))
  343. {
  344. if (device && device->isReadable())
  345. d_ptr->loadPackage(device);
  346. d_ptr->init();
  347. }
  348. /*!
  349. \overload
  350. Write \a value to cell \a row_column with the given \a format.
  351. */
  352. bool Document::write(const CellReference &row_column, const QVariant &value, const Format &format)
  353. {
  354. if (Worksheet *sheet = currentWorksheet())
  355. return sheet->write(row_column, value, format);
  356. return false;
  357. }
  358. /*!
  359. * Write \a value to cell (\a row, \a col) with the \a format.
  360. * Returns true on success.
  361. */
  362. bool Document::write(int row, int col, const QVariant &value, const Format &format)
  363. {
  364. if (Worksheet *sheet = currentWorksheet())
  365. return sheet->write(row, col, value, format);
  366. return false;
  367. }
  368. /*!
  369. \overload
  370. Returns the contents of the cell \a cell.
  371. \sa cellAt()
  372. */
  373. QVariant Document::read(const CellReference &cell) const
  374. {
  375. if (Worksheet *sheet = currentWorksheet())
  376. return sheet->read(cell);
  377. return QVariant();
  378. }
  379. /*!
  380. Returns the contents of the cell (\a row, \a col).
  381. \sa cellAt()
  382. */
  383. QVariant Document::read(int row, int col) const
  384. {
  385. if (Worksheet *sheet = currentWorksheet())
  386. return sheet->read(row, col);
  387. return QVariant();
  388. }
  389. /*!
  390. * Insert an \a image to current active worksheet at the position \a row, \a column
  391. * Returns ture if success.
  392. */
  393. bool Document::insertImage(int row, int column, const QImage &image)
  394. {
  395. if (Worksheet *sheet = currentWorksheet())
  396. return sheet->insertImage(row, column, image);
  397. return false;
  398. }
  399. /*!
  400. * Creates an chart with the given \a size and insert it to the current
  401. * active worksheet at the position \a row, \a col.
  402. * The chart will be returned.
  403. */
  404. Chart *Document::insertChart(int row, int col, const QSize &size)
  405. {
  406. if (Worksheet *sheet = currentWorksheet())
  407. return sheet->insertChart(row, col, size);
  408. return 0;
  409. }
  410. /*!
  411. Merge a \a range of cells. The first cell should contain the data and the others should
  412. be blank. All cells will be applied the same style if a valid \a format is given.
  413. Returns true on success.
  414. \note All cells except the top-left one will be cleared.
  415. */
  416. bool Document::mergeCells(const CellRange &range, const Format &format)
  417. {
  418. if (Worksheet *sheet = currentWorksheet())
  419. return sheet->mergeCells(range, format);
  420. return false;
  421. }
  422. /*!
  423. Unmerge the cells in the \a range.
  424. Returns true on success.
  425. */
  426. bool Document::unmergeCells(const CellRange &range)
  427. {
  428. if (Worksheet *sheet = currentWorksheet())
  429. return sheet->unmergeCells(range);
  430. return false;
  431. }
  432. /*!
  433. Sets width in characters of columns with the given \a range and \a width.
  434. Returns true on success.
  435. */
  436. bool Document::setColumnWidth(const CellRange &range, double width)
  437. {
  438. if (Worksheet *sheet = currentWorksheet())
  439. return sheet->setColumnWidth(range, width);
  440. return false;
  441. }
  442. /*!
  443. Sets format property of columns with the gien \a range and \a format.
  444. Returns true on success.
  445. */
  446. bool Document::setColumnFormat(const CellRange &range, const Format &format)
  447. {
  448. if (Worksheet *sheet = currentWorksheet())
  449. return sheet->setColumnFormat(range, format);
  450. return false;
  451. }
  452. /*!
  453. Sets hidden property of columns \a range to \a hidden. Columns are 1-indexed.
  454. Hidden columns are not visible.
  455. Returns true on success.
  456. */
  457. bool Document::setColumnHidden(const CellRange &range, bool hidden)
  458. {
  459. if (Worksheet *sheet = currentWorksheet())
  460. return sheet->setColumnWidth(range, hidden);
  461. return false;
  462. }
  463. /*!
  464. Sets width in characters \a column to \a width. Columns are 1-indexed.
  465. Returns true on success.
  466. */
  467. bool Document::setColumnWidth(int column, double width)
  468. {
  469. return setColumnWidth(column,column,width);
  470. }
  471. /*!
  472. Sets format property \a column to \a format. Columns are 1-indexed.
  473. Returns true on success.
  474. */
  475. bool Document::setColumnFormat(int column, const Format &format)
  476. {
  477. return setColumnFormat(column,column,format);
  478. }
  479. /*!
  480. Sets hidden property of a \a column. Columns are 1-indexed.
  481. Returns true on success.
  482. */
  483. bool Document::setColumnHidden(int column, bool hidden)
  484. {
  485. return setColumnHidden(column,column,hidden);
  486. }
  487. /*!
  488. Sets width in characters for columns [\a colFirst, \a colLast]. Columns are 1-indexed.
  489. Returns true on success.
  490. */
  491. bool Document::setColumnWidth(int colFirst, int colLast, double width)
  492. {
  493. if (Worksheet *sheet = currentWorksheet())
  494. return sheet->setColumnWidth(colFirst, colLast, width);
  495. return false;
  496. }
  497. /*!
  498. Sets format property of columns [\a colFirst, \a colLast] to \a format.
  499. Columns are 1-indexed.
  500. Returns true on success.
  501. */
  502. bool Document::setColumnFormat(int colFirst, int colLast, const Format &format)
  503. {
  504. if (Worksheet *sheet = currentWorksheet())
  505. return sheet->setColumnFormat(colFirst, colLast, format);
  506. return false;
  507. }
  508. /*!
  509. Sets hidden property of columns [\a colFirst, \a colLast] to \a hidden.
  510. Columns are 1-indexed.
  511. Returns true on success.
  512. */
  513. bool Document::setColumnHidden(int colFirst, int colLast, bool hidden)
  514. {
  515. if (Worksheet *sheet = currentWorksheet())
  516. return sheet->setColumnHidden(colFirst, colLast, hidden);
  517. return false;
  518. }
  519. /*!
  520. Returns width of the \a column in characters of the normal font.
  521. Columns are 1-indexed.
  522. Returns true on success.
  523. */
  524. double Document::columnWidth(int column)
  525. {
  526. if (Worksheet *sheet = currentWorksheet())
  527. return sheet->columnWidth(column);
  528. return 0.0;
  529. }
  530. /*!
  531. Returns formatting of the \a column. Columns are 1-indexed.
  532. */
  533. Format Document::columnFormat(int column)
  534. {
  535. if (Worksheet *sheet = currentWorksheet())
  536. return sheet->columnFormat(column);
  537. return Format();
  538. }
  539. /*!
  540. Returns true if \a column is hidden. Columns are 1-indexed.
  541. */
  542. bool Document::isColumnHidden(int column)
  543. {
  544. if (Worksheet *sheet = currentWorksheet())
  545. return sheet->isColumnHidden(column);
  546. return false;
  547. }
  548. /*!
  549. Sets the \a format of the \a row.
  550. Rows are 1-indexed.
  551. Returns true if success.
  552. */
  553. bool Document::setRowFormat(int row, const Format &format)
  554. {
  555. return setRowFormat(row,row, format);
  556. }
  557. /*!
  558. Sets the \a format of the rows including and between \a rowFirst and \a rowLast.
  559. Rows are 1-indexed.
  560. Returns true if success.
  561. */
  562. bool Document::setRowFormat(int rowFirst, int rowLast, const Format &format)
  563. {
  564. if (Worksheet *sheet = currentWorksheet())
  565. return sheet->setRowFormat(rowFirst, rowLast, format);
  566. return false;
  567. }
  568. /*!
  569. Sets the \a hidden property of the row \a row.
  570. Rows are 1-indexed. If hidden is true rows will not be visible.
  571. Returns true if success.
  572. */
  573. bool Document::setRowHidden(int row, bool hidden)
  574. {
  575. return setRowHidden(row,row,hidden);
  576. }
  577. /*!
  578. Sets the \a hidden property of the rows including and between \a rowFirst and \a rowLast.
  579. Rows are 1-indexed. If hidden is true rows will not be visible.
  580. Returns true if success.
  581. */
  582. bool Document::setRowHidden(int rowFirst, int rowLast, bool hidden)
  583. {
  584. if (Worksheet *sheet = currentWorksheet())
  585. return sheet->setRowHidden(rowFirst, rowLast, hidden);
  586. return false;
  587. }
  588. /*!
  589. Sets the \a height of the row \a row.
  590. Row height measured in point size.
  591. Rows are 1-indexed.
  592. Returns true if success.
  593. */
  594. bool Document::setRowHeight(int row, double height)
  595. {
  596. return setRowHeight(row,row,height);
  597. }
  598. /*!
  599. Sets the \a height of the rows including and between \a rowFirst and \a rowLast.
  600. Row height measured in point size.
  601. Rows are 1-indexed.
  602. Returns true if success.
  603. */
  604. bool Document::setRowHeight(int rowFirst, int rowLast, double height)
  605. {
  606. if (Worksheet *sheet = currentWorksheet())
  607. return sheet->setRowHeight(rowFirst, rowLast, height);
  608. return false;
  609. }
  610. /*!
  611. Returns height of \a row in points.
  612. */
  613. double Document::rowHeight(int row)
  614. {
  615. if (Worksheet *sheet = currentWorksheet())
  616. return sheet->rowHeight(row);
  617. return 0.0;
  618. }
  619. /*!
  620. Returns format of \a row.
  621. */
  622. Format Document::rowFormat(int row)
  623. {
  624. if (Worksheet *sheet = currentWorksheet())
  625. return sheet->rowFormat(row);
  626. return Format();
  627. }
  628. /*!
  629. Returns true if \a row is hidden.
  630. */
  631. bool Document::isRowHidden(int row)
  632. {
  633. if (Worksheet *sheet = currentWorksheet())
  634. return sheet->isRowHidden(row);
  635. return false;
  636. }
  637. /*!
  638. Groups rows from \a rowFirst to \a rowLast with the given \a collapsed.
  639. Returns false if error occurs.
  640. */
  641. bool Document::groupRows(int rowFirst, int rowLast, bool collapsed)
  642. {
  643. if (Worksheet *sheet = currentWorksheet())
  644. return sheet->groupRows(rowFirst, rowLast, collapsed);
  645. return false;
  646. }
  647. /*!
  648. Groups columns from \a colFirst to \a colLast with the given \a collapsed.
  649. Returns false if error occurs.
  650. */
  651. bool Document::groupColumns(int colFirst, int colLast, bool collapsed)
  652. {
  653. if (Worksheet *sheet = currentWorksheet())
  654. return sheet->groupColumns(colFirst, colLast, collapsed);
  655. return false;
  656. }
  657. /*!
  658. * Add a data \a validation rule for current worksheet. Returns true if successful.
  659. */
  660. bool Document::addDataValidation(const DataValidation &validation)
  661. {
  662. if (Worksheet *sheet = currentWorksheet())
  663. return sheet->addDataValidation(validation);
  664. return false;
  665. }
  666. /*!
  667. * Add a conditional formatting \a cf for current worksheet. Returns true if successful.
  668. */
  669. bool Document::addConditionalFormatting(const ConditionalFormatting &cf)
  670. {
  671. if (Worksheet *sheet = currentWorksheet())
  672. return sheet->addConditionalFormatting(cf);
  673. return false;
  674. }
  675. /*!
  676. * \overload
  677. * Returns the cell at the position \a pos. If there is no cell at
  678. * the specified position, the function returns 0.
  679. *
  680. * \sa read()
  681. */
  682. Cell *Document::cellAt(const CellReference &pos) const
  683. {
  684. if (Worksheet *sheet = currentWorksheet())
  685. return sheet->cellAt(pos);
  686. return 0;
  687. }
  688. /*!
  689. * Returns the cell at the given \a row and \a col. If there
  690. * is no cell at the specified position, the function returns 0.
  691. *
  692. * \sa read()
  693. */
  694. Cell *Document::cellAt(int row, int col) const
  695. {
  696. if (Worksheet *sheet = currentWorksheet())
  697. return sheet->cellAt(row, col);
  698. return 0;
  699. }
  700. /*!
  701. * \brief Create a defined name in the workbook with the given \a name, \a formula, \a comment
  702. * and \a scope.
  703. *
  704. * \param name The defined name.
  705. * \param formula The cell or range that the defined name refers to.
  706. * \param scope The name of one worksheet, or empty which means golbal scope.
  707. * \return Return false if the name invalid.
  708. */
  709. bool Document::defineName(const QString &name, const QString &formula, const QString &comment, const QString &scope)
  710. {
  711. Q_D(Document);
  712. return d->workbook->defineName(name, formula, comment, scope);
  713. }
  714. /*!
  715. Return the range that contains cell data.
  716. */
  717. CellRange Document::dimension() const
  718. {
  719. if (Worksheet *sheet = currentWorksheet())
  720. return sheet->dimension();
  721. return CellRange();
  722. }
  723. /*!
  724. * Returns the value of the document's \a key property.
  725. */
  726. QString Document::documentProperty(const QString &key) const
  727. {
  728. Q_D(const Document);
  729. if (d->documentProperties.contains(key))
  730. return d->documentProperties[key];
  731. else
  732. return QString();
  733. }
  734. /*!
  735. Set the document properties such as Title, Author etc.
  736. The method can be used to set the document properties of the Excel
  737. file created by Qt Xlsx. These properties are visible when you use the
  738. Office Button -> Prepare -> Properties option in Excel and are also
  739. available to external applications that read or index windows files.
  740. The \a property \a key that can be set are:
  741. \list
  742. \li title
  743. \li subject
  744. \li creator
  745. \li manager
  746. \li company
  747. \li category
  748. \li keywords
  749. \li description
  750. \li status
  751. \endlist
  752. */
  753. void Document::setDocumentProperty(const QString &key, const QString &property)
  754. {
  755. Q_D(Document);
  756. d->documentProperties[key] = property;
  757. }
  758. /*!
  759. * Returns the names of all properties that were addedusing setDocumentProperty().
  760. */
  761. QStringList Document::documentPropertyNames() const
  762. {
  763. Q_D(const Document);
  764. return d->documentProperties.keys();
  765. }
  766. /*!
  767. * Return the internal Workbook object.
  768. */
  769. Workbook *Document::workbook() const
  770. {
  771. Q_D(const Document);
  772. return d->workbook.data();
  773. }
  774. /*!
  775. * Returns the sheet object named \a sheetName.
  776. */
  777. AbstractSheet *Document::sheet(const QString &sheetName) const
  778. {
  779. Q_D(const Document);
  780. return d->workbook->sheet(sheetNames().indexOf(sheetName));
  781. }
  782. /*!
  783. * Creates and append an sheet with the given \a name and \a type.
  784. * Return true if success.
  785. */
  786. bool Document::addSheet(const QString &name, AbstractSheet::SheetType type)
  787. {
  788. Q_D(Document);
  789. return d->workbook->addSheet(name, type);
  790. }
  791. /*!
  792. * Creates and inserts an document with the given \a name and \a type at the \a index.
  793. * Returns false if the \a name already used.
  794. */
  795. bool Document::insertSheet(int index, const QString &name, AbstractSheet::SheetType type)
  796. {
  797. Q_D(Document);
  798. return d->workbook->insertSheet(index, name, type);
  799. }
  800. /*!
  801. Rename the worksheet from \a oldName to \a newName.
  802. Returns true if the success.
  803. */
  804. bool Document::renameSheet(const QString &oldName, const QString &newName)
  805. {
  806. Q_D(Document);
  807. if (oldName == newName)
  808. return false;
  809. return d->workbook->renameSheet(sheetNames().indexOf(oldName), newName);
  810. }
  811. /*!
  812. Make a copy of the worksheet \a srcName with the new name \a distName.
  813. Returns true if the success.
  814. */
  815. bool Document::copySheet(const QString &srcName, const QString &distName)
  816. {
  817. Q_D(Document);
  818. if (srcName == distName)
  819. return false;
  820. return d->workbook->copySheet(sheetNames().indexOf(srcName), distName);
  821. }
  822. /*!
  823. Move the worksheet \a srcName to the new pos \a distIndex.
  824. Returns true if the success.
  825. */
  826. bool Document::moveSheet(const QString &srcName, int distIndex)
  827. {
  828. Q_D(Document);
  829. return d->workbook->moveSheet(sheetNames().indexOf(srcName), distIndex);
  830. }
  831. /*!
  832. Delete the worksheet \a name.
  833. Returns true if current sheet was deleted successfully.
  834. */
  835. bool Document::deleteSheet(const QString &name)
  836. {
  837. Q_D(Document);
  838. return d->workbook->deleteSheet(sheetNames().indexOf(name));
  839. }
  840. /*!
  841. * \brief Return pointer of current sheet.
  842. */
  843. AbstractSheet *Document::currentSheet() const
  844. {
  845. Q_D(const Document);
  846. return d->workbook->activeSheet();
  847. }
  848. /*!
  849. * \brief Return pointer of current worksheet.
  850. * If the type of sheet is not AbstractSheet::ST_WorkSheet, then 0 will be returned.
  851. */
  852. Worksheet *Document::currentWorksheet() const
  853. {
  854. AbstractSheet *st = currentSheet();
  855. if (st && st->sheetType() == AbstractSheet::ST_WorkSheet)
  856. return static_cast<Worksheet *>(st);
  857. else
  858. return 0;
  859. }
  860. /*!
  861. * \brief Set worksheet named \a name to be active sheet.
  862. * Returns true if success.
  863. */
  864. bool Document::selectSheet(const QString &name)
  865. {
  866. Q_D(Document);
  867. return d->workbook->setActiveSheet(sheetNames().indexOf(name));
  868. }
  869. /*!
  870. * Returns the names of worksheets contained in current document.
  871. */
  872. QStringList Document::sheetNames() const
  873. {
  874. Q_D(const Document);
  875. return d->workbook->worksheetNames();
  876. }
  877. /*!
  878. * Save current document to the filesystem. If no name specified when
  879. * the document constructed, a default name "book1.xlsx" will be used.
  880. * Returns true if saved successfully.
  881. */
  882. bool Document::save() const
  883. {
  884. Q_D(const Document);
  885. QString name = d->packageName.isEmpty() ? d->defaultPackageName : d->packageName;
  886. return saveAs(name);
  887. }
  888. /*!
  889. * Saves the document to the file with the given \a name.
  890. * Returns true if saved successfully.
  891. */
  892. bool Document::saveAs(const QString &name) const
  893. {
  894. QFile file(name);
  895. if (file.open(QIODevice::WriteOnly))
  896. return saveAs(&file);
  897. return false;
  898. }
  899. /*!
  900. * \overload
  901. * This function writes a document to the given \a device.
  902. *
  903. * \warning The \a device will be closed when this function returned.
  904. */
  905. bool Document::saveAs(QIODevice *device) const
  906. {
  907. Q_D(const Document);
  908. return d->savePackage(device);
  909. }
  910. /*!
  911. * Destroys the document and cleans up.
  912. */
  913. Document::~Document()
  914. {
  915. delete d_ptr;
  916. }
  917. QT_END_NAMESPACE_XLSX