Browse Source

Code refactoring: Improve picture support

Don't lost picture information when edit existing .xlsx files
master
Debao Zhang 11 years ago
parent
commit
24c26133b4
  1. 10
      examples/xlsx/image/main.cpp
  2. 8
      src/xlsx/qtxlsx.pri
  3. 60
      src/xlsx/xlsxdocument.cpp
  4. 174
      src/xlsx/xlsxdrawing.cpp
  5. 43
      src/xlsx/xlsxdrawing_p.h
  6. 457
      src/xlsx/xlsxdrawinganchor.cpp
  7. 148
      src/xlsx/xlsxdrawinganchor_p.h
  8. 99
      src/xlsx/xlsxmediafile.cpp
  9. 68
      src/xlsx/xlsxmediafile_p.h
  10. 66
      src/xlsx/xlsxworkbook.cpp
  11. 7
      src/xlsx/xlsxworkbook.h
  12. 3
      src/xlsx/xlsxworkbook_p.h
  13. 188
      src/xlsx/xlsxworksheet.cpp
  14. 10
      src/xlsx/xlsxworksheet.h
  15. 66
      src/xlsx/xlsxworksheet_p.h

10
examples/xlsx/image/main.cpp

@ -6,12 +6,14 @@ int main(int argc, char** argv)
QGuiApplication(argc, argv);
QXlsx::Document xlsx;
QImage image(400, 300, QImage::Format_RGB32);
QImage image(40, 30, QImage::Format_RGB32);
image.fill(Qt::green);
xlsx.insertImage(5, 5, image);
for (int i=0; i<10; ++i)
xlsx.insertImage(10*i, 5, image);
xlsx.saveAs("Book1.xlsx");
xlsx.save();
QXlsx::Document xlsx2("Book1.xlsx");
xlsx2.saveAs("Book2.xlsx");
return 0;
}

8
src/xlsx/qtxlsx.pri

@ -34,7 +34,9 @@ HEADERS += $$PWD/xlsxdocpropscore_p.h \
$$PWD/xlsxconditionalformatting.h \
$$PWD/xlsxconditionalformatting_p.h \
$$PWD/xlsxcolor_p.h \
$$PWD/xlsxnumformatparser_p.h
$$PWD/xlsxnumformatparser_p.h \
$$PWD/xlsxdrawinganchor_p.h \
$$PWD/xlsxmediafile_p.h
SOURCES += $$PWD/xlsxdocpropscore.cpp \
$$PWD/xlsxdocpropsapp.cpp \
@ -57,4 +59,6 @@ SOURCES += $$PWD/xlsxdocpropscore.cpp \
$$PWD/xlsxrichstring.cpp \
$$PWD/xlsxconditionalformatting.cpp \
$$PWD/xlsxcolor.cpp \
$$PWD/xlsxnumformatparser.cpp
$$PWD/xlsxnumformatparser.cpp \
$$PWD/xlsxdrawinganchor.cpp \
$$PWD/xlsxmediafile.cpp

60
src/xlsx/xlsxdocument.cpp

