Code refactoring: Improve picture support
Don't lost picture information when edit existing .xlsx files
This commit is contained in:
@@ -6,12 +6,14 @@ int main(int argc, char** argv)
|
|||||||
QGuiApplication(argc, argv);
|
QGuiApplication(argc, argv);
|
||||||
|
|
||||||
QXlsx::Document xlsx;
|
QXlsx::Document xlsx;
|
||||||
|
QImage image(40, 30, QImage::Format_RGB32);
|
||||||
QImage image(400, 300, QImage::Format_RGB32);
|
|
||||||
image.fill(Qt::green);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-2
@@ -34,7 +34,9 @@ HEADERS += $$PWD/xlsxdocpropscore_p.h \
|
|||||||
$$PWD/xlsxconditionalformatting.h \
|
$$PWD/xlsxconditionalformatting.h \
|
||||||
$$PWD/xlsxconditionalformatting_p.h \
|
$$PWD/xlsxconditionalformatting_p.h \
|
||||||
$$PWD/xlsxcolor_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 \
|
SOURCES += $$PWD/xlsxdocpropscore.cpp \
|
||||||
$$PWD/xlsxdocpropsapp.cpp \
|
$$PWD/xlsxdocpropsapp.cpp \
|
||||||
@@ -57,4 +59,6 @@ SOURCES += $$PWD/xlsxdocpropscore.cpp \
|
|||||||
$$PWD/xlsxrichstring.cpp \
|
$$PWD/xlsxrichstring.cpp \
|
||||||
$$PWD/xlsxconditionalformatting.cpp \
|
$$PWD/xlsxconditionalformatting.cpp \
|
||||||
$$PWD/xlsxcolor.cpp \
|
$$PWD/xlsxcolor.cpp \
|
||||||
$$PWD/xlsxnumformatparser.cpp
|
$$PWD/xlsxnumformatparser.cpp \
|
||||||
|
$$PWD/xlsxdrawinganchor.cpp \
|
||||||
|
$$PWD/xlsxmediafile.cpp
|
||||||
|
|||||||
+32
-28
@@ -37,12 +37,14 @@
|
|||||||
#include "xlsxutility_p.h"
|
#include "xlsxutility_p.h"
|
||||||
#include "xlsxworkbook_p.h"
|
#include "xlsxworkbook_p.h"
|
||||||
#include "xlsxdrawing_p.h"
|
#include "xlsxdrawing_p.h"
|
||||||
|
#include "xlsxmediafile_p.h"
|
||||||
#include "xlsxzipreader_p.h"
|
#include "xlsxzipreader_p.h"
|
||||||
#include "xlsxzipwriter_p.h"
|
#include "xlsxzipwriter_p.h"
|
||||||
|
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QPointF>
|
#include <QPointF>
|
||||||
#include <QBuffer>
|
#include <QBuffer>
|
||||||
|
#include <QDir>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE_XLSX
|
QT_BEGIN_NAMESPACE_XLSX
|
||||||
|
|
||||||
@@ -189,6 +191,27 @@ bool DocumentPrivate::loadPackage(QIODevice *device)
|
|||||||
if (zipReader.filePaths().contains(rel_path))
|
if (zipReader.filePaths().contains(rel_path))
|
||||||
sheet->relationships().loadFromXmlData(zipReader.fileData(rel_path));
|
sheet->relationships().loadFromXmlData(zipReader.fileData(rel_path));
|
||||||
sheet->loadFromXmlData(zipReader.fileData(worksheet_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;
|
return true;
|
||||||
@@ -206,9 +229,6 @@ bool DocumentPrivate::savePackage(QIODevice *device) const
|
|||||||
DocPropsApp docPropsApp;
|
DocPropsApp docPropsApp;
|
||||||
DocPropsCore docPropsCore;
|
DocPropsCore docPropsCore;
|
||||||
|
|
||||||
//: Todo
|
|
||||||
workbook->prepareDrawings();
|
|
||||||
|
|
||||||
// save worksheet xml files
|
// save worksheet xml files
|
||||||
for (int i=0; i<workbook->worksheetCount(); ++i) {
|
for (int i=0; i<workbook->worksheetCount(); ++i) {
|
||||||
Worksheet *sheet = workbook->worksheet(i);
|
Worksheet *sheet = workbook->worksheet(i);
|
||||||
@@ -232,19 +252,8 @@ bool DocumentPrivate::savePackage(QIODevice *device) const
|
|||||||
|
|
||||||
Drawing *drawing = workbook->drawings()[i];
|
Drawing *drawing = workbook->drawings()[i];
|
||||||
zipWriter.addFile(QStringLiteral("xl/drawings/drawing%1.xml").arg(i+1), drawing->saveToXmlData());
|
zipWriter.addFile(QStringLiteral("xl/drawings/drawing%1.xml").arg(i+1), drawing->saveToXmlData());
|
||||||
}
|
if (!drawing->relationships.isEmpty())
|
||||||
|
zipWriter.addFile(QStringLiteral("xl/drawings/_rels/drawing%1.xml.rels").arg(i+1), drawing->relationships.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());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// save docProps app/core xml file
|
// 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());
|
zipWriter.addFile(QStringLiteral("xl/theme/theme1.xml"), workbook->theme()->saveToXmlData());
|
||||||
|
|
||||||
// save image files
|
// save image files
|
||||||
if (!workbook->images().isEmpty())
|
for (int i=0; i<workbook->mediaFiles().size(); ++i) {
|
||||||
contentTypes.addDefault(QStringLiteral("png"), QStringLiteral("image/png"));
|
QSharedPointer<MediaFile> mf = workbook->mediaFiles()[i];
|
||||||
|
if (!mf->mimeType().isEmpty())
|
||||||
|
contentTypes.addDefault(mf->suffix(), mf->mimeType());
|
||||||
|
|
||||||
for (int i=0; i<workbook->images().size(); ++i) {
|
zipWriter.addFile(QStringLiteral("xl/media/image%1.%2").arg(i+1).arg(mf->suffix()), mf->contents());
|
||||||
QImage image = workbook->images()[i];
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// save root .rels xml file
|
// 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
|
* \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.
|
* \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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
|||||||
+35
-141
@@ -24,6 +24,7 @@
|
|||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
#include "xlsxdrawing_p.h"
|
#include "xlsxdrawing_p.h"
|
||||||
|
#include "xlsxdrawinganchor_p.h"
|
||||||
|
|
||||||
#include <QXmlStreamWriter>
|
#include <QXmlStreamWriter>
|
||||||
#include <QXmlStreamReader>
|
#include <QXmlStreamReader>
|
||||||
@@ -31,10 +32,15 @@
|
|||||||
|
|
||||||
namespace QXlsx {
|
namespace QXlsx {
|
||||||
|
|
||||||
Drawing::Drawing()
|
Drawing::Drawing(Workbook *workbook)
|
||||||
|
:workbook(workbook)
|
||||||
{
|
{
|
||||||
embedded = false;
|
|
||||||
orientation = 0;
|
}
|
||||||
|
|
||||||
|
Drawing::~Drawing()
|
||||||
|
{
|
||||||
|
qDeleteAll(anchors);
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray Drawing::saveToXmlData() const
|
QByteArray Drawing::saveToXmlData() const
|
||||||
@@ -48,6 +54,8 @@ QByteArray Drawing::saveToXmlData() const
|
|||||||
|
|
||||||
void Drawing::saveToXmlFile(QIODevice *device) const
|
void Drawing::saveToXmlFile(QIODevice *device) const
|
||||||
{
|
{
|
||||||
|
relationships.clear();
|
||||||
|
|
||||||
QXmlStreamWriter writer(device);
|
QXmlStreamWriter writer(device);
|
||||||
|
|
||||||
writer.writeStartDocument(QStringLiteral("1.0"), true);
|
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:xdr"), QStringLiteral("http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing"));
|
||||||
writer.writeAttribute(QStringLiteral("xmlns:a"), QStringLiteral("http://schemas.openxmlformats.org/drawingml/2006/main"));
|
writer.writeAttribute(QStringLiteral("xmlns:a"), QStringLiteral("http://schemas.openxmlformats.org/drawingml/2006/main"));
|
||||||
|
|
||||||
if (embedded) {
|
foreach (DrawingAnchor *anchor, anchors)
|
||||||
int index = 1;
|
anchor->saveToXml(writer);
|
||||||
foreach (XlsxDrawingDimensionData *dimension, dimensionList) {
|
|
||||||
writeTwoCellAnchor(writer, index, dimension);
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
//write the xdr:absoluteAnchor element
|
|
||||||
writeAbsoluteAnchor(writer, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.writeEndElement();//xdr:wsDr
|
writer.writeEndElement();//xdr:wsDr
|
||||||
writer.writeEndDocument();
|
writer.writeEndDocument();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Drawing::writeTwoCellAnchor(QXmlStreamWriter &writer, int index, XlsxDrawingDimensionData *data) const
|
bool Drawing::loadFromXmlFile(QIODevice *device)
|
||||||
{
|
{
|
||||||
writer.writeStartElement(QStringLiteral("xdr:twoCellAnchor"));
|
QXmlStreamReader reader(device);
|
||||||
if (data->drawing_type == 2)
|
while (!reader.atEnd()) {
|
||||||
writer.writeAttribute(QStringLiteral("editAs"), QStringLiteral("oneCell"));
|
reader.readNextStartElement();
|
||||||
// if (shape)
|
if (reader.tokenType() == QXmlStreamReader::StartElement) {
|
||||||
// writer.writeAttribute(QStringLiteral("editAs"), );
|
if (reader.name() == QLatin1String("absoluteAnchor")) {
|
||||||
|
DrawingAbsoluteAnchor * anchor = new DrawingAbsoluteAnchor(this);
|
||||||
writer.writeStartElement(QStringLiteral("xdr:from"));
|
anchor->loadFromXml(reader);
|
||||||
writer.writeTextElement(QStringLiteral("xdr:col"), QString::number(data->col_from));
|
} else if (reader.name() == QLatin1String("oneCellAnchor")) {
|
||||||
writer.writeTextElement(QStringLiteral("xdr:colOff"), QString::number((int)data->col_from_offset));
|
DrawingOneCellAnchor * anchor = new DrawingOneCellAnchor(this);
|
||||||
writer.writeTextElement(QStringLiteral("xdr:row"), QString::number(data->row_from));
|
anchor->loadFromXml(reader);
|
||||||
writer.writeTextElement(QStringLiteral("xdr:rowOff"), QString::number((int)data->row_from_offset));
|
} else if (reader.name() == QLatin1String("twoCellAnchor")) {
|
||||||
writer.writeEndElement(); //xdr:from
|
DrawingTwoCellAnchor * anchor = new DrawingTwoCellAnchor(this);
|
||||||
|
anchor->loadFromXml(reader);
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.writeEmptyElement(QStringLiteral("xdr:clientData"));
|
return true;
|
||||||
writer.writeEndElement(); //xdr:twoCellAnchor
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Drawing::writeAbsoluteAnchor(QXmlStreamWriter &writer, int index) const
|
bool Drawing::loadFromXmlData(const QByteArray &data)
|
||||||
{
|
{
|
||||||
writer.writeStartElement(QStringLiteral("xdr:absoluteAnchor"));
|
QBuffer buffer;
|
||||||
if (orientation == 0) {
|
buffer.setData(data);
|
||||||
writePos(writer, 0, 0);
|
buffer.open(QIODevice::ReadOnly);
|
||||||
writeExt(writer, 9308969, 6078325);
|
|
||||||
} else {
|
|
||||||
writePos(writer, 0, -47625);
|
|
||||||
writeExt(writer, 6162675, 6124575);
|
|
||||||
}
|
|
||||||
|
|
||||||
writeGraphicFrame(writer, index);
|
return loadFromXmlFile(&buffer);
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
void Drawing::writePicture(QXmlStreamWriter &writer, int index, double col_abs, double row_abs, int width, int height, const QString &description) const
|
|
||||||
{
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace QXlsx
|
} // namespace QXlsx
|
||||||
|
|||||||
+14
-29
@@ -37,51 +37,36 @@
|
|||||||
// We mean it.
|
// We mean it.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#include "xlsxrelationships_p.h"
|
||||||
|
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
#include <QSharedPointer>
|
||||||
|
|
||||||
class QIODevice;
|
class QIODevice;
|
||||||
class QXmlStreamWriter;
|
class QXmlStreamWriter;
|
||||||
|
|
||||||
namespace QXlsx {
|
namespace QXlsx {
|
||||||
|
|
||||||
struct XlsxDrawingDimensionData
|
class DrawingAnchor;
|
||||||
{
|
class Workbook;
|
||||||
int drawing_type;
|
class MediaFile;
|
||||||
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 Drawing
|
class Drawing
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Drawing();
|
Drawing(Workbook *workbook);
|
||||||
|
~Drawing();
|
||||||
void saveToXmlFile(QIODevice *device) const;
|
void saveToXmlFile(QIODevice *device) const;
|
||||||
QByteArray saveToXmlData() const;
|
QByteArray saveToXmlData() const;
|
||||||
|
bool loadFromXmlFile(QIODevice *device);
|
||||||
|
bool loadFromXmlData(const QByteArray &data);
|
||||||
|
|
||||||
bool embedded;
|
Workbook *workbook;
|
||||||
int orientation;
|
QList<DrawingAnchor *> anchors;
|
||||||
QList <XlsxDrawingDimensionData *> dimensionList;
|
mutable Relationships relationships;
|
||||||
|
|
||||||
private:
|
QString pathInPackage;
|
||||||
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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QXlsx
|
} // namespace QXlsx
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
+39
-29
@@ -30,6 +30,7 @@
|
|||||||
#include "xlsxformat.h"
|
#include "xlsxformat.h"
|
||||||
#include "xlsxworksheet_p.h"
|
#include "xlsxworksheet_p.h"
|
||||||
#include "xlsxformat_p.h"
|
#include "xlsxformat_p.h"
|
||||||
|
#include "xlsxmediafile_p.h"
|
||||||
|
|
||||||
#include <QXmlStreamWriter>
|
#include <QXmlStreamWriter>
|
||||||
#include <QXmlStreamReader>
|
#include <QXmlStreamReader>
|
||||||
@@ -370,41 +371,22 @@ Theme *Workbook::theme()
|
|||||||
return d->theme.data();
|
return d->theme.data();
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<QImage> Workbook::images()
|
/*!
|
||||||
{
|
* \internal
|
||||||
Q_D(Workbook);
|
*
|
||||||
return d->images;
|
* Unlike media files, drawing file is a property of the sheet.
|
||||||
}
|
*/
|
||||||
|
|
||||||
QList<Drawing *> Workbook::drawings()
|
QList<Drawing *> Workbook::drawings()
|
||||||
{
|
{
|
||||||
Q_D(Workbook);
|
Q_D(Workbook);
|
||||||
return d->drawings;
|
QList<Drawing *> ds;
|
||||||
}
|
|
||||||
|
|
||||||
void Workbook::prepareDrawings()
|
|
||||||
{
|
|
||||||
Q_D(Workbook);
|
|
||||||
int image_ref_id = 0;
|
|
||||||
d->images.clear();
|
|
||||||
d->drawings.clear();
|
|
||||||
|
|
||||||
for (int i=0; i<d->worksheets.size(); ++i) {
|
for (int i=0; i<d->worksheets.size(); ++i) {
|
||||||
QSharedPointer<Worksheet> sheet = d->worksheets[i];
|
QSharedPointer<Worksheet> sheet = d->worksheets[i];
|
||||||
if (sheet->images().isEmpty()) //No drawing (such as Image, ...)
|
if (sheet->drawing())
|
||||||
continue;
|
ds.append(sheet->drawing());
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
d->drawings.append(sheet->drawing());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ds;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Workbook::saveToXmlFile(QIODevice *device) const
|
void Workbook::saveToXmlFile(QIODevice *device) const
|
||||||
@@ -585,4 +567,32 @@ Relationships &Workbook::relationships()
|
|||||||
return d->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
|
QT_END_NAMESPACE_XLSX
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ class Document;
|
|||||||
class Theme;
|
class Theme;
|
||||||
class Relationships;
|
class Relationships;
|
||||||
class DocumentPrivate;
|
class DocumentPrivate;
|
||||||
|
class MediaFile;
|
||||||
|
|
||||||
class WorkbookPrivate;
|
class WorkbookPrivate;
|
||||||
class Q_XLSX_EXPORT Workbook
|
class Q_XLSX_EXPORT Workbook
|
||||||
@@ -75,6 +76,10 @@ public:
|
|||||||
QString defaultDateFormat() const;
|
QString defaultDateFormat() const;
|
||||||
void setDefaultDateFormat(const QString &format);
|
void setDefaultDateFormat(const QString &format);
|
||||||
|
|
||||||
|
//internal used member
|
||||||
|
void addMediaFile(QSharedPointer<MediaFile> media, bool force=false);
|
||||||
|
QList<QSharedPointer<MediaFile> > mediaFiles() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class Worksheet;
|
friend class Worksheet;
|
||||||
friend class WorksheetPrivate;
|
friend class WorksheetPrivate;
|
||||||
@@ -94,9 +99,9 @@ private:
|
|||||||
Theme *theme();
|
Theme *theme();
|
||||||
QList<QImage> images();
|
QList<QImage> images();
|
||||||
QList<Drawing *> drawings();
|
QList<Drawing *> drawings();
|
||||||
void prepareDrawings();
|
|
||||||
QStringList worksheetNames() const;
|
QStringList worksheetNames() const;
|
||||||
Worksheet *addWorksheet(const QString &name, int sheetId);
|
Worksheet *addWorksheet(const QString &name, int sheetId);
|
||||||
|
|
||||||
WorkbookPrivate * const d_ptr;
|
WorkbookPrivate * const d_ptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -87,8 +87,7 @@ public:
|
|||||||
QStringList worksheetNames;
|
QStringList worksheetNames;
|
||||||
QSharedPointer<Styles> styles;
|
QSharedPointer<Styles> styles;
|
||||||
QSharedPointer<Theme> theme;
|
QSharedPointer<Theme> theme;
|
||||||
QList<QImage> images;
|
QList<QSharedPointer<MediaFile> > mediaFiles;
|
||||||
QList<Drawing *> drawings;
|
|
||||||
QList<XlsxDefineNameData> definedNamesList;
|
QList<XlsxDefineNameData> definedNamesList;
|
||||||
|
|
||||||
QList<XlsxSheetItemInfo> sheetItemInfoList;//Data from xml file
|
QList<XlsxSheetItemInfo> sheetItemInfoList;//Data from xml file
|
||||||
|
|||||||
+47
-141
@@ -36,6 +36,7 @@
|
|||||||
#include "xlsxcell_p.h"
|
#include "xlsxcell_p.h"
|
||||||
#include "xlsxcellrange.h"
|
#include "xlsxcellrange.h"
|
||||||
#include "xlsxconditionalformatting_p.h"
|
#include "xlsxconditionalformatting_p.h"
|
||||||
|
#include "xlsxdrawinganchor_p.h"
|
||||||
|
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
@@ -455,12 +456,6 @@ void Worksheet::setWhiteSpaceVisible(bool visible)
|
|||||||
d->showWhiteSpace = 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.
|
* Write \a value to cell (\a row, \a column) with the \a format.
|
||||||
* Both \a row and \a column are all 1-indexed value.
|
* Both \a row and \a column are all 1-indexed value.
|
||||||
@@ -1024,12 +1019,33 @@ bool Worksheet::addConditionalFormatting(const ConditionalFormatting &cf)
|
|||||||
return true;
|
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);
|
Q_D(Worksheet);
|
||||||
|
|
||||||
d->imageList.append(new XlsxImageData(row, column, image, offset, xScale, yScale));
|
if (image.isNull())
|
||||||
return 0;
|
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;
|
return d->dimension;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \internal
|
||||||
|
*/
|
||||||
Drawing *Worksheet::drawing() const
|
Drawing *Worksheet::drawing() const
|
||||||
{
|
{
|
||||||
Q_D(const Worksheet);
|
Q_D(const Worksheet);
|
||||||
return d->drawing;
|
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);
|
Q_D(const Worksheet);
|
||||||
return d->imageList;
|
return d->drawingPath_in_zip;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Worksheet::clearExtraDrawingInfo()
|
/*!
|
||||||
|
* \internal
|
||||||
|
*
|
||||||
|
* Note, the object will be managed by this sheet.
|
||||||
|
*/
|
||||||
|
void Worksheet::setDrawing(Drawing *draw)
|
||||||
{
|
{
|
||||||
Q_D(Worksheet);
|
Q_D(Worksheet);
|
||||||
if (d->drawing) {
|
Q_ASSERT(!d->drawing);
|
||||||
delete d->drawing;
|
|
||||||
d->drawing = 0;
|
|
||||||
d->drawingLinks.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Worksheet::prepareImage(int index, int image_id)
|
d->drawing = draw;
|
||||||
{
|
|
||||||
Q_D(Worksheet);
|
|
||||||
if (!d->drawing) {
|
|
||||||
d->drawing = new Drawing;
|
|
||||||
d->drawing->embedded = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
XlsxImageData *imageData = d->imageList[index];
|
|
||||||
|
|
||||||
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)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1774,96 +1767,6 @@ int WorksheetPrivate::colPixelsSize(int col) const
|
|||||||
return pixels;
|
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 Worksheet::saveToXmlData() const
|
||||||
{
|
{
|
||||||
QByteArray data;
|
QByteArray data;
|
||||||
@@ -2220,6 +2123,9 @@ bool Worksheet::loadFromXmlFile(QIODevice *device)
|
|||||||
d->conditionalFormattingList.append(cf);
|
d->conditionalFormattingList.append(cf);
|
||||||
} else if (reader.name() == QLatin1String("hyperlinks")) {
|
} else if (reader.name() == QLatin1String("hyperlinks")) {
|
||||||
d->loadXmlHyperlinks(reader);
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,7 +90,8 @@ public:
|
|||||||
Cell *cellAt(const QString &row_column) const;
|
Cell *cellAt(const QString &row_column) const;
|
||||||
Cell *cellAt(int row, int 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 QString &range, const Format &format=Format());
|
||||||
int mergeCells(const CellRange &range, const Format &format=Format());
|
int mergeCells(const CellRange &range, const Format &format=Format());
|
||||||
@@ -149,11 +150,10 @@ private:
|
|||||||
bool isHidden() const;
|
bool isHidden() const;
|
||||||
void setHidden(bool hidden);
|
void setHidden(bool hidden);
|
||||||
int sheetId() const;
|
int sheetId() const;
|
||||||
QList<QPair<QString, QString> > drawingLinks() const;
|
|
||||||
Drawing *drawing() const;
|
Drawing *drawing() const;
|
||||||
QList<XlsxImageData *> images() const;
|
void setDrawing(Drawing *d);
|
||||||
void prepareImage(int index, int image_id);
|
QString drawingPath() const;
|
||||||
void clearExtraDrawingInfo();
|
|
||||||
|
|
||||||
WorksheetPrivate * const d_ptr;
|
WorksheetPrivate * const d_ptr;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -79,67 +79,6 @@ struct XlsxHyperlinkData
|
|||||||
QString tooltip;
|
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
|
struct XlsxRowInfo
|
||||||
{
|
{
|
||||||
XlsxRowInfo(double height=0, const Format &format=Format(), bool hidden=false) :
|
XlsxRowInfo(double height=0, const Format &format=Format(), bool hidden=false) :
|
||||||
@@ -193,8 +132,6 @@ public:
|
|||||||
void saveXmlDataValidations(QXmlStreamWriter &writer) const;
|
void saveXmlDataValidations(QXmlStreamWriter &writer) const;
|
||||||
int rowPixelsSize(int row) const;
|
int rowPixelsSize(int row) const;
|
||||||
int colPixelsSize(int col) 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);
|
QSharedPointer<Cell> loadXmlNumericCellData(QXmlStreamReader &reader);
|
||||||
void loadXmlSheetData(QXmlStreamReader &reader);
|
void loadXmlSheetData(QXmlStreamReader &reader);
|
||||||
@@ -210,15 +147,14 @@ public:
|
|||||||
Workbook *workbook;
|
Workbook *workbook;
|
||||||
mutable Relationships relationships;
|
mutable Relationships relationships;
|
||||||
Drawing *drawing;
|
Drawing *drawing;
|
||||||
|
QString drawingPath_in_zip;
|
||||||
QMap<int, QMap<int, QSharedPointer<Cell> > > cellTable;
|
QMap<int, QMap<int, QSharedPointer<Cell> > > cellTable;
|
||||||
QMap<int, QMap<int, QString> > comments;
|
QMap<int, QMap<int, QString> > comments;
|
||||||
QMap<int, QMap<int, QSharedPointer<XlsxHyperlinkData> > > urlTable;
|
QMap<int, QMap<int, QSharedPointer<XlsxHyperlinkData> > > urlTable;
|
||||||
QList<CellRange> merges;
|
QList<CellRange> merges;
|
||||||
QList<XlsxImageData *> imageList;
|
|
||||||
QMap<int, QSharedPointer<XlsxRowInfo> > rowsInfo;
|
QMap<int, QSharedPointer<XlsxRowInfo> > rowsInfo;
|
||||||
QMap<int, QSharedPointer<XlsxColumnInfo> > colsInfo;
|
QMap<int, QSharedPointer<XlsxColumnInfo> > colsInfo;
|
||||||
QMap<int, QSharedPointer<XlsxColumnInfo> > colsInfoHelper;
|
QMap<int, QSharedPointer<XlsxColumnInfo> > colsInfoHelper;
|
||||||
QList<QPair<QString, QString> > drawingLinks;
|
|
||||||
|
|
||||||
QList<DataValidation> dataValidationsList;
|
QList<DataValidation> dataValidationsList;
|
||||||
QList<ConditionalFormatting> conditionalFormattingList;
|
QList<ConditionalFormatting> conditionalFormattingList;
|
||||||
|
|||||||
Reference in New Issue
Block a user