xlsxdrawinganchor.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  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 "xlsxdrawinganchor_p.h"
  26. #include "xlsxdrawing_p.h"
  27. #include "xlsxmediafile_p.h"
  28. #include "xlsxchart.h"
  29. #include "xlsxworkbook.h"
  30. #include "xlsxutility_p.h"
  31. #include <QXmlStreamReader>
  32. #include <QXmlStreamWriter>
  33. #include <QBuffer>
  34. #include <QDir>
  35. namespace QXlsx {
  36. /*
  37. The vertices that define the position of a graphical object
  38. within the worksheet in pixels.
  39. +------------+------------+
  40. | A | B |
  41. +-----+------------+------------+
  42. | |(x1,y1) | |
  43. | 1 |(A1)._______|______ |
  44. | | | | |
  45. | | | | |
  46. +-----+----| OBJECT |-----+
  47. | | | | |
  48. | 2 | |______________. |
  49. | | | (B2)|
  50. | | | (x2,y2)|
  51. +---- +------------+------------+
  52. Example of an object that covers some of the area from cell A1 to B2.
  53. Based on the width and height of the object we need to calculate 8 vars:
  54. col_start, row_start, col_end, row_end, x1, y1, x2, y2.
  55. We also calculate the absolute x and y position of the top left vertex of
  56. the object. This is required for images.
  57. The width and height of the cells that the object occupies can be
  58. variable and have to be taken into account.
  59. */
  60. //anchor
  61. DrawingAnchor::DrawingAnchor(Drawing *drawing, ObjectType objectType)
  62. :m_drawing(drawing), m_objectType(objectType)
  63. {
  64. m_drawing->anchors.append(this);
  65. m_id = m_drawing->anchors.size();//must be unique in one drawing{x}.xml file.
  66. }
  67. DrawingAnchor::~DrawingAnchor()
  68. {
  69. }
  70. void DrawingAnchor::setObjectPicture(const QImage &img)
  71. {
  72. QByteArray ba;
  73. QBuffer buffer(&ba);
  74. buffer.open(QIODevice::WriteOnly);
  75. img.save(&buffer, "PNG");
  76. m_pictureFile = QSharedPointer<MediaFile>(new MediaFile(ba, QStringLiteral("png"), QStringLiteral("image/png")));
  77. m_drawing->workbook->addMediaFile(m_pictureFile);
  78. m_objectType = Picture;
  79. }
  80. void DrawingAnchor::setObjectGraphicFrame(QSharedPointer<Chart> chart)
  81. {
  82. m_chartFile = chart;
  83. m_drawing->workbook->addChartFile(chart);
  84. m_objectType = GraphicFrame;
  85. }
  86. QPoint DrawingAnchor::loadXmlPos(QXmlStreamReader &reader)
  87. {
  88. Q_ASSERT(reader.name() == QLatin1String("pos"));
  89. QPoint pos;
  90. QXmlStreamAttributes attrs = reader.attributes();
  91. pos.setX(attrs.value(QLatin1String("x")).toString().toInt());
  92. pos.setY(attrs.value(QLatin1String("y")).toString().toInt());
  93. return pos;
  94. }
  95. QSize DrawingAnchor::loadXmlExt(QXmlStreamReader &reader)
  96. {
  97. Q_ASSERT(reader.name() == QLatin1String("ext"));
  98. QSize size;
  99. QXmlStreamAttributes attrs = reader.attributes();
  100. size.setWidth(attrs.value(QLatin1String("cx")).toString().toInt());
  101. size.setHeight(attrs.value(QLatin1String("cy")).toString().toInt());
  102. return size;
  103. }
  104. XlsxMarker DrawingAnchor::loadXmlMarker(QXmlStreamReader &reader, const QString &node)
  105. {
  106. Q_ASSERT(reader.name() == node);
  107. int col = 0;
  108. int colOffset = 0;
  109. int row = 0;
  110. int rowOffset = 0;
  111. while (!reader.atEnd()) {
  112. reader.readNextStartElement();
  113. if (reader.tokenType() == QXmlStreamReader::StartElement) {
  114. if (reader.name() == QLatin1String("col")) {
  115. col = reader.readElementText().toInt();
  116. } else if (reader.name() == QLatin1String("colOff")) {
  117. colOffset = reader.readElementText().toInt();
  118. } else if (reader.name() == QLatin1String("row")) {
  119. row = reader.readElementText().toInt();
  120. } else if (reader.name() == QLatin1String("rowOff")) {
  121. rowOffset = reader.readElementText().toInt();
  122. }
  123. } else if (reader.tokenType() == QXmlStreamReader::EndElement
  124. && reader.name() == node) {
  125. break;
  126. }
  127. }
  128. return XlsxMarker(row, col, rowOffset, colOffset);
  129. }
  130. void DrawingAnchor::loadXmlObject(QXmlStreamReader &reader)
  131. {
  132. if (reader.name() == QLatin1String("sp")) {
  133. //Shape
  134. m_objectType = Shape;
  135. loadXmlObjectShape(reader);
  136. } else if (reader.name() == QLatin1String("grpSp")) {
  137. //Group Shape
  138. m_objectType = GroupShape;
  139. loadXmlObjectGroupShape(reader);
  140. } else if (reader.name() == QLatin1String("graphicFrame")) {
  141. //Graphic Frame
  142. m_objectType = GraphicFrame;
  143. loadXmlObjectGraphicFrame(reader);
  144. } else if (reader.name() == QLatin1String("cxnSp")) {
  145. //Connection Shape
  146. m_objectType = ConnectionShape;
  147. loadXmlObjectConnectionShape(reader);
  148. } else if (reader.name() == QLatin1String("pic")) {
  149. //Picture
  150. m_objectType = Picture;
  151. loadXmlObjectPicture(reader);
  152. }
  153. }
  154. void DrawingAnchor::loadXmlObjectConnectionShape(QXmlStreamReader &reader)
  155. {
  156. Q_UNUSED(reader)
  157. }
  158. void DrawingAnchor::loadXmlObjectGraphicFrame(QXmlStreamReader &reader)
  159. {
  160. Q_ASSERT(reader.name() == QLatin1String("graphicFrame"));
  161. while (!reader.atEnd()) {
  162. reader.readNextStartElement();
  163. if (reader.tokenType() == QXmlStreamReader::StartElement) {
  164. if (reader.name() == QLatin1String("chart")) {
  165. QString rId = reader.attributes().value(QLatin1String("r:id")).toString();
  166. QString name = m_drawing->relationships()->getRelationshipById(rId).target;
  167. QString path = QDir::cleanPath(splitPath(m_drawing->filePath())[0] + QLatin1String("/") + name);
  168. bool exist = false;
  169. QList<QSharedPointer<Chart> > cfs = m_drawing->workbook->chartFiles();
  170. for (int i=0; i<cfs.size(); ++i) {
  171. if (cfs[i]->filePath() == path) {
  172. //already exist
  173. exist = true;
  174. m_chartFile = cfs[i];
  175. }
  176. }
  177. if (!exist) {
  178. m_chartFile = QSharedPointer<Chart> (new Chart(m_drawing->sheet, Chart::F_LoadFromExists));
  179. m_chartFile->setFilePath(path);
  180. m_drawing->workbook->addChartFile(m_chartFile);
  181. }
  182. }
  183. } else if (reader.tokenType() == QXmlStreamReader::EndElement
  184. && reader.name() == QLatin1String("graphicFrame")) {
  185. break;
  186. }
  187. }
  188. return;
  189. }
  190. void DrawingAnchor::loadXmlObjectGroupShape(QXmlStreamReader &reader)
  191. {
  192. Q_UNUSED(reader)
  193. }
  194. void DrawingAnchor::loadXmlObjectPicture(QXmlStreamReader &reader)
  195. {
  196. Q_ASSERT(reader.name() == QLatin1String("pic"));
  197. while (!reader.atEnd()) {
  198. reader.readNextStartElement();
  199. if (reader.tokenType() == QXmlStreamReader::StartElement) {
  200. if (reader.name() == QLatin1String("blip")) {
  201. QString rId = reader.attributes().value(QLatin1String("r:embed")).toString();
  202. QString name = m_drawing->relationships()->getRelationshipById(rId).target;
  203. QString path = QDir::cleanPath(splitPath(m_drawing->filePath())[0] + QLatin1String("/") + name);
  204. bool exist = false;
  205. QList<QSharedPointer<MediaFile> > mfs = m_drawing->workbook->mediaFiles();
  206. for (int i=0; i<mfs.size(); ++i) {
  207. if (mfs[i]->fileName() == path) {
  208. //already exist
  209. exist = true;
  210. m_pictureFile = mfs[i];
  211. }
  212. }
  213. if (!exist) {
  214. m_pictureFile = QSharedPointer<MediaFile> (new MediaFile(path));
  215. m_drawing->workbook->addMediaFile(m_pictureFile, true);
  216. }
  217. }
  218. } else if (reader.tokenType() == QXmlStreamReader::EndElement
  219. && reader.name() == QLatin1String("pic")) {
  220. break;
  221. }
  222. }
  223. return;
  224. }
  225. void DrawingAnchor::loadXmlObjectShape(QXmlStreamReader &reader)
  226. {
  227. Q_UNUSED(reader)
  228. }
  229. void DrawingAnchor::saveXmlPos(QXmlStreamWriter &writer, const QPoint &pos) const
  230. {
  231. writer.writeEmptyElement(QStringLiteral("xdr:pos"));
  232. writer.writeAttribute(QStringLiteral("x"), QString::number(pos.x()));
  233. writer.writeAttribute(QStringLiteral("y"), QString::number(pos.y()));
  234. }
  235. void DrawingAnchor::saveXmlExt(QXmlStreamWriter &writer, const QSize &ext) const
  236. {
  237. writer.writeStartElement(QStringLiteral("xdr:ext"));
  238. writer.writeAttribute(QStringLiteral("cx"), QString::number(ext.width()));
  239. writer.writeAttribute(QStringLiteral("cy"), QString::number(ext.height()));
  240. writer.writeEndElement(); //xdr:ext
  241. }
  242. void DrawingAnchor::saveXmlMarker(QXmlStreamWriter &writer, const XlsxMarker &marker, const QString &node) const
  243. {
  244. writer.writeStartElement(node); //xdr:from or xdr:to
  245. writer.writeTextElement(QStringLiteral("xdr:col"), QString::number(marker.col()));
  246. writer.writeTextElement(QStringLiteral("xdr:colOff"), QString::number(marker.colOff()));
  247. writer.writeTextElement(QStringLiteral("xdr:row"), QString::number(marker.row()));
  248. writer.writeTextElement(QStringLiteral("xdr:rowOff"), QString::number(marker.rowOff()));
  249. writer.writeEndElement();
  250. }
  251. void DrawingAnchor::saveXmlObject(QXmlStreamWriter &writer) const
  252. {
  253. if (m_objectType == Picture)
  254. saveXmlObjectPicture(writer);
  255. else if (m_objectType == ConnectionShape)
  256. saveXmlObjectConnectionShape(writer);
  257. else if (m_objectType == GraphicFrame)
  258. saveXmlObjectGraphicFrame(writer);
  259. else if (m_objectType == GroupShape)
  260. saveXmlObjectGroupShape(writer);
  261. else if (m_objectType == Shape)
  262. saveXmlObjectShape(writer);
  263. }
  264. void DrawingAnchor::saveXmlObjectConnectionShape(QXmlStreamWriter &writer) const
  265. {
  266. Q_UNUSED(writer)
  267. }
  268. void DrawingAnchor::saveXmlObjectGraphicFrame(QXmlStreamWriter &writer) const
  269. {
  270. writer.writeStartElement(QStringLiteral("xdr:graphicFrame"));
  271. writer.writeAttribute(QStringLiteral("macro"), QString());
  272. writer.writeStartElement(QStringLiteral("xdr:nvGraphicFramePr"));
  273. writer.writeEmptyElement(QStringLiteral("xdr:cNvPr"));
  274. writer.writeAttribute(QStringLiteral("id"), QString::number(m_id));
  275. writer.writeAttribute(QStringLiteral("name"),QStringLiteral("Chart %1").arg(m_id));
  276. writer.writeEmptyElement(QStringLiteral("xdr:cNvGraphicFramePr"));
  277. writer.writeEndElement();//xdr:nvGraphicFramePr
  278. writer.writeStartElement(QStringLiteral("xdr:xfrm"));
  279. writer.writeEndElement(); //xdr:xfrm
  280. writer.writeStartElement(QStringLiteral("a:graphic"));
  281. writer.writeStartElement(QStringLiteral("a:graphicData"));
  282. writer.writeAttribute(QStringLiteral("uri"), QStringLiteral("http://schemas.openxmlformats.org/drawingml/2006/chart"));
  283. int idx = m_drawing->workbook->chartFiles().indexOf(m_chartFile);
  284. m_drawing->relationships()->addDocumentRelationship(QStringLiteral("/chart"), QStringLiteral("../charts/chart%1.xml").arg(idx+1));
  285. writer.writeEmptyElement(QStringLiteral("c:chart"));
  286. writer.writeAttribute(QStringLiteral("xmlns:c"), QStringLiteral("http://schemas.openxmlformats.org/drawingml/2006/chart"));
  287. writer.writeAttribute(QStringLiteral("xmlns:r"), QStringLiteral("http://schemas.openxmlformats.org/officeDocument/2006/relationships"));
  288. writer.writeAttribute(QStringLiteral("r:id"), QStringLiteral("rId%1").arg(m_drawing->relationships()->count()));
  289. writer.writeEndElement(); //a:graphicData
  290. writer.writeEndElement(); //a:graphic
  291. writer.writeEndElement(); //xdr:graphicFrame
  292. }
  293. void DrawingAnchor::saveXmlObjectGroupShape(QXmlStreamWriter &writer) const
  294. {
  295. Q_UNUSED(writer)
  296. }
  297. void DrawingAnchor::saveXmlObjectPicture(QXmlStreamWriter &writer) const
  298. {
  299. Q_ASSERT(m_objectType == Picture && !m_pictureFile.isNull());
  300. writer.writeStartElement(QStringLiteral("xdr:pic"));
  301. writer.writeStartElement(QStringLiteral("xdr:nvPicPr"));
  302. writer.writeEmptyElement(QStringLiteral("xdr:cNvPr"));
  303. writer.writeAttribute(QStringLiteral("id"), QString::number(m_id));
  304. writer.writeAttribute(QStringLiteral("name"), QStringLiteral("Picture %1").arg(m_id));
  305. writer.writeStartElement(QStringLiteral("xdr:cNvPicPr"));
  306. writer.writeEmptyElement(QStringLiteral("a:picLocks"));
  307. writer.writeAttribute(QStringLiteral("noChangeAspect"), QStringLiteral("1"));
  308. writer.writeEndElement(); //xdr:cNvPicPr
  309. writer.writeEndElement(); //xdr:nvPicPr
  310. m_drawing->relationships()->addDocumentRelationship(QStringLiteral("/image"), QStringLiteral("../media/image%1.%2")
  311. .arg(m_pictureFile->index()+1)
  312. .arg(m_pictureFile->suffix()));
  313. writer.writeStartElement(QStringLiteral("xdr:blipFill"));
  314. writer.writeEmptyElement(QStringLiteral("a:blip"));
  315. writer.writeAttribute(QStringLiteral("xmlns:r"), QStringLiteral("http://schemas.openxmlformats.org/officeDocument/2006/relationships"));
  316. writer.writeAttribute(QStringLiteral("r:embed"), QStringLiteral("rId%1").arg(m_drawing->relationships()->count()));
  317. writer.writeStartElement(QStringLiteral("a:stretch"));
  318. writer.writeEmptyElement(QStringLiteral("a:fillRect"));
  319. writer.writeEndElement(); //a:stretch
  320. writer.writeEndElement();//xdr:blipFill
  321. writer.writeStartElement(QStringLiteral("xdr:spPr"));
  322. writer.writeStartElement(QStringLiteral("a:prstGeom"));
  323. writer.writeAttribute(QStringLiteral("prst"), QStringLiteral("rect"));
  324. writer.writeEmptyElement(QStringLiteral("a:avLst"));
  325. writer.writeEndElement(); //a:prstGeom
  326. writer.writeEndElement(); //xdr:spPr
  327. writer.writeEndElement(); //xdr:pic
  328. }
  329. void DrawingAnchor::saveXmlObjectShape(QXmlStreamWriter &writer) const
  330. {
  331. Q_UNUSED(writer)
  332. }
  333. //absolute anchor
  334. DrawingAbsoluteAnchor::DrawingAbsoluteAnchor(Drawing *drawing, ObjectType objectType)
  335. :DrawingAnchor(drawing, objectType)
  336. {
  337. }
  338. bool DrawingAbsoluteAnchor::loadFromXml(QXmlStreamReader &reader)
  339. {
  340. Q_ASSERT(reader.name() == QLatin1String("absoluteAnchor"));
  341. while (!reader.atEnd()) {
  342. reader.readNextStartElement();
  343. if (reader.tokenType() == QXmlStreamReader::StartElement) {
  344. if (reader.name() == QLatin1String("pos")) {
  345. pos = loadXmlPos(reader);
  346. } else if (reader.name() == QLatin1String("ext")) {
  347. ext = loadXmlExt(reader);
  348. } else {
  349. loadXmlObject(reader);
  350. }
  351. } else if (reader.tokenType() == QXmlStreamReader::EndElement
  352. && reader.name() == QLatin1String("absoluteAnchor")) {
  353. break;
  354. }
  355. }
  356. return true;
  357. }
  358. void DrawingAbsoluteAnchor::saveToXml(QXmlStreamWriter &writer) const
  359. {
  360. writer.writeStartElement(QStringLiteral("xdr:absoluteAnchor"));
  361. saveXmlPos(writer, pos);
  362. saveXmlExt(writer, ext);
  363. saveXmlObject(writer);
  364. writer.writeEmptyElement(QStringLiteral("xdr:clientData"));
  365. writer.writeEndElement(); //xdr:absoluteAnchor
  366. }
  367. //one cell anchor
  368. DrawingOneCellAnchor::DrawingOneCellAnchor(Drawing *drawing, ObjectType objectType)
  369. :DrawingAnchor(drawing, objectType)
  370. {
  371. }
  372. bool DrawingOneCellAnchor::loadFromXml(QXmlStreamReader &reader)
  373. {
  374. Q_ASSERT(reader.name() == QLatin1String("oneCellAnchor"));
  375. while (!reader.atEnd()) {
  376. reader.readNextStartElement();
  377. if (reader.tokenType() == QXmlStreamReader::StartElement) {
  378. if (reader.name() == QLatin1String("from")) {
  379. from = loadXmlMarker(reader, QLatin1String("from"));
  380. } else if (reader.name() == QLatin1String("ext")) {
  381. ext = loadXmlExt(reader);
  382. } else {
  383. loadXmlObject(reader);
  384. }
  385. } else if (reader.tokenType() == QXmlStreamReader::EndElement
  386. && reader.name() == QLatin1String("oneCellAnchor")) {
  387. break;
  388. }
  389. }
  390. return true;
  391. }
  392. void DrawingOneCellAnchor::saveToXml(QXmlStreamWriter &writer) const
  393. {
  394. writer.writeStartElement(QStringLiteral("xdr:oneCellAnchor"));
  395. saveXmlMarker(writer, from, QStringLiteral("xdr:from"));
  396. saveXmlExt(writer, ext);
  397. saveXmlObject(writer);
  398. writer.writeEmptyElement(QStringLiteral("xdr:clientData"));
  399. writer.writeEndElement(); //xdr:oneCellAnchor
  400. }
  401. /*
  402. Two cell anchor
  403. This class specifies a two cell anchor placeholder for a group
  404. , a shape, or a drawing element. It moves with
  405. cells and its extents are in EMU units.
  406. */
  407. DrawingTwoCellAnchor::DrawingTwoCellAnchor(Drawing *drawing, ObjectType objectType)
  408. :DrawingAnchor(drawing, objectType)
  409. {
  410. }
  411. bool DrawingTwoCellAnchor::loadFromXml(QXmlStreamReader &reader)
  412. {
  413. Q_ASSERT(reader.name() == QLatin1String("twoCellAnchor"));
  414. while (!reader.atEnd()) {
  415. reader.readNextStartElement();
  416. if (reader.tokenType() == QXmlStreamReader::StartElement) {
  417. if (reader.name() == QLatin1String("from")) {
  418. from = loadXmlMarker(reader, QLatin1String("from"));
  419. } else if (reader.name() == QLatin1String("to")) {
  420. to = loadXmlMarker(reader, QLatin1String("to"));
  421. } else {
  422. loadXmlObject(reader);
  423. }
  424. } else if (reader.tokenType() == QXmlStreamReader::EndElement
  425. && reader.name() == QLatin1String("twoCellAnchor")) {
  426. break;
  427. }
  428. }
  429. return true;
  430. }
  431. void DrawingTwoCellAnchor::saveToXml(QXmlStreamWriter &writer) const
  432. {
  433. writer.writeStartElement(QStringLiteral("xdr:twoCellAnchor"));
  434. writer.writeAttribute(QStringLiteral("editAs"), QStringLiteral("oneCell"));
  435. saveXmlMarker(writer, from, QStringLiteral("xdr:from"));
  436. saveXmlMarker(writer, to, QStringLiteral("xdr:to"));
  437. saveXmlObject(writer);
  438. writer.writeEmptyElement(QStringLiteral("xdr:clientData"));
  439. writer.writeEndElement(); //xdr:twoCellAnchor
  440. }
  441. } // namespace QXlsx