@ -37,12 +37,14 @@
#include "xlsxutility_p.h"
#include "xlsxworkbook_p.h"
#include "xlsxdrawing_p.h"
#include "xlsxmediafile_p.h"
#include "xlsxzipreader_p.h"
#include "xlsxzipwriter_p.h"
#include <QFile>
#include <QPointF>
#include <QBuffer>
#include <QDir>
QT_BEGIN_NAMESPACE_XLSX
@ -189,6 +191,27 @@ bool DocumentPrivate::loadPackage(QIODevice *device)
if (zipReader.filePaths().contains(rel_path))
sheet->relationships().loadFromXmlData(zipReader.fileData(rel_path));
sheet->loadFromXmlData(zipReader.fileData(worksheet_path));
//load drawing if exists
if (!sheet->drawingPath().isEmpty()) {
QString drawingPath = QDir::cleanPath(splitPath(worksheet_path)[0] + QLatin1String("/") + sheet->drawingPath());
Drawing *drawing = new Drawing(workbook.data());
drawing->pathInPackage = drawingPath;
QString drawing_rel_path = getRelFilePath(drawingPath);
if (zipReader.filePaths().contains(drawing_rel_path))
drawing->relationships.loadFromXmlData(zipReader.fileData(drawing_rel_path));
drawing->loadFromXmlData(zipReader.fileData(drawingPath));
sheet->setDrawing(drawing);
}
}
//load media files
QList<QSharedPointer<MediaFile> > mediaFileToLoad = workbook->mediaFiles();
for (int i=0; i<mediaFileToLoad.size(); ++i) {
QSharedPointer<MediaFile> mf = mediaFileToLoad[i];
const QString path = mf->fileName();
const QString suffix = path.mid(path.lastIndexOf(QLatin1Char('.'))+1);
mf->set(zipReader.fileData(path), suffix);
}
return true;
@ -206,9 +229,6 @@ bool DocumentPrivate::savePackage(QIODevice *device) const
DocPropsApp docPropsApp;
DocPropsCore docPropsCore;
//: Todo
workbook->prepareDrawings();
// save worksheet xml files
for (int i=0; i<workbook->worksheetCount(); ++i) {
Worksheet *sheet = workbook->worksheet(i);
@ -232,19 +252,8 @@ bool DocumentPrivate::savePackage(QIODevice *device) const
Drawing *drawing = workbook->drawings()[i];
zipWriter.addFile(QStringLiteral("xl/drawings/drawing%1.xml").arg(i+1), drawing->saveToXmlData());
}
for (int i=0; i<workbook->worksheetCount(); ++i) {
Worksheet *sheet = workbook->worksheet(i);
if (sheet->drawingLinks().size() == 0)
continue;
Relationships rels;
typedef QPair<QString, QString> PairType;
foreach (PairType pair, sheet->drawingLinks())
rels.addDocumentRelationship(pair.first, pair.second);
zipWriter.addFile(QStringLiteral("xl/drawings/_rels/drawing%1.xml.rels").arg(i+1), rels.saveToXmlData());
if (!drawing->relationships.isEmpty())
zipWriter.addFile(QStringLiteral("xl/drawings/_rels/drawing%1.xml.rels").arg(i+1), drawing->relationships.saveToXmlData());
}
// save docProps app/core xml file
@ -274,17 +283,12 @@ bool DocumentPrivate::savePackage(QIODevice *device) const
zipWriter.addFile(QStringLiteral("xl/theme/theme1.xml"), workbook->theme()->saveToXmlData());
// save image files
if (!workbook->images().isEmpty())
contentTypes.addDefault(QStringLiteral("png"), QStringLiteral("image/png"));
for (int i=0; i<workbook->images().size(); ++i) {
QImage image = workbook->images()[i];
for (int i=0; i<workbook->mediaFiles().size(); ++i) {
QSharedPointer<MediaFile> mf = workbook->mediaFiles()[i];
if (!mf->mimeType().isEmpty())
contentTypes.addDefault(mf->suffix(), mf->mimeType());
QByteArray data;
QBuffer buffer(&data);
buffer.open(QIODevice::WriteOnly);
image.save(&buffer, "png");
zipWriter.addFile(QStringLiteral("xl/media/image%1.png").arg(i+1), data);
zipWriter.addFile(QStringLiteral("xl/media/image%1.%2").arg(i+1).arg(mf->suffix()), mf->contents());
}
// save root .rels xml file
@ -388,9 +392,9 @@ QVariant Document::read(int row, int col) const
* \brief Insert an \a image to current active worksheet to the position \a row, \a column with the given
* \a xOffset, \a yOffset, \a xScale and \a yScale.
*/
int Document::insertImage(int row, int column, const QImage &image, double xOffset, double yOffset, double xScale, double yScale)
int Document::insertImage(int row, int column, const QImage &image, double /*xOffset*/, double /*yOffset*/, double /*xScale*/, double /*yScale*/)
{
return currentWorksheet()->insertImage(row, column, image, QPointF(xOffset, yOffset), xScale, yScale);
return currentWorksheet()->insertImage(row, column, image);
}
/*!

174
src/xlsx/xlsxdrawing.cpp

@ -24,6 +24,7 @@
****************************************************************************/
#include "xlsxdrawing_p.h"
#include "xlsxdrawinganchor_p.h"
#include <QXmlStreamWriter>
#include <QXmlStreamReader>
@ -31,10 +32,15 @@
namespace QXlsx {
Drawing::Drawing()
Drawing::Drawing(Workbook *workbook)
:workbook(workbook)
{
embedded = false;
orientation = 0;
}
Drawing::~Drawing()
{
qDeleteAll(anchors);
}
QByteArray Drawing::saveToXmlData() const
@ -48,6 +54,8 @@ QByteArray Drawing::saveToXmlData() const
void Drawing::saveToXmlFile(QIODevice *device) const
{
relationships.clear();
QXmlStreamWriter writer(device);
writer.writeStartDocument(QStringLiteral("1.0"), true);
@ -55,156 +63,42 @@ void Drawing::saveToXmlFile(QIODevice *device) const
writer.writeAttribute(QStringLiteral("xmlns:xdr"), QStringLiteral("http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing"));
writer.writeAttribute(QStringLiteral("xmlns:a"), QStringLiteral("http://schemas.openxmlformats.org/drawingml/2006/main"));
if (embedded) {
int index = 1;
foreach (XlsxDrawingDimensionData *dimension, dimensionList) {
writeTwoCellAnchor(writer, index, dimension);
index++;
}
} else {
//write the xdr:absoluteAnchor element
writeAbsoluteAnchor(writer, 1);
}
foreach (DrawingAnchor *anchor, anchors)
anchor->saveToXml(writer);
writer.writeEndElement();//xdr:wsDr
writer.writeEndDocument();
}
void Drawing::writeTwoCellAnchor(QXmlStreamWriter &writer, int index, XlsxDrawingDimensionData *data) const
bool Drawing::loadFromXmlFile(QIODevice *device)
{
writer.writeStartElement(QStringLiteral("xdr:twoCellAnchor"));
if (data->drawing_type == 2)
writer.writeAttribute(QStringLiteral("editAs"), QStringLiteral("oneCell"));
// if (shape)
// writer.writeAttribute(QStringLiteral("editAs"), );
writer.writeStartElement(QStringLiteral("xdr:from"));
writer.writeTextElement(QStringLiteral("xdr:col"), QString::number(data->col_from));
writer.writeTextElement(QStringLiteral("xdr:colOff"), QString::number((int)data->col_from_offset));
writer.writeTextElement(QStringLiteral("xdr:row"), QString::number(data->row_from));
writer.writeTextElement(QStringLiteral("xdr:rowOff"), QString::number((int)data->row_from_offset));
writer.writeEndElement(); //xdr:from
writer.writeStartElement(QStringLiteral("xdr:to"));
writer.writeTextElement(QStringLiteral("xdr:col"), QString::number(data->col_to));
writer.writeTextElement(QStringLiteral("xdr:colOff"), QString::number((int)data->col_to_offset));
writer.writeTextElement(QStringLiteral("xdr:row"), QString::number(data->row_to));
writer.writeTextElement(QStringLiteral("xdr:rowOff"), QString::number((int)data->row_to_offset));
writer.writeEndElement(); //xdr:to
if (data->drawing_type == 1) {
//Graphics frame, xdr:graphicFrame
writeGraphicFrame(writer, index, data->description);
} else if (data->drawing_type == 2) {
//Image, xdr:pic
writePicture(writer, index, data->col_absolute, data->row_absolute, data->width, data->height, data->description);
} else {
//Shape, xdr:sp
QXmlStreamReader reader(device);
while (!reader.atEnd()) {
reader.readNextStartElement();
if (reader.tokenType() == QXmlStreamReader::StartElement) {
if (reader.name() == QLatin1String("absoluteAnchor")) {
DrawingAbsoluteAnchor * anchor = new DrawingAbsoluteAnchor(this);
anchor->loadFromXml(reader);
} else if (reader.name() == QLatin1String("oneCellAnchor")) {
DrawingOneCellAnchor * anchor = new DrawingOneCellAnchor(this);
anchor->loadFromXml(reader);
} else if (reader.name() == QLatin1String("twoCellAnchor")) {
DrawingTwoCellAnchor * anchor = new DrawingTwoCellAnchor(this);
anchor->loadFromXml(reader);
}
writer.writeEmptyElement(QStringLiteral("xdr:clientData"));
writer.writeEndElement(); //xdr:twoCellAnchor
}
void Drawing::writeAbsoluteAnchor(QXmlStreamWriter &writer, int index) const
{
writer.writeStartElement(QStringLiteral("xdr:absoluteAnchor"));
if (orientation == 0) {
writePos(writer, 0, 0);
writeExt(writer, 9308969, 6078325);
} else {
writePos(writer, 0, -47625);
writeExt(writer, 6162675, 6124575);
}
writeGraphicFrame(writer, index);
writer.writeEmptyElement(QStringLiteral("xdr:clientData"));
writer.writeEndElement(); //xdr:absoluteAnchor
}
void Drawing::writePos(QXmlStreamWriter &writer, int x, int y) const
{
writer.writeEmptyElement(QStringLiteral("xdr:pos"));
writer.writeAttribute(QStringLiteral("x"), QString::number(x));
writer.writeAttribute(QStringLiteral("y"), QString::number(y));
}
void Drawing::writeExt(QXmlStreamWriter &writer, int cx, int cy) const
{
writer.writeStartElement(QStringLiteral("xdr:ext"));
writer.writeAttribute(QStringLiteral("cx"), QString::number(cx));
writer.writeAttribute(QStringLiteral("cy"), QString::number(cy));
}
void Drawing::writeGraphicFrame(QXmlStreamWriter &writer, int index, const QString &name) const
{
writer.writeStartElement(QStringLiteral("xdr:graphicFrame"));
writer.writeAttribute(QStringLiteral("macro"), QString());
writer.writeStartElement(QStringLiteral("xdr:nvGraphicFramePr"));
writer.writeEmptyElement(QStringLiteral("xdr:cNvPr"));
writer.writeAttribute(QStringLiteral("id"), QString::number(index+1));
writer.writeAttribute(QStringLiteral("name"), name.isEmpty() ? QStringLiteral("Chart%1").arg(index): name);
if (embedded) {
writer.writeEmptyElement(QStringLiteral("xdr:cNvGraphicFramePr"));
} else {
writer.writeStartElement(QStringLiteral("xdr:cNvGraphicFramePr"));
writer.writeEmptyElement(QStringLiteral("a:graphicFrameLocks"));
writer.writeAttribute(QStringLiteral("noGrp"), QStringLiteral("1"));
writer.writeEndElement(); //xdr:cNvGraphicFramePr
}
writer.writeEndElement();//xdr:nvGraphicFramePr
writer.writeEndElement(); //xdr:graphicFrame
return true;
}
void Drawing::writePicture(QXmlStreamWriter &writer, int index, double col_abs, double row_abs, int width, int height, const QString &description) const
bool Drawing::loadFromXmlData(const QByteArray &data)
{
writer.writeStartElement(QStringLiteral("xdr:pic"));
writer.writeStartElement(QStringLiteral("xdr:nvPicPr"));
writer.writeEmptyElement(QStringLiteral("xdr:cNvPr"));
writer.writeAttribute(QStringLiteral("id"), QString::number(index+1));
writer.writeAttribute(QStringLiteral("name"), QStringLiteral("Picture%1").arg(index));
if (!description.isEmpty())
writer.writeAttribute(QStringLiteral("descr"), description);
writer.writeStartElement(QStringLiteral("xdr:cNvPicPr"));
writer.writeEmptyElement(QStringLiteral("a:picLocks"));
writer.writeAttribute(QStringLiteral("noChangeAspect"), QStringLiteral("1"));
writer.writeEndElement(); //xdr:cNvPicPr
writer.writeEndElement(); //xdr:nvPicPr
writer.writeStartElement(QStringLiteral("xdr:blipFill"));
writer.writeEmptyElement(QStringLiteral("a:blip"));
writer.writeAttribute(QStringLiteral("xmlns:r"), QStringLiteral("http://schemas.openxmlformats.org/officeDocument/2006/relationships"));
writer.writeAttribute(QStringLiteral("r:embed"), QStringLiteral("rId%1").arg(index));
writer.writeStartElement(QStringLiteral("a:stretch"));
writer.writeEmptyElement(QStringLiteral("a:fillRect"));
writer.writeEndElement(); //a:stretch
writer.writeEndElement();//xdr:blipFill
writer.writeStartElement(QStringLiteral("xdr:spPr"));
writer.writeStartElement(QStringLiteral("a:xfrm"));
writer.writeEmptyElement(QStringLiteral("a:off"));
writer.writeAttribute(QStringLiteral("x"), QString::number((int)col_abs));
writer.writeAttribute(QStringLiteral("y"), QString::number((int)row_abs));
writer.writeEmptyElement(QStringLiteral("a:ext"));
writer.writeAttribute(QStringLiteral("cx"), QString::number(width));
writer.writeAttribute(QStringLiteral("cy"), QString::number(height));
writer.writeEndElement(); //a:xfrm
writer.writeStartElement(QStringLiteral("a:prstGeom"));
writer.writeAttribute(QStringLiteral("prst"), QStringLiteral("rect"));
writer.writeEmptyElement(QStringLiteral("a:avLst"));
writer.writeEndElement(); //a:prstGeom
writer.writeEndElement(); //xdr:spPr
writer.writeEndElement(); //xdr:pic
QBuffer buffer;
buffer.setData(data);
buffer.open(QIODevice::ReadOnly);
return loadFromXmlFile(&buffer);
}
} // namespace QXlsx

43
src/xlsx/xlsxdrawing_p.h

@ -37,51 +37,36 @@
// We mean it.
//
#include "xlsxrelationships_p.h"
#include <QList>
#include <QString>
#include <QSharedPointer>
class QIODevice;
class QXmlStreamWriter;
namespace QXlsx {
struct XlsxDrawingDimensionData
{
int drawing_type;
int col_from;
int row_from;
double col_from_offset;
double row_from_offset;
int col_to;
int row_to;
double col_to_offset;
double row_to_offset;
int col_absolute;
int row_absolute;
int width;
int height;
QString description;
int shape;
};
class DrawingAnchor;
class Workbook;
class MediaFile;
class Drawing
{
public:
Drawing();
Drawing(Workbook *workbook);
~Drawing();
void saveToXmlFile(QIODevice *device) const;
QByteArray saveToXmlData() const;
bool loadFromXmlFile(QIODevice *device);
bool loadFromXmlData(const QByteArray &data);
bool embedded;
int orientation;
QList <XlsxDrawingDimensionData *> dimensionList;
Workbook *workbook;
QList<DrawingAnchor *> anchors;
mutable Relationships relationships;
private:
void writeTwoCellAnchor(QXmlStreamWriter &writer, int index, XlsxDrawingDimensionData *data) const;
void writeAbsoluteAnchor(QXmlStreamWriter &writer, int index) const;
void writePos(QXmlStreamWriter &writer, int x, int y) const;
void writeExt(QXmlStreamWriter &writer, int cx, int cy) const;
void writeGraphicFrame(QXmlStreamWriter &writer, int index, const QString &name=QString()) const;
void writePicture(QXmlStreamWriter &writer, int index, double col_abs, double row_abs, int width, int height, const QString &description) const;
QString pathInPackage;
};
} // namespace QXlsx

457
src/xlsx/xlsxdrawinganchor.cpp

@ -0,0 +1,457 @@
/****************************************************************************
** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me>
** All right reserved.
**
** Permission is hereby granted, free of charge, to any person obtaining
** a copy of this software and associated documentation files (the
** "Software"), to deal in the Software without restriction, including
** without limitation the rights to use, copy, modify, merge, publish,
** distribute, sublicense, and/or sell copies of the Software, and to
** permit persons to whom the Software is furnished to do so, subject to
** the following conditions:
**
** The above copyright notice and this permission notice shall be
** included in all copies or substantial portions of the Software.
**
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
**
****************************************************************************/
#include "xlsxdrawinganchor_p.h"
#include "xlsxdrawing_p.h"
#include "xlsxmediafile_p.h"
#include "xlsxworkbook.h"
#include "xlsxutility_p.h"
#include <QXmlStreamReader>
#include <QXmlStreamWriter>
#include <QBuffer>
#include <QDir>
namespace QXlsx {
/*
The vertices that define the position of a graphical object
within the worksheet in pixels.
+------------+------------+
| A | B |
+-----+------------+------------+
| |(x1,y1) | |
| 1 |(A1)._______|______ |
| | | | |
| | | | |
+-----+----| OBJECT |-----+
| | | | |
| 2 | |______________. |
| | | (B2)|
| | | (x2,y2)|
+---- +------------+------------+
Example of an object that covers some of the area from cell A1 to B2.
Based on the width and height of the object we need to calculate 8 vars:
col_start, row_start, col_end, row_end, x1, y1, x2, y2.
We also calculate the absolute x and y position of the top left vertex of
the object. This is required for images.
The width and height of the cells that the object occupies can be
variable and have to be taken into account.
*/
//anchor
DrawingAnchor::DrawingAnchor(Drawing *drawing, ObjectType objectType)
:m_drawing(drawing), m_objectType(objectType)
{
m_drawing->anchors.append(this);
m_id = m_drawing->anchors.size();//must be unique in one drawing{x}.xml file.
}
DrawingAnchor::~DrawingAnchor()
{
}
void DrawingAnchor::setObjectPicture(const QImage &img)
{
QByteArray ba;
QBuffer buffer(&ba);
buffer.open(QIODevice::WriteOnly);
img.save(&buffer, "PNG");
m_pictureFile = QSharedPointer<MediaFile>(new MediaFile(ba, QStringLiteral("png"), QStringLiteral("image/png")));
m_drawing->workbook->addMediaFile(m_pictureFile);
}
QPoint DrawingAnchor::loadXmlPos(QXmlStreamReader &reader)
{
Q_ASSERT(reader.name() == QLatin1String("pos"));
QPoint pos;
QXmlStreamAttributes attrs = reader.attributes();
pos.setX(attrs.value(QLatin1String("x")).toString().toInt());
pos.setY(attrs.value(QLatin1String("y")).toString().toInt());
return pos;
}
QSize DrawingAnchor::loadXmlExt(QXmlStreamReader &reader)
{
Q_ASSERT(reader.name() == QLatin1String("ext"));
QSize size;
QXmlStreamAttributes attrs = reader.attributes();
size.setWidth(attrs.value(QLatin1String("cx")).toString().toInt());
size.setHeight(attrs.value(QLatin1String("cy")).toString().toInt());
return size;
}
XlsxMarker DrawingAnchor::loadXmlMarker(QXmlStreamReader &reader, const QString &node)
{
Q_ASSERT(reader.name() == node);
int col, colOffset, row, rowOffset;
while (!reader.atEnd()) {
reader.readNextStartElement();
if (reader.tokenType() == QXmlStreamReader::StartElement) {
if (reader.name() == QLatin1String("col")) {
col = reader.readElementText().toInt();
} else if (reader.name() == QLatin1String("colOff")) {
colOffset = reader.readElementText().toInt();
} else if (reader.name() == QLatin1String("row")) {
row = reader.readElementText().toInt();
} else if (reader.name() == QLatin1String("rowOff")) {
rowOffset = reader.readElementText().toInt();
}
} else if (reader.tokenType() == QXmlStreamReader::EndElement
&& reader.name() == node) {
break;
}
}
return XlsxMarker(row, col, rowOffset, colOffset);
}
void DrawingAnchor::loadXmlObject(QXmlStreamReader &reader)
{
if (reader.name() == QLatin1String("sp")) {
//Shape
m_objectType = Shape;
loadXmlObjectShape(reader);
} else if (reader.name() == QLatin1String("grpSp")) {
//Group Shape
m_objectType = GroupShape;
loadXmlObjectGroupShape(reader);
} else if (reader.name() == QLatin1String("graphicFrame")) {
//Graphic Frame
m_objectType = GraphicFrame;
loadXmlObjectGraphicFrame(reader);
} else if (reader.name() == QLatin1String("cxnSp")) {
//Connection Shape
m_objectType = ConnectionShape;
loadXmlObjectConnectionShape(reader);
} else if (reader.name() == QLatin1String("pic")) {
//Picture
m_objectType = Picture;
loadXmlObjectPicture(reader);
}
}
void DrawingAnchor::loadXmlObjectConnectionShape(QXmlStreamReader &reader)
{
Q_UNUSED(reader)
}
void DrawingAnchor::loadXmlObjectGraphicFrame(QXmlStreamReader &reader)
{
Q_UNUSED(reader)
}
void DrawingAnchor::loadXmlObjectGroupShape(QXmlStreamReader &reader)
{
Q_UNUSED(reader)
}
void DrawingAnchor::loadXmlObjectPicture(QXmlStreamReader &reader)
{
Q_ASSERT(reader.name() == QLatin1String("pic"));
while (!reader.atEnd()) {
reader.readNextStartElement();
if (reader.tokenType() == QXmlStreamReader::StartElement) {
if (reader.name() == QLatin1String("blip")) {
QString rId = reader.attributes().value(QLatin1String("r:embed")).toString();
QString name = m_drawing->relationships.getRelationshipById(rId).target;
QString path = QDir::cleanPath(splitPath(m_drawing->pathInPackage)[0] + QLatin1String("/") + name);
bool exist = false;
QList<QSharedPointer<MediaFile> > mfs = m_drawing->workbook->mediaFiles();
for (int i=0; i<mfs.size(); ++i) {
if (mfs[i]->fileName() == path) {
//already exist
exist = true;
m_pictureFile = mfs[i];
}
}
if (!exist) {
m_pictureFile = QSharedPointer<MediaFile> (new MediaFile(path));
m_drawing->workbook->addMediaFile(m_pictureFile, true);
}
}
} else if (reader.tokenType() == QXmlStreamReader::EndElement
&& reader.name() == QLatin1String("pic")) {
break;
}
}
return;
}
void DrawingAnchor::loadXmlObjectShape(QXmlStreamReader &reader)
{
Q_UNUSED(reader)
}
void DrawingAnchor::saveXmlPos(QXmlStreamWriter &writer, const QPoint &pos) const
{
writer.writeEmptyElement(QStringLiteral("xdr:pos"));
writer.writeAttribute(QStringLiteral("x"), QString::number(pos.x()));
writer.writeAttribute(QStringLiteral("y"), QString::number(pos.y()));
}
void DrawingAnchor::saveXmlExt(QXmlStreamWriter &writer, const QSize &ext) const
{
writer.writeStartElement(QStringLiteral("xdr:ext"));
writer.writeAttribute(QStringLiteral("cx"), QString::number(ext.width()));
writer.writeAttribute(QStringLiteral("cy"), QString::number(ext.height()));
writer.writeEndElement(); //xdr:ext
}
void DrawingAnchor::saveXmlMarker(QXmlStreamWriter &writer, const XlsxMarker &marker, const QString &node) const
{
writer.writeStartElement(node); //xdr:from or xdr:to
writer.writeTextElement(QStringLiteral("xdr:col"), QString::number(marker.col()));
writer.writeTextElement(QStringLiteral("xdr:colOff"), QString::number(marker.colOff()));
writer.writeTextElement(QStringLiteral("xdr:row"), QString::number(marker.row()));
writer.writeTextElement(QStringLiteral("xdr:rowOff"), QString::number(marker.rowOff()));
writer.writeEndElement();
}
void DrawingAnchor::saveXmlObject(QXmlStreamWriter &writer) const
{
if (m_objectType == Picture)
saveXmlObjectPicture(writer);
else if (m_objectType == ConnectionShape)
saveXmlObjectConnectionShape(writer);
else if (m_objectType == GraphicFrame)
saveXmlObjectGraphicFrame(writer);
else if (m_objectType == GroupShape)
saveXmlObjectGroupShape(writer);
else if (m_objectType == Shape)
saveXmlObjectShape(writer);
}
void DrawingAnchor::saveXmlObjectConnectionShape(QXmlStreamWriter &writer) const
{
Q_UNUSED(writer)
}
void DrawingAnchor::saveXmlObjectGraphicFrame(QXmlStreamWriter &writer) const
{
Q_UNUSED(writer)
}
void DrawingAnchor::saveXmlObjectGroupShape(QXmlStreamWriter &writer) const
{
Q_UNUSED(writer)
}
void DrawingAnchor::saveXmlObjectPicture(QXmlStreamWriter &writer) const
{
Q_ASSERT(m_objectType == Picture && !m_pictureFile.isNull());
writer.writeStartElement(QStringLiteral("xdr:pic"));
writer.writeStartElement(QStringLiteral("xdr:nvPicPr"));
writer.writeEmptyElement(QStringLiteral("xdr:cNvPr"));
writer.writeAttribute(QStringLiteral("id"), QString::number(m_id));
writer.writeAttribute(QStringLiteral("name"), QStringLiteral("Picture %1").arg(m_id));
writer.writeStartElement(QStringLiteral("xdr:cNvPicPr"));
writer.writeEmptyElement(QStringLiteral("a:picLocks"));
writer.writeAttribute(QStringLiteral("noChangeAspect"), QStringLiteral("1"));
writer.writeEndElement(); //xdr:cNvPicPr
writer.writeEndElement(); //xdr:nvPicPr
m_drawing->relationships.addDocumentRelationship(QStringLiteral("/image"), QStringLiteral("../media/image%1.%2")
.arg(m_pictureFile->index()+1)
.arg(m_pictureFile->suffix()));
writer.writeStartElement(QStringLiteral("xdr:blipFill"));
writer.writeEmptyElement(QStringLiteral("a:blip"));
writer.writeAttribute(QStringLiteral("xmlns:r"), QStringLiteral("http://schemas.openxmlformats.org/officeDocument/2006/relationships"));
writer.writeAttribute(QStringLiteral("r:embed"), QStringLiteral("rId%1").arg(m_drawing->relationships.count()));
writer.writeStartElement(QStringLiteral("a:stretch"));
writer.writeEmptyElement(QStringLiteral("a:fillRect"));
writer.writeEndElement(); //a:stretch
writer.writeEndElement();//xdr:blipFill
writer.writeStartElement(QStringLiteral("xdr:spPr"));
writer.writeStartElement(QStringLiteral("a:prstGeom"));
writer.writeAttribute(QStringLiteral("prst"), QStringLiteral("rect"));
writer.writeEmptyElement(QStringLiteral("a:avLst"));
writer.writeEndElement(); //a:prstGeom
writer.writeEndElement(); //xdr:spPr
writer.writeEndElement(); //xdr:pic
}
void DrawingAnchor::saveXmlObjectShape(QXmlStreamWriter &writer) const
{
Q_UNUSED(writer)
}
//absolute anchor
DrawingAbsoluteAnchor::DrawingAbsoluteAnchor(Drawing *drawing, ObjectType objectType)
:DrawingAnchor(drawing, objectType)
{
}
bool DrawingAbsoluteAnchor::loadFromXml(QXmlStreamReader &reader)
{
Q_ASSERT(reader.name() == QLatin1String("absoluteAnchor"));
while (!reader.atEnd()) {
reader.readNextStartElement();
if (reader.tokenType() == QXmlStreamReader::StartElement) {
if (reader.name() == QLatin1String("pos")) {
pos = loadXmlPos(reader);
} else if (reader.name() == QLatin1String("ext")) {
ext = loadXmlExt(reader);
} else {
loadXmlObject(reader);
}
} else if (reader.tokenType() == QXmlStreamReader::EndElement
&& reader.name() == QLatin1String("absoluteAnchor")) {
break;
}
}
return true;
}
void DrawingAbsoluteAnchor::saveToXml(QXmlStreamWriter &writer) const
{
writer.writeStartElement(QStringLiteral("xdr:absoluteAnchor"));
saveXmlPos(writer, pos);
saveXmlExt(writer, ext);
saveXmlObject(writer);
writer.writeEmptyElement(QStringLiteral("xdr:clientData"));
writer.writeEndElement(); //xdr:absoluteAnchor
}
//one cell anchor
DrawingOneCellAnchor::DrawingOneCellAnchor(Drawing *drawing, ObjectType objectType)
:DrawingAnchor(drawing, objectType)
{
}
bool DrawingOneCellAnchor::loadFromXml(QXmlStreamReader &reader)
{
Q_ASSERT(reader.name() == QLatin1String("oneCellAnchor"));
while (!reader.atEnd()) {
reader.readNextStartElement();
if (reader.tokenType() == QXmlStreamReader::StartElement) {
if (reader.name() == QLatin1String("from")) {
from = loadXmlMarker(reader, QLatin1String("from"));
} else if (reader.name() == QLatin1String("ext")) {
ext = loadXmlExt(reader);
} else {
loadXmlObject(reader);
}
} else if (reader.tokenType() == QXmlStreamReader::EndElement
&& reader.name() == QLatin1String("oneCellAnchor")) {
break;
}
}
return true;
}
void DrawingOneCellAnchor::saveToXml(QXmlStreamWriter &writer) const
{
writer.writeStartElement(QStringLiteral("xdr:oneCellAnchor"));
saveXmlMarker(writer, from, QStringLiteral("xdr:from"));
saveXmlExt(writer, ext);
saveXmlObject(writer);
writer.writeEmptyElement(QStringLiteral("xdr:clientData"));
writer.writeEndElement(); //xdr:oneCellAnchor
}
/*
Two cell anchor
This class specifies a two cell anchor placeholder for a group
, a shape, or a drawing element. It moves with
cells and its extents are in EMU units.
*/
DrawingTwoCellAnchor::DrawingTwoCellAnchor(Drawing *drawing, ObjectType objectType)
:DrawingAnchor(drawing, objectType)
{
}
bool DrawingTwoCellAnchor::loadFromXml(QXmlStreamReader &reader)
{
Q_ASSERT(reader.name() == QLatin1String("twoCellAnchor"));
while (!reader.atEnd()) {
reader.readNextStartElement();
if (reader.tokenType() == QXmlStreamReader::StartElement) {
if (reader.name() == QLatin1String("from")) {
from = loadXmlMarker(reader, QLatin1String("from"));
} else if (reader.name() == QLatin1String("to")) {
to = loadXmlMarker(reader, QLatin1String("to"));
} else {
loadXmlObject(reader);
}
} else if (reader.tokenType() == QXmlStreamReader::EndElement
&& reader.name() == QLatin1String("twoCellAnchor")) {
break;
}
}
return true;
}
void DrawingTwoCellAnchor::saveToXml(QXmlStreamWriter &writer) const
{
writer.writeStartElement(QStringLiteral("xdr:twoCellAnchor"));
writer.writeAttribute(QStringLiteral("editAs"), QStringLiteral("oneCell"));
saveXmlMarker(writer, from, QStringLiteral("xdr:from"));
saveXmlMarker(writer, to, QStringLiteral("xdr:to"));
saveXmlObject(writer);
writer.writeEmptyElement(QStringLiteral("xdr:clientData"));
writer.writeEndElement(); //xdr:twoCellAnchor
}
} // namespace QXlsx

148
src/xlsx/xlsxdrawinganchor_p.h

@ -0,0 +1,148 @@
/****************************************************************************
** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me>
** All right reserved.
**
** Permission is hereby granted, free of charge, to any person obtaining
** a copy of this software and associated documentation files (the
** "Software"), to deal in the Software without restriction, including
** without limitation the rights to use, copy, modify, merge, publish,
** distribute, sublicense, and/or sell copies of the Software, and to
** permit persons to whom the Software is furnished to do so, subject to
** the following conditions:
**
** The above copyright notice and this permission notice shall be
** included in all copies or substantial portions of the Software.
**
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
**
****************************************************************************/
#ifndef QXLSX_XLSXDRAWINGANCHOR_P_H
#define QXLSX_XLSXDRAWINGANCHOR_P_H
#include "xlsxglobal.h"
#include <QPoint>
#include <QSize>
#include <QString>
#include <QSharedPointer>
class QXmlStreamReader;
class QXmlStreamWriter;
namespace QXlsx {
class Drawing;
class MediaFile;
//Helper class
struct XlsxMarker
{
XlsxMarker(){}
XlsxMarker(int row, int column, int rowOffset, int colOffset)
:cell(QPoint(row, column)), offset(rowOffset, colOffset)
{
}
int row() const {return cell.x();}
int col() const {return cell.y();}
int rowOff() const {return offset.width();}
int colOff() const {return offset.height();}
QPoint cell;
QSize offset;
};
class DrawingAnchor
{
public:
enum ObjectType {
GraphicFrame,
Shape,
GroupShape,
ConnectionShape,
Picture,
Unknown
};
DrawingAnchor(Drawing *drawing, ObjectType objectType);
virtual ~DrawingAnchor();
void setObjectPicture(const QImage &img);
virtual bool loadFromXml(QXmlStreamReader &reader) = 0;
virtual void saveToXml(QXmlStreamWriter &writer) const = 0;
protected:
QPoint loadXmlPos(QXmlStreamReader &reader);
QSize loadXmlExt(QXmlStreamReader &reader);
XlsxMarker loadXmlMarker(QXmlStreamReader &reader, const QString &node);
void loadXmlObject(QXmlStreamReader &reader);
void loadXmlObjectShape(QXmlStreamReader &reader);
void loadXmlObjectGroupShape(QXmlStreamReader &reader);
void loadXmlObjectGraphicFrame(QXmlStreamReader &reader);
void loadXmlObjectConnectionShape(QXmlStreamReader &reader);
void loadXmlObjectPicture(QXmlStreamReader &reader);
void saveXmlPos(QXmlStreamWriter &writer, const QPoint &pos) const;
void saveXmlExt(QXmlStreamWriter &writer, const QSize &ext) const;
void saveXmlMarker(QXmlStreamWriter &writer, const XlsxMarker &marker, const QString &node) const;
void saveXmlObject(QXmlStreamWriter &writer) const;
void saveXmlObjectShape(QXmlStreamWriter &writer) const;
void saveXmlObjectGroupShape(QXmlStreamWriter &writer) const;
void saveXmlObjectGraphicFrame(QXmlStreamWriter &writer) const;
void saveXmlObjectConnectionShape(QXmlStreamWriter &writer) const;
void saveXmlObjectPicture(QXmlStreamWriter &writer) const;
Drawing *m_drawing;
ObjectType m_objectType;
QSharedPointer<MediaFile> m_pictureFile;
int m_id;
};
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;
};
} // namespace QXlsx
#endif // QXLSX_XLSXDRAWINGANCHOR_P_H

99
src/xlsx/xlsxmediafile.cpp

@ -0,0 +1,99 @@
/****************************************************************************
** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me>
** All right reserved.
**
** Permission is hereby granted, free of charge, to any person obtaining
** a copy of this software and associated documentation files (the
** "Software"), to deal in the Software without restriction, including
** without limitation the rights to use, copy, modify, merge, publish,
** distribute, sublicense, and/or sell copies of the Software, and to
** permit persons to whom the Software is furnished to do so, subject to
** the following conditions:
**
** The above copyright notice and this permission notice shall be
** included in all copies or substantial portions of the Software.
**
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
**
****************************************************************************/
#include "xlsxmediafile_p.h"
#include <QCryptographicHash>
namespace QXlsx {
MediaFile::MediaFile(const QByteArray &bytes, const QString &suffix, const QString &mimeType)
: m_contents(bytes), m_suffix(suffix), m_mimeType(mimeType)
, m_index(0), m_indexValid(false)
{
m_hashKey = QCryptographicHash::hash(m_contents, QCryptographicHash::Md5);
}
MediaFile::MediaFile(const QString &fileName)
:m_fileName(fileName), m_index(0), m_indexValid(false)
{
}
void MediaFile::set(const QByteArray &bytes, const QString &suffix, const QString &mimeType)
{
m_contents = bytes;
m_suffix = suffix;
m_mimeType = mimeType;
m_hashKey = QCryptographicHash::hash(m_contents, QCryptographicHash::Md5);
m_indexValid = false;
}
void MediaFile::setFileName(const QString &name)
{
m_fileName = name;
}
QString MediaFile::fileName() const
{
return m_fileName;
}
QString MediaFile::suffix() const
{
return m_suffix;
}
QString MediaFile::mimeType() const
{
return m_mimeType;
}
QByteArray MediaFile::contents() const
{
return m_contents;
}
int MediaFile::index() const
{
return m_index;
}
bool MediaFile::isIndexValid() const
{
return m_indexValid;
}
void MediaFile::setIndex(int idx)
{
m_index = idx;
m_indexValid = true;
}
QByteArray MediaFile::hashKey() const
{
return m_hashKey;
}
} // namespace QXlsx

68
src/xlsx/xlsxmediafile_p.h

@ -0,0 +1,68 @@
/****************************************************************************
** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me>
** All right reserved.
**
** Permission is hereby granted, free of charge, to any person obtaining
** a copy of this software and associated documentation files (the
** "Software"), to deal in the Software without restriction, including
** without limitation the rights to use, copy, modify, merge, publish,
** distribute, sublicense, and/or sell copies of the Software, and to
** permit persons to whom the Software is furnished to do so, subject to
** the following conditions:
**
** The above copyright notice and this permission notice shall be
** included in all copies or substantial portions of the Software.
**
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
**
****************************************************************************/
#ifndef QXLSX_XLSXMEDIAFILE_H
#define QXLSX_XLSXMEDIAFILE_H
#include "xlsxglobal.h"
#include <QString>
#include <QByteArray>
namespace QXlsx {
class MediaFile
{
public:
MediaFile(const QString &fileName);
MediaFile(const QByteArray &bytes, const QString &suffix, const QString &mimeType=QString());
void set(const QByteArray &bytes, const QString &suffix, const QString &mimeType=QString());
QString suffix() const;
QString mimeType() const;
QByteArray contents() const;
bool isIndexValid() const;
int index() const;
void setIndex(int idx);
QByteArray hashKey() const;
void setFileName(const QString &name);
QString fileName() const;
private:
QString m_fileName; //...
QByteArray m_contents;
QString m_suffix;
QString m_mimeType;
bool m_indexValid;
int m_index;
QByteArray m_hashKey;
};
} // namespace QXlsx
#endif // QXLSX_XLSXMEDIAFILE_H

66
src/xlsx/xlsxworkbook.cpp

@ -30,6 +30,7 @@
#include "xlsxformat.h"
#include "xlsxworksheet_p.h"
#include "xlsxformat_p.h"
#include "xlsxmediafile_p.h"
#include <QXmlStreamWriter>
#include <QXmlStreamReader>
@ -370,41 +371,22 @@ Theme *Workbook::theme()
return d->theme.data();
}
QList<QImage> Workbook::images()
{
Q_D(Workbook);
return d->images;
}
/*!
* \internal
*
* Unlike media files, drawing file is a property of the sheet.
*/
QList<Drawing *> Workbook::drawings()
{
Q_D(Workbook);
return d->drawings;
}
void Workbook::prepareDrawings()
{
Q_D(Workbook);
int image_ref_id = 0;
d->images.clear();
d->drawings.clear();
QList<Drawing *> ds;
for (int i=0; i<d->worksheets.size(); ++i) {
QSharedPointer<Worksheet> sheet = d->worksheets[i];
if (sheet->images().isEmpty()) //No drawing (such as Image, ...)
continue;
sheet->clearExtraDrawingInfo();
//At present, only picture type supported
for (int idx = 0; idx < sheet->images().size(); ++idx) {
image_ref_id += 1;
sheet->prepareImage(idx, image_ref_id);
d->images.append(sheet->images()[idx]->image);
if (sheet->drawing())
ds.append(sheet->drawing());
}
d->drawings.append(sheet->drawing());
}
return ds;
}
void Workbook::saveToXmlFile(QIODevice *device) const
@ -585,4 +567,32 @@ Relationships &Workbook::relationships()
return d->relationships;
}
/*!
* \internal
*/
QList<QSharedPointer<MediaFile> > Workbook::mediaFiles() const
{
Q_D(const Workbook);
return d->mediaFiles;
}
/*!
* \internal
*/
void Workbook::addMediaFile(QSharedPointer<MediaFile> media, bool force)
{
Q_D(Workbook);
if (!force) {
for (int i=0; i<d->mediaFiles.size(); ++i) {
if (d->mediaFiles[i]->hashKey() == media->hashKey()) {
media->setIndex(i);
return;
}
}
}
media->setIndex(d->mediaFiles.size());
d->mediaFiles.append(media);
}
QT_END_NAMESPACE_XLSX

7
src/xlsx/xlsxworkbook.h

@ -42,6 +42,7 @@ class Document;
class Theme;
class Relationships;
class DocumentPrivate;
class MediaFile;
class WorkbookPrivate;
class Q_XLSX_EXPORT Workbook
@ -75,6 +76,10 @@ public:
QString defaultDateFormat() const;
void setDefaultDateFormat(const QString &format);
//internal used member
void addMediaFile(QSharedPointer<MediaFile> media, bool force=false);
QList<QSharedPointer<MediaFile> > mediaFiles() const;
private:
friend class Worksheet;
friend class WorksheetPrivate;
@ -94,9 +99,9 @@ private:
Theme *theme();
QList<QImage> images();
QList<Drawing *> drawings();
void prepareDrawings();
QStringList worksheetNames() const;
Worksheet *addWorksheet(const QString &name, int sheetId);
WorkbookPrivate * const d_ptr;
};

