xlsxdocument.cpp 40 KB

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