QtXlsx读写.xlsx的图片
先介绍一下.xlsx 的图片保存的xml文件。解压.xlsx文件后,在 /xl/drawings 目录下有对应sheet的图片.xml文件统一叫 drawingX.xml
- <xdr:wsDr>
- <xdr:twoCellAnchor editAs="oneCell">
- <xdr:from>
- <xdr:col>2</xdr:col>
- <xdr:colOff>0</xdr:colOff>
- <xdr:row>2</xdr:row>
- <xdr:rowOff>165100</xdr:rowOff>
- </xdr:from>
- <xdr:to>
- <xdr:col>4</xdr:col>
- <xdr:colOff>671830</xdr:colOff>
- <xdr:row>8</xdr:row>
- <xdr:rowOff>154305</xdr:rowOff>
- </xdr:to>
- <xdr:pic>
- <xdr:nvPicPr>
- <xdr:cNvPr id="2" name="图片 1" descr="image1"/>
- <xdr:cNvPicPr>
- <a:picLocks noChangeAspect="1"/>
- </xdr:cNvPicPr>
- </xdr:nvPicPr>
- <xdr:blipFill>
- <a:blip r:embed="rId1"/>
- <a:srcRect/>
- <a:stretch>
- <a:fillRect/>
- </a:stretch>
- </xdr:blipFill>
- <xdr:spPr>
- <a:xfrm>
- <a:off x="1371600" y="527050"/>
- <a:ext cx="1662430" cy="1017905"/>
- </a:xfrm>
- <a:prstGeom prst="rect">
- <a:avLst/>
- </a:prstGeom>
- </xdr:spPr>
- </xdr:pic>
- <xdr:clientData/>
- </xdr:twoCellAnchor>
- </xdr:wsDr>
比如这个是我的某一个drawing1.xml文件,其中xdr:twoCellAnchor表示了确定图片位置的方式,.xlsx 共有3中方式,除了前面的还有OneCellAnchor和AbsoluteAnchor。
xdr:from 和 xdr:to 分别是这个twoCellAnchor的两个位置参数。明显,这个twoCellAnchor表示图片位置由两个单元格的位置确认,同理OneCell由一个确定,而AbsoluteAnchor则直接给定坐标
form 和to 标签下有col ,row还有对应的rowoff和coloff是对应的偏移量,现在回过头来看QXlsx
QXlsx写入图片
QXlsx只给了一个插入图片的方法
- bool Document::insertImage(int row, int column, const QImage &image)
这个方法非常简单粗糙,继续看里面的内容
- bool Worksheet::insertImage(int row, int column, const QImage &image)
- {
- Q_D(Worksheet);
-
- if (image.isNull())
- return false;
-
- if (!d->drawing)
- d->drawing = QSharedPointer<Drawing>(new Drawing(this, F_NewFromScratch));
-
- DrawingOneCellAnchor *anchor = new DrawingOneCellAnchor(d->drawing.data(), DrawingAnchor::Picture);
-
-
-
-
-
-
- anchor->from = XlsxMarker(row, column, 0, 0);
- anchor->ext = QSize(image.width() * 9525, image.height() * 9525);
-
- anchor->setObjectPicture(image);
- return true;
- }
实际上只插入了一张 OneCellAnchor的图片,连rowoff 和coloff都直接填的0,这样应该很难满足我们的要求的
- class DrawingAbsoluteAnchor : public DrawingAnchor
- {
- public:
- DrawingAbsoluteAnchor(Drawing *drawing, ObjectType objectType=Unknown);
-
- QPoint pos;
- QSize ext;
-
- bool loadFromXml(QXmlStreamReader &reader);
- void saveToXml(QXmlStreamWriter &writer) const;
- };
-
- class DrawingOneCellAnchor : public DrawingAnchor
- {
- public:
- DrawingOneCellAnchor(Drawing *drawing, ObjectType objectType=Unknown);
-
- XlsxMarker from;
- QSize ext;
-
- bool loadFromXml(QXmlStreamReader &reader);
- void saveToXml(QXmlStreamWriter &writer) const;
- };
-
- class DrawingTwoCellAnchor : public DrawingAnchor
- {
- public:
- DrawingTwoCellAnchor(Drawing *drawing, ObjectType objectType=Unknown);
-
- XlsxMarker from;
- XlsxMarker to;
-
- bool loadFromXml(QXmlStreamReader &reader);
- void saveToXml(QXmlStreamWriter &writer) const;
- };
-
- }
实际上 QXlsx 已经完全定义好了这三种格式的图片,功能都已经完成了,真正有需要的话,可以自己仿照他的方法把剩下的两种写好
2015-12-28 编辑
自行添加了三种方法,在xlsxworksheet.h头文件中,可以参考一下
- bool insertImage(XlsxMarker star_cell, QSize size,const QImage &image);
- bool insertImage(XlsxMarker star_cell, XlsxMarker end_cell,const QImage &image);
- bool insertImage(QPoint star,QSize size, const QImage &image);
分别对应 onecell 、twocell 以及 absolute
- bool Worksheet::insertImage(XlsxMarker star_cell, QSize size, const QImage &image)
- {
- Q_D(Worksheet);
-
- if (image.isNull())
- return false;
-
- if (!d->drawing)
- d->drawing = QSharedPointer<Drawing>(new Drawing(this, F_NewFromScratch));
- DrawingOneCellAnchor *anchor = new DrawingOneCellAnchor(d->drawing.data(), DrawingAnchor::Picture);
- anchor->from = XlsxMarker(star_cell.row(), star_cell.col(), star_cell.rowOff()*9525, star_cell.colOff()*9525);
- anchor->ext = QSize(size.width() * 9525, size.height() * 9525);
-
- anchor->setObjectPicture(image);
- return true;
-
- }
-
- bool Worksheet::insertImage(XlsxMarker star_cell, XlsxMarker end_cell, const QImage &image)
- {
- Q_D(Worksheet);
-
- if (image.isNull())
- return false;
-
- if (!d->drawing)
- d->drawing = QSharedPointer<Drawing>(new Drawing(this, F_NewFromScratch));
- DrawingTwoCellAnchor *anchor = new DrawingTwoCellAnchor(d->drawing.data(), DrawingAnchor::Picture);
- anchor->from = XlsxMarker(star_cell.row(), star_cell.col(), star_cell.rowOff()*9525, star_cell.colOff()*9525);
- anchor->to = XlsxMarker(end_cell.row(), end_cell.col(), end_cell.rowOff()*9525, end_cell.colOff()*9525);
-
- anchor->setObjectPicture(image);
- return true;
-
- }
-
- bool Worksheet::insertImage(QPoint star, QSize size, const QImage &image)
- {
- Q_D(Worksheet);
-
- if (image.isNull())
- return false;
-
- if (!d->drawing)
- d->drawing = QSharedPointer<Drawing>(new Drawing(this, F_NewFromScratch));
- DrawingAbsoluteAnchor *anchor = new DrawingAbsoluteAnchor(d->drawing.data(), DrawingAnchor::Picture);
- anchor->pos=star*9525;
- anchor->ext=size*9525;
- anchor->setObjectPicture(image);
- return true;
-
- }
这里传入的都是对应的界面值,也就是 pix 单位的参数,所以存入的参数都乘以了 9525 需要注意一下
QXlsx读取图片
读取图片,QXlsx完全就没有提供接口,只能靠自己完成了。实际上,QXlsx已经读取了图片信息
- for (int i=0; i<workbook->drawings().size(); ++i) {
- Drawing *drawing = workbook->drawings()[i];
- QString rel_path = getRelFilePath(drawing->filePath());
- if (zipReader.filePaths().contains(rel_path))
- drawing->relationships()->loadFromXmlData(zipReader.fileData(rel_path));
- drawing->loadFromXmlData(zipReader.fileData(drawing->filePath()));
- }
所以,稍微修改源码,我们也能提取到里面的图片
- foreach(QString sheetName,xls_doc->sheetNames())
- {
- Worksheet *sheet = static_cast<Worksheet *>(xls_doc->sheet(sheetName));
- if(sheet)
- {
- Drawing *drawing = sheet->drawing();
- if(drawing)
- {
- qDebug()<<drawing->anchors.count();
- for(int j=0;j<drawing->anchors.count();j++)
- {
- DrawingAnchor *my_anchor=drawing->anchors.at(j);
- MediaFile* m_mediafile=my_anchor->m_pictureFile.data();
- QImage image;
- QByteArray contents=m_mediafile->m_contents;
-
- if(my_anchor->DrawingName=="DrawingTwoCellAnchor")
- {
- DrawingTwoCellAnchor* cellanchor=static_cast<DrawingTwoCellAnchor*>(my_anchor);
-
-
-
- }
- if(my_anchor->DrawingName=="DrawingOneCellAnchor")
- {
- DrawingOneCellAnchor* cellanchor=static_cast<DrawingOneCellAnchor*>(my_anchor);
- }
- if(my_anchor->DrawingName=="DrawingAbsoluteAnchor")
- {
- DrawingAbsoluteAnchor* cellanchor=static_cast<DrawingAbsoluteAnchor*>(my_anchor);
- }
-
-
- }
-
- }
-
- }
- }
sheet->drawing();是一个私有的方法,需要在源码中修改一下。
这里我为了分清3中派生的类自己添加了一个 DrawingName
sheet 下面的drawing 有个一个叫 anchors 的QList
- class Drawing : public AbstractOOXmlFile
- {
- public:
- Drawing(AbstractSheet *sheet, CreateFlag flag);
- ~Drawing();
- void saveToXmlFile(QIODevice *device) const;
- bool loadFromXmlFile(QIODevice *device);
-
- AbstractSheet *sheet;
- Workbook *workbook;
- QList<DrawingAnchor *> anchors;
- };
我们需要的就是从DrawingAnchor *中得到
- QSharedPointer<MediaFile> m_pictureFile;
- QByteArray MediaFile::contents() const
- {
- return m_contents;
- }
这就是我们需要的图片信息了
- new_image=QImage::fromData(cellanchor->m_pictureFile.data()->contents());
这样就可以得到我们需要的图片,而位置信息可以自行从上述的3个类中寻找