3
src/xlsx/xlsxworkbook_p.h

@ -87,8 +87,7 @@ public:
QStringList worksheetNames;
QSharedPointer<Styles> styles;
QSharedPointer<Theme> theme;
QList<QImage> images;
QList<Drawing *> drawings;
QList<QSharedPointer<MediaFile> > mediaFiles;
QList<XlsxDefineNameData> definedNamesList;
QList<XlsxSheetItemInfo> sheetItemInfoList;//Data from xml file

188
src/xlsx/xlsxworksheet.cpp

@ -36,6 +36,7 @@
#include "xlsxcell_p.h"
#include "xlsxcellrange.h"
#include "xlsxconditionalformatting_p.h"
#include "xlsxdrawinganchor_p.h"
#include <QVariant>
#include <QDateTime>
@ -455,12 +456,6 @@ void Worksheet::setWhiteSpaceVisible(bool visible)
d->showWhiteSpace = visible;
}
QList<QPair<QString, QString> > Worksheet::drawingLinks() const
{
Q_D(const Worksheet);
return d->drawingLinks;
}
/*!
* Write \a value to cell (\a row, \a column) with the \a format.
* Both \a row and \a column are all 1-indexed value.
@ -1024,12 +1019,33 @@ bool Worksheet::addConditionalFormatting(const ConditionalFormatting &cf)
return true;
}
int Worksheet::insertImage(int row, int column, const QImage &image, const QPointF &offset, double xScale, double yScale)
bool Worksheet::insertImage(int row, int column, const QImage &image)
{
Q_D(Worksheet);
d->imageList.append(new XlsxImageData(row, column, image, offset, xScale, yScale));
return 0;
if (image.isNull())
return false;
if (!d->drawing)
d->drawing = new Drawing(d->workbook);
DrawingOneCellAnchor *anchor = new DrawingOneCellAnchor(d->drawing, DrawingAnchor::Picture);
/*
The size are expressed as English Metric Units (EMUs). There are
12,700 EMUs per point. Therefore, 12,700 * 3 /4 = 9,525 EMUs per
pixel
*/
anchor->from = XlsxMarker(row, column, 0, 0);
anchor->ext = QSize(image.width() * 9525, image.height() * 9525);
anchor->setObjectPicture(image);
return true;
}
int Worksheet::insertImage(int row, int column, const QImage &image, const QPointF & /*offset*/, double /*xScale*/, double /*yScale*/)
{
return insertImage(row, column, image);
}
/*!
@ -1678,61 +1694,38 @@ CellRange Worksheet::dimension() const
return d->dimension;
}
/*!
* \internal
*/
Drawing *Worksheet::drawing() const
{
Q_D(const Worksheet);
return d->drawing;
}
QList<XlsxImageData *> Worksheet::images() const
/*!
* \internal
*
* When loading the .xlsx package, the drawing{x}.xml file path
* is extracted when we parse the sheet{x}.xml file.
*/
QString Worksheet::drawingPath() const
{
Q_D(const Worksheet);
return d->imageList;
return d->drawingPath_in_zip;
}
void Worksheet::clearExtraDrawingInfo()
{
Q_D(Worksheet);
if (d->drawing) {
delete d->drawing;
d->drawing = 0;
d->drawingLinks.clear();
}
}
void Worksheet::prepareImage(int index, int image_id)
/*!
* \internal
*
* Note, the object will be managed by this sheet.
*/
void Worksheet::setDrawing(Drawing *draw)
{
Q_D(Worksheet);
if (!d->drawing) {
d->drawing = new Drawing;
d->drawing->embedded = true;
}
XlsxImageData *imageData = d->imageList[index];
Q_ASSERT(!d->drawing);
XlsxDrawingDimensionData *data = new XlsxDrawingDimensionData;
data->drawing_type = 2;
double width = imageData->image.width() * imageData->xScale;
double height = imageData->image.height() * imageData->yScale;
XlsxObjectPositionData posData = d->pixelsToEMUs(d->objectPixelsPosition(imageData->col, imageData->row, imageData->offset.x(), imageData->offset.y(), width, height));
data->col_from = posData.col_start;
data->col_from_offset = posData.x1;
data->row_from = posData.row_start;
data->row_from_offset = posData.y1;
data->col_to = posData.col_end;
data->col_to_offset = posData.x2;
data->row_to = posData.row_end;
data->row_to_offset = posData.y2;
data->width = posData.width;
data->height = posData.height;
data->col_absolute = posData.x_abs;
data->row_absolute = posData.y_abs;
d->drawing->dimensionList.append(data);
d->drawingLinks.append(QPair<QString, QString>(QStringLiteral("/image"), QStringLiteral("../media/image%1.png").arg(image_id)));
d->drawing = draw;
}
/*
@ -1774,96 +1767,6 @@ int WorksheetPrivate::colPixelsSize(int col) const
return pixels;
}
/*
col_start Col containing upper left corner of object.
x1 Distance to left side of object.
row_start Row containing top left corner of object.
y1 Distance to top of object.
col_end Col containing lower right corner of object.
x2 Distance to right side of object.
row_end Row containing bottom right corner of object.
y2 Distance to bottom of object.
width Width of object frame.
height Height of object frame.
x_abs Absolute distance to left side of object.
y_abs Absolute distance to top side of object.
*/
XlsxObjectPositionData WorksheetPrivate::objectPixelsPosition(int col_start, int row_start, double x1, double y1, double width, double height) const
{
double x_abs = 0;
double y_abs = 0;
for (int col_id = 1; col_id < col_start; ++col_id)
x_abs += colPixelsSize(col_id);
x_abs += x1;
for (int row_id = 1; row_id < row_start; ++row_id)
y_abs += rowPixelsSize(row_id);
y_abs += y1;
// Adjust start column for offsets that are greater than the col width.
while (x1 > colPixelsSize(col_start)) {
x1 -= colPixelsSize(col_start);
col_start += 1;
}
while (y1 > rowPixelsSize(row_start)) {
y1 -= rowPixelsSize(row_start);
row_start += 1;
}
int col_end = col_start;
int row_end = row_start;
double x2 = width + x1;
double y2 = height + y1;
while (x2 > colPixelsSize(col_end)) {
x2 -= colPixelsSize(col_end);
col_end += 1;
}
while (y2 > rowPixelsSize(row_end)) {
y2 -= rowPixelsSize(row_end);
row_end += 1;
}
XlsxObjectPositionData data;
data.col_start = col_start;
data.x1 = x1;
data.row_start = row_start;
data.y1 = y1;
data.col_end = col_end;
data.x2 = x2;
data.row_end = row_end;
data.y2 = y2;
data.x_abs = x_abs;
data.y_abs = y_abs;
data.width = width;
data.height = height;
return data;
}
/*
Calculate the vertices that define the position of a graphical
object within the worksheet in EMUs.
The vertices are expressed as English Metric Units (EMUs). There are
12,700 EMUs per point. Therefore, 12,700 * 3 /4 = 9,525 EMUs per
pixel
*/
XlsxObjectPositionData WorksheetPrivate::pixelsToEMUs(const XlsxObjectPositionData &data) const
{
XlsxObjectPositionData result = data;
result.x1 = static_cast<int>(data.x1 * 9525 + 0.5);
result.y1 = static_cast<int>(data.y1 * 9525 + 0.5);
result.x2 = static_cast<int>(data.x2 * 9525 + 0.5);
result.y2 = static_cast<int>(data.y2 * 9525 + 0.5);
result.x_abs = static_cast<int>(data.x_abs * 9525 + 0.5);
result.y_abs = static_cast<int>(data.y_abs * 9525 + 0.5);
result.width = static_cast<int>(data.width * 9525 + 0.5);
result.height = static_cast<int>(data.height * 9525 + 0.5);
return result;
}
QByteArray Worksheet::saveToXmlData() const
{
QByteArray data;
@ -2220,6 +2123,9 @@ bool Worksheet::loadFromXmlFile(QIODevice *device)
d->conditionalFormattingList.append(cf);
} else if (reader.name() == QLatin1String("hyperlinks")) {
d->loadXmlHyperlinks(reader);
} else if (reader.name() == QLatin1String("drawing")) {
QString rId = reader.attributes().value(QStringLiteral("r:id")).toString();
d->drawingPath_in_zip = d->relationships.getRelationshipById(rId).target;
}
}
}

10
src/xlsx/xlsxworksheet.h

@ -90,7 +90,8 @@ public:
Cell *cellAt(const QString &row_column) const;
Cell *cellAt(int row, int column) const;
int insertImage(int row, int column, const QImage &image, const QPointF &offset=QPointF(), double xScale=1, double yScale=1);
bool insertImage(int row, int column, const QImage &image);
Q_DECL_DEPRECATED int insertImage(int row, int column, const QImage &image, const QPointF &offset, double xScale=1, double yScale=1);
int mergeCells(const QString &range, const Format &format=Format());
int mergeCells(const CellRange &range, const Format &format=Format());
@ -149,11 +150,10 @@ private:
bool isHidden() const;
void setHidden(bool hidden);
int sheetId() const;
QList<QPair<QString, QString> > drawingLinks() const;
Drawing *drawing() const;
QList<XlsxImageData *> images() const;
void prepareImage(int index, int image_id);
void clearExtraDrawingInfo();
void setDrawing(Drawing *d);
QString drawingPath() const;
WorksheetPrivate * const d_ptr;
};

66
src/xlsx/xlsxworksheet_p.h

@ -79,67 +79,6 @@ struct XlsxHyperlinkData
QString tooltip;
};
struct XlsxImageData
{
XlsxImageData(int row, int col, const QImage &image, const QPointF &offset, double xScale, double yScale) :
row(row), col(col), image(image), offset(offset), xScale(xScale), yScale(yScale)
{
}
int row;
int col;
QImage image;
QPointF offset;
double xScale;
double yScale;
};
/*
The vertices that define the position of a graphical object
within the worksheet in pixels.
+------------+------------+
| A | B |
+-----+------------+------------+
| |(x1,y1) | |
| 1 |(A1)._______|______ |
| | | | |
| | | | |
+-----+----| OBJECT |-----+
| | | | |
| 2 | |______________. |
| | | (B2)|
| | | (x2,y2)|
+---- +------------+------------+
Example of an object that covers some of the area from cell A1 to B2.
Based on the width and height of the object we need to calculate 8 vars:
col_start, row_start, col_end, row_end, x1, y1, x2, y2.
We also calculate the absolute x and y position of the top left vertex of
the object. This is required for images.
The width and height of the cells that the object occupies can be
variable and have to be taken into account.
*/
struct XlsxObjectPositionData
{
int col_start;
double x1;
int row_start;
double y1;
int col_end;
double x2;
int row_end;
double y2;
double width;
double height;
double x_abs;
double y_abs;
};
struct XlsxRowInfo
{
XlsxRowInfo(double height=0, const Format &format=Format(), bool hidden=false) :
@ -193,8 +132,6 @@ public:
void saveXmlDataValidations(QXmlStreamWriter &writer) const;
int rowPixelsSize(int row) const;
int colPixelsSize(int col) const;
XlsxObjectPositionData objectPixelsPosition(int col_start, int row_start, double x1, double y1, double width, double height) const;
XlsxObjectPositionData pixelsToEMUs(const XlsxObjectPositionData &data) const;
QSharedPointer<Cell> loadXmlNumericCellData(QXmlStreamReader &reader);
void loadXmlSheetData(QXmlStreamReader &reader);
@ -210,15 +147,14 @@ public:
Workbook *workbook;
mutable Relationships relationships;
Drawing *drawing;
QString drawingPath_in_zip;
QMap<int, QMap<int, QSharedPointer<Cell> > > cellTable;
QMap<int, QMap<int, QString> > comments;
QMap<int, QMap<int, QSharedPointer<XlsxHyperlinkData> > > urlTable;
QList<CellRange> merges;
QList<XlsxImageData *> imageList;
QMap<int, QSharedPointer<XlsxRowInfo> > rowsInfo;
QMap<int, QSharedPointer<XlsxColumnInfo> > colsInfo;
QMap<int, QSharedPointer<XlsxColumnInfo> > colsInfoHelper;
QList<QPair<QString, QString> > drawingLinks;
QList<DataValidation> dataValidationsList;
QList<ConditionalFormatting> conditionalFormattingList;

Loading…
Cancel
Save