Browse Source

Fix Issue1: Add insert_image() function

master
Debao Zhang 11 years ago
parent
commit
b9dc02b79e
  1. 6
      examples/xlsx/image/image.pro
  2. 25
      examples/xlsx/image/main.cpp
  3. 3
      examples/xlsx/xlsx.pro
  4. 6
      src/xlsx/qtxlsx.pri
  5. 7
      src/xlsx/xlsxcontenttypes.cpp
  6. 174
      src/xlsx/xlsxdrawing.cpp
  7. 53
      src/xlsx/xlsxdrawing_p.h
  8. 72
      src/xlsx/xlsxpackage.cpp
  9. 7
      src/xlsx/xlsxpackage_p.h
  10. 39
      src/xlsx/xlsxworkbook.cpp
  11. 5
      src/xlsx/xlsxworkbook.h
  12. 2
      src/xlsx/xlsxworkbook_p.h
  13. 219
      src/xlsx/xlsxworksheet.cpp
  14. 12
      src/xlsx/xlsxworksheet.h
  15. 74
      src/xlsx/xlsxworksheet_p.h

6
examples/xlsx/image/image.pro

@ -0,0 +1,6 @@
TARGET = image
#include(../../../src/xlsx/qtxlsx.pri)
QT += xlsx
SOURCES += main.cpp

25
examples/xlsx/image/main.cpp

@ -0,0 +1,25 @@
#include <QtGui>
#include "xlsxworkbook.h"
#include "xlsxworksheet.h"
#ifdef Q_OS_MAC
# define DATA_PATH "../../../"
#else
# define DATA_PATH "./"
#endif
int main(int argc, char** argv)
{
QGuiApplication(argc, argv);
QXlsx::Workbook workbook;
QXlsx::Worksheet *sheet = workbook.addWorksheet();
QImage image(400, 300, QImage::Format_RGB32);
image.fill(Qt::green);
sheet->insertImage(5, 5, image);
workbook.save(DATA_PATH"Test.xlsx");
// workbook.save(DATA_PATH"Test2.zip");
return 0;
}

3
examples/xlsx/xlsx.pro

@ -1,4 +1,5 @@
TEMPLATE = subdirs TEMPLATE = subdirs
SUBDIRS = hello style \ SUBDIRS = hello style \
documentproperty documentproperty \
image

6
src/xlsx/qtxlsx.pri

@ -21,7 +21,8 @@ HEADERS += $$PWD/xlsxdocpropscore_p.h \
$$PWD/xlsxworkbook_p.h \ $$PWD/xlsxworkbook_p.h \
$$PWD/xlsxworksheet_p.h \ $$PWD/xlsxworksheet_p.h \
$$PWD/xlsxformat_p.h \ $$PWD/xlsxformat_p.h \
$$PWD/xlsxglobal.h $$PWD/xlsxglobal.h \
$$PWD/xlsxdrawing_p.h
SOURCES += $$PWD/xlsxdocpropscore.cpp \ SOURCES += $$PWD/xlsxdocpropscore.cpp \
$$PWD/xlsxdocpropsapp.cpp \ $$PWD/xlsxdocpropsapp.cpp \
@ -36,4 +37,5 @@ SOURCES += $$PWD/xlsxdocpropscore.cpp \
$$PWD/xlsxworkbook.cpp \ $$PWD/xlsxworkbook.cpp \
$$PWD/xlsxworksheet.cpp \ $$PWD/xlsxworksheet.cpp \
$$PWD/xlsxzipwriter.cpp \ $$PWD/xlsxzipwriter.cpp \
$$PWD/xlsxpackage.cpp $$PWD/xlsxpackage.cpp \
$$PWD/xlsxdrawing.cpp

7
src/xlsx/xlsxcontenttypes.cpp

@ -64,6 +64,11 @@ void ContentTypes::addChartsheetName(const QString &name)
addOverride(QStringLiteral("/xl/chartsheets/%1.xml").arg(name), m_document_prefix + QStringLiteral("spreadsheetml.chartsheet+xml")); addOverride(QStringLiteral("/xl/chartsheets/%1.xml").arg(name), m_document_prefix + QStringLiteral("spreadsheetml.chartsheet+xml"));
} }
void ContentTypes::addDrawingName(const QString &name)
{
addOverride(QStringLiteral("/xl/drawings/%1.xml").arg(name), m_document_prefix + QStringLiteral("drawing+xml"));
}
void ContentTypes::addChartName(const QString &name) void ContentTypes::addChartName(const QString &name)
{ {
addOverride(QStringLiteral("/xl/charts/%1.xml").arg(name), m_document_prefix + QStringLiteral("drawingml.chart+xml")); addOverride(QStringLiteral("/xl/charts/%1.xml").arg(name), m_document_prefix + QStringLiteral("drawingml.chart+xml"));
@ -77,7 +82,7 @@ void ContentTypes::addCommentName(const QString &name)
void ContentTypes::addImageTypes(const QStringList &imageTypes) void ContentTypes::addImageTypes(const QStringList &imageTypes)
{ {
foreach (QString type, imageTypes) foreach (QString type, imageTypes)
addOverride(type, QStringLiteral("image/") + type); addDefault(type, QStringLiteral("image/") + type);
} }
void ContentTypes::addTableName(const QString &name) void ContentTypes::addTableName(const QString &name)

174
src/xlsx/xlsxdrawing.cpp

@ -0,0 +1,174 @@
#include "xlsxdrawing_p.h"
#include "xlsxxmlwriter_p.h"
namespace QXlsx {
Drawing::Drawing(QObject *parent) :
QObject(parent)
{
embedded = false;
orientation = 0;
}
void Drawing::saveToXmlFile(QIODevice *device)
{
XmlStreamWriter writer(device);
writer.writeStartDocument(QStringLiteral("1.0"), true);
writer.writeStartElement(QStringLiteral("xdr:wsDr"));
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);
}
writer.writeEndElement();//xdr:wsDr
writer.writeEndDocument();
}
void Drawing::writeTwoCellAnchor(XmlStreamWriter &writer, int index, XlsxDrawingDimensionData *data)
{
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
}
writer.writeEmptyElement(QStringLiteral("xdr:clientData"));
writer.writeEndElement(); //xdr:twoCellAnchor
}
void Drawing::writeAbsoluteAnchor(XmlStreamWriter &writer, int index)
{
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(XmlStreamWriter &writer, int x, int y)
{
writer.writeEmptyElement(QStringLiteral("xdr:pos"));
writer.writeAttribute(QStringLiteral("x"), QString::number(x));
writer.writeAttribute(QStringLiteral("y"), QString::number(y));
}
void Drawing::writeExt(XmlStreamWriter &writer, int cx, int cy)
{
writer.writeStartElement(QStringLiteral("xdr:ext"));
writer.writeAttribute(QStringLiteral("cx"), QString::number(cx));
writer.writeAttribute(QStringLiteral("cy"), QString::number(cy));
}
void Drawing::writeGraphicFrame(XmlStreamWriter &writer, int index, const QString &name)
{
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(XmlStreamWriter &writer, int index, double col_abs, double row_abs, int width, int height, const QString &description)
{
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

53
src/xlsx/xlsxdrawing_p.h

@ -0,0 +1,53 @@
#ifndef QXLSX_DRAWING_H
#define QXLSX_DRAWING_H
#include <QObject>
#include <QList>
class QIODevice;
namespace QXlsx {
class XmlStreamWriter;
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 Drawing : public QObject
{
Q_OBJECT
public:
explicit Drawing(QObject *parent = 0);
void saveToXmlFile(QIODevice *device);
bool embedded;
int orientation;
QList <XlsxDrawingDimensionData *> dimensionList;
private:
void writeTwoCellAnchor(XmlStreamWriter &writer, int index, XlsxDrawingDimensionData *data);
void writeAbsoluteAnchor(XmlStreamWriter &writer, int index);
void writePos(XmlStreamWriter &writer, int x, int y);
void writeExt(XmlStreamWriter &writer, int cx, int cy);
void writeGraphicFrame(XmlStreamWriter &writer, int index, const QString &name=QString());
void writePicture(XmlStreamWriter &writer, int index, double col_abs, double row_abs, int width, int height, const QString &description);
};
} // namespace QXlsx
#endif // QXLSX_DRAWING_H

72
src/xlsx/xlsxpackage.cpp

@ -33,6 +33,7 @@
#include "xlsxstyles_p.h" #include "xlsxstyles_p.h"
#include "xlsxrelationships_p.h" #include "xlsxrelationships_p.h"
#include "xlsxzipwriter_p.h" #include "xlsxzipwriter_p.h"
#include "xlsxdrawing_p.h"
#include <QBuffer> #include <QBuffer>
#include <QDebug> #include <QDebug>
@ -93,11 +94,13 @@ bool Package::createPackage(const QString &packageName)
return false; return false;
m_workbook->styles()->clearExtraFormatInfo(); //These info will be generated when write the worksheet data. m_workbook->styles()->clearExtraFormatInfo(); //These info will be generated when write the worksheet data.
m_workbook->prepareDrawings();
writeWorksheetFiles(zipWriter); writeWorksheetFiles(zipWriter);
// writeChartsheetFiles(zipWriter); // writeChartsheetFiles(zipWriter);
writeWorkbookFile(zipWriter); writeWorkbookFile(zipWriter);
// writeChartFiles(zipWriter); // writeChartFiles(zipWriter);
// writeDrawingFiles(zipWriter); writeDrawingFiles(zipWriter);
// writeVmlFiles(zipWriter); // writeVmlFiles(zipWriter);
// writeCommentFiles(zipWriter); // writeCommentFiles(zipWriter);
// writeTableFiles(zipWriter); // writeTableFiles(zipWriter);
@ -110,9 +113,10 @@ bool Package::createPackage(const QString &packageName)
writeThemeFile(zipWriter); writeThemeFile(zipWriter);
writeRootRelsFile(zipWriter); writeRootRelsFile(zipWriter);
writeWorkbookRelsFile(zipWriter); writeWorkbookRelsFile(zipWriter);
writeWorksheetRelsFile(zipWriter); writeWorksheetRelsFiles(zipWriter);
// writeChartsheetRelsFile(zipWriter); // writeChartsheetRelsFile(zipWriter);
// writeImageFiles(zipWriter); writeDrawingRelsFiles(zipWriter);
writeImageFiles(zipWriter);
// writeVbaProjectFiles(zipWriter); // writeVbaProjectFiles(zipWriter);
zipWriter.close(); zipWriter.close();
@ -144,6 +148,19 @@ void Package::writeWorkbookFile(ZipWriter &zipWriter)
zipWriter.addFile(QStringLiteral("xl/workbook.xml"), data); zipWriter.addFile(QStringLiteral("xl/workbook.xml"), data);
} }
void Package::writeDrawingFiles(ZipWriter &zipWriter)
{
for (int i=0; i<m_workbook->drawings().size(); ++i) {
Drawing *drawing = m_workbook->drawings()[i];
QByteArray data;
QBuffer buffer(&data);
buffer.open(QIODevice::WriteOnly);
drawing->saveToXmlFile(&buffer);
zipWriter.addFile(QStringLiteral("xl/drawings/drawing%1.xml").arg(i+1), data);
}
}
void Package::writeContentTypesFiles(ZipWriter &zipWriter) void Package::writeContentTypesFiles(ZipWriter &zipWriter)
{ {
ContentTypes content; ContentTypes content;
@ -158,6 +175,15 @@ void Package::writeContentTypesFiles(ZipWriter &zipWriter)
} }
} }
int drawing_index = 1;
foreach (Drawing *drawing, m_workbook->drawings()) {
content.addDrawingName(QStringLiteral("drawing%1").arg(drawing_index));
drawing_index += 1;
}
if (!m_workbook->images().isEmpty())
content.addImageTypes(QStringList()<<QStringLiteral("png"));
if (m_workbook->sharedStrings()->count()) if (m_workbook->sharedStrings()->count())
content.addSharedString(); content.addSharedString();
@ -283,7 +309,7 @@ void Package::writeWorkbookRelsFile(ZipWriter &zipWriter)
zipWriter.addFile(QStringLiteral("xl/_rels/workbook.xml.rels"), data); zipWriter.addFile(QStringLiteral("xl/_rels/workbook.xml.rels"), data);
} }
void Package::writeWorksheetRelsFile(ZipWriter &zipWriter) void Package::writeWorksheetRelsFiles(ZipWriter &zipWriter)
{ {
int index = 1; int index = 1;
foreach (Worksheet *sheet, m_workbook->worksheets()) { foreach (Worksheet *sheet, m_workbook->worksheets()) {
@ -293,7 +319,8 @@ void Package::writeWorksheetRelsFile(ZipWriter &zipWriter)
foreach (QString link, sheet->externUrlList()) foreach (QString link, sheet->externUrlList())
rels.addWorksheetRelationship(QStringLiteral("/hyperlink"), link, QStringLiteral("External")); rels.addWorksheetRelationship(QStringLiteral("/hyperlink"), link, QStringLiteral("External"));
foreach (QString link, sheet->externDrawingList())
rels.addWorksheetRelationship(QStringLiteral("/drawing"), link);
QByteArray data; QByteArray data;
QBuffer buffer(&data); QBuffer buffer(&data);
buffer.open(QIODevice::WriteOnly); buffer.open(QIODevice::WriteOnly);
@ -302,4 +329,39 @@ void Package::writeWorksheetRelsFile(ZipWriter &zipWriter)
index += 1; index += 1;
} }
} }
void Package::writeDrawingRelsFiles(ZipWriter &zipWriter)
{
int index = 1;
foreach (Worksheet *sheet, m_workbook->worksheets()) {
if (sheet->drawingLinks().size() == 0)
continue;
Relationships rels;
typedef QPair<QString, QString> PairType;
foreach (PairType pair, sheet->drawingLinks())
rels.addDocumentRelationship(pair.first, pair.second);
QByteArray data;
QBuffer buffer(&data);
buffer.open(QIODevice::WriteOnly);
rels.saveToXmlFile(&buffer);
zipWriter.addFile(QStringLiteral("xl/drawings/_rels/drawing%1.xml.rels").arg(index), data);
index += 1;
}
}
void Package::writeImageFiles(ZipWriter &zipWriter)
{
for (int i=0; i<m_workbook->images().size(); ++i) {
QImage image = m_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);
}
}
} // namespace QXlsx } // namespace QXlsx

7
src/xlsx/xlsxpackage_p.h

@ -44,7 +44,7 @@ private:
// void writeChartsheetFiles(ZipWriter &zipWriter); // void writeChartsheetFiles(ZipWriter &zipWriter);
void writeWorkbookFile(ZipWriter &zipWriter); void writeWorkbookFile(ZipWriter &zipWriter);
// void writeChartFiles(ZipWriter &zipWriter); // void writeChartFiles(ZipWriter &zipWriter);
// void writeDrawingFiles(ZipWriter &zipWriter); void writeDrawingFiles(ZipWriter &zipWriter);
// void writeVmlFiles(ZipWriter &zipWriter); // void writeVmlFiles(ZipWriter &zipWriter);
// void writeCommentFiles(ZipWriter &zipWriter); // void writeCommentFiles(ZipWriter &zipWriter);
// void writeTableFiles(ZipWriter &zipWriter); // void writeTableFiles(ZipWriter &zipWriter);
@ -56,9 +56,10 @@ private:
void writeThemeFile(ZipWriter &zipWriter); void writeThemeFile(ZipWriter &zipWriter);
void writeRootRelsFile(ZipWriter &zipWriter); void writeRootRelsFile(ZipWriter &zipWriter);
void writeWorkbookRelsFile(ZipWriter &zipWriter); void writeWorkbookRelsFile(ZipWriter &zipWriter);
void writeWorksheetRelsFile(ZipWriter &zipWriter); void writeWorksheetRelsFiles(ZipWriter &zipWriter);
// void writeChartsheetRelsFile(ZipWriter &zipWriter); // void writeChartsheetRelsFile(ZipWriter &zipWriter);
// void writeImageFiles(ZipWriter &zipWriter); void writeDrawingRelsFiles(ZipWriter &zipWriter);
void writeImageFiles(ZipWriter &zipWriter);
// void writeVbaProjectFiles(ZipWriter &zipWriter); // void writeVbaProjectFiles(ZipWriter &zipWriter);
Workbook * m_workbook; Workbook * m_workbook;

39
src/xlsx/xlsxworkbook.cpp

@ -30,6 +30,7 @@
#include "xlsxformat.h" #include "xlsxformat.h"
#include "xlsxpackage_p.h" #include "xlsxpackage_p.h"
#include "xlsxxmlwriter_p.h" #include "xlsxxmlwriter_p.h"
#include "xlsxworksheet_p.h"
namespace QXlsx { namespace QXlsx {
@ -221,6 +222,44 @@ Styles *Workbook::styles()
return d->styles; return d->styles;
} }
QList<QImage> Workbook::images()
{
Q_D(Workbook);
return d->images;
}
QList<Drawing *> Workbook::drawings()
{
Q_D(Workbook);
return d->drawings;
}
void Workbook::prepareDrawings()
{
Q_D(Workbook);
int drawing_id = 0;
int image_ref_id = 0;
d->images.clear();
d->drawings.clear();
foreach (Worksheet *sheet, d->worksheets) {
if (sheet->images().isEmpty()) //No drawing (such as Image, ...)
continue;
sheet->clearExtraDrawingInfo();
//At present, only picture type supported
drawing_id += 1;
for (int idx = 0; idx < sheet->images().size(); ++idx) {
image_ref_id += 1;
sheet->prepareImage(idx, image_ref_id, drawing_id);
d->images.append(sheet->images()[idx]->image);
}
d->drawings.append(sheet->drawing());
}
}
void Workbook::saveToXmlFile(QIODevice *device) void Workbook::saveToXmlFile(QIODevice *device)
{ {
Q_D(Workbook); Q_D(Workbook);

5
src/xlsx/xlsxworkbook.h

@ -28,6 +28,7 @@
#include "xlsxglobal.h" #include "xlsxglobal.h"
#include <QObject> #include <QObject>
#include <QList> #include <QList>
#include <QImage>
class QIODevice; class QIODevice;
namespace QXlsx { namespace QXlsx {
@ -37,6 +38,7 @@ class Format;
class SharedStrings; class SharedStrings;
class Styles; class Styles;
class Package; class Package;
class Drawing;
class WorkbookPrivate; class WorkbookPrivate;
class Q_XLSX_EXPORT Workbook : public QObject class Q_XLSX_EXPORT Workbook : public QObject
@ -73,6 +75,9 @@ private:
SharedStrings *sharedStrings(); SharedStrings *sharedStrings();
Styles *styles(); Styles *styles();
QList<QImage> images();
QList<Drawing *> drawings();
void prepareDrawings();
void saveToXmlFile(QIODevice *device); void saveToXmlFile(QIODevice *device);
WorkbookPrivate * const d_ptr; WorkbookPrivate * const d_ptr;

2
src/xlsx/xlsxworkbook_p.h

@ -39,6 +39,8 @@ public:
SharedStrings *sharedStrings; SharedStrings *sharedStrings;
QList<Worksheet *> worksheets; QList<Worksheet *> worksheets;
Styles *styles; Styles *styles;
QList<QImage> images;
QList<Drawing *> drawings;
bool strings_to_numbers_enabled; bool strings_to_numbers_enabled;
bool date1904; bool date1904;

219
src/xlsx/xlsxworksheet.cpp

@ -29,6 +29,7 @@
#include "xlsxutility_p.h" #include "xlsxutility_p.h"
#include "xlsxsharedstrings_p.h" #include "xlsxsharedstrings_p.h"
#include "xlsxxmlwriter_p.h" #include "xlsxxmlwriter_p.h"
#include "xlsxdrawing_p.h"
#include <QVariant> #include <QVariant>
#include <QDateTime> #include <QDateTime>
@ -45,6 +46,8 @@ namespace QXlsx {
WorksheetPrivate::WorksheetPrivate(Worksheet *p) : WorksheetPrivate::WorksheetPrivate(Worksheet *p) :
q_ptr(p) q_ptr(p)
{ {
drawing = 0;
xls_rowmax = 1048576; xls_rowmax = 1048576;
xls_colmax = 16384; xls_colmax = 16384;
xls_strmax = 32767; xls_strmax = 32767;
@ -264,6 +267,18 @@ QStringList Worksheet::externUrlList() const
return d->externUrlList; return d->externUrlList;
} }
QStringList Worksheet::externDrawingList() const
{
Q_D(const Worksheet);
return d->externDrawingList;
}
QList<QPair<QString, QString> > Worksheet::drawingLinks() const
{
Q_D(const Worksheet);
return d->drawingLinks;
}
int Worksheet::write(int row, int column, const QVariant &value, Format *format) int Worksheet::write(int row, int column, const QVariant &value, Format *format)
{ {
Q_D(Worksheet); Q_D(Worksheet);
@ -461,6 +476,14 @@ int Worksheet::writeUrl(int row, int column, const QUrl &url, Format *format, co
return error; return error;
} }
int Worksheet::insertImage(int row, int column, const QImage &image, const QPointF &offset, double xScale, double yScale)
{
Q_D(Worksheet);
d->imageList.append(new XlsxImageData(row, column, image, offset, xScale, yScale));
return 0;
}
void Worksheet::saveToXmlFile(QIODevice *device) void Worksheet::saveToXmlFile(QIODevice *device)
{ {
Q_D(Worksheet); Q_D(Worksheet);
@ -533,6 +556,7 @@ void Worksheet::saveToXmlFile(QIODevice *device)
writer.writeEndElement();//sheetData writer.writeEndElement();//sheetData
d->writeHyperlinks(writer); d->writeHyperlinks(writer);
d->writeDrawings(writer);
writer.writeEndElement();//worksheet writer.writeEndElement();//worksheet
writer.writeEndDocument(); writer.writeEndDocument();
@ -681,6 +705,13 @@ void WorksheetPrivate::writeHyperlinks(XmlStreamWriter &writer)
writer.writeEndElement();//hyperlinks writer.writeEndElement();//hyperlinks
} }
void WorksheetPrivate::writeDrawings(XmlStreamWriter &writer)
{
int index = externUrlList.size() + 1;
writer.writeEmptyElement(QStringLiteral("drawing"));
writer.writeAttribute(QStringLiteral("r:id"), QStringLiteral("rId%1").arg(index));
}
/* /*
Sets row height and format. Row height measured in point size. If format Sets row height and format. Row height measured in point size. If format
equals 0 then format is ignored. equals 0 then format is ignored.
@ -729,4 +760,192 @@ bool Worksheet::setColumn(int colFirst, int colLast, double width, Format *forma
return true; return true;
} }
Drawing *Worksheet::drawing() const
{
Q_D(const Worksheet);
return d->drawing;
}
QList<XlsxImageData *> Worksheet::images() const
{
Q_D(const Worksheet);
return d->imageList;
}
void Worksheet::clearExtraDrawingInfo()
{
Q_D(Worksheet);
if (d->drawing) {
delete d->drawing;
d->drawing = 0;
d->externDrawingList.clear();
d->drawingLinks.clear();
}
}
void Worksheet::prepareImage(int index, int image_id, int drawing_id)
{
Q_D(Worksheet);
if (!d->drawing) {
d->drawing = new Drawing(this);
d->drawing->embedded = true;
d->externDrawingList.append(QStringLiteral("../drawings/drawing%1.xml").arg(drawing_id));
}
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)));
}
/*
Convert the height of a cell from user's units to pixels. If the
height hasn't been set by the user we use the default value. If
the row is hidden it has a value of zero.
*/
int WorksheetPrivate::rowPixelsSize(int row)
{
double height;
if (row_sizes.contains(row))
height = row_sizes[row];
else
height = default_row_height;
return static_cast<int>(4.0 / 3.0 *height);
}
/*
Convert the width of a cell from user's units to pixels. Excel rounds
the column width to the nearest pixel. If the width hasn't been set
by the user we use the default value. If the column is hidden it
has a value of zero.
*/
int WorksheetPrivate::colPixelsSize(int col)
{
double max_digit_width = 7.0; //For Calabri 11
double padding = 5.0;
int pixels = 0;
if (col_sizes.contains(col)) {
double width = col_sizes[col];
if (width < 1)
pixels = static_cast<int>(width * (max_digit_width + padding) + 0.5);
else
pixels = static_cast<int>(width * max_digit_width + 0.5) + padding;
} else {
pixels = 64;
}
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)
{
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)
{
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;
}
} //namespace } //namespace

12
src/xlsx/xlsxworksheet.h

@ -30,15 +30,19 @@
#include <QStringList> #include <QStringList>
#include <QMap> #include <QMap>
#include <QVariant> #include <QVariant>
#include <QPointF>
class QIODevice; class QIODevice;
class QDateTime; class QDateTime;
class QUrl; class QUrl;
class QImage;
namespace QXlsx { namespace QXlsx {
class Package; class Package;
class Workbook; class Workbook;
class XmlStreamWriter; class XmlStreamWriter;
class Format; class Format;
class Drawing;
struct XlsxImageData;
class WorksheetPrivate; class WorksheetPrivate;
class Q_XLSX_EXPORT Worksheet : public QObject class Q_XLSX_EXPORT Worksheet : public QObject
@ -56,6 +60,8 @@ public:
int writeDateTime(int row, int column, const QDateTime& dt, Format *format=0); int writeDateTime(int row, int column, const QDateTime& dt, Format *format=0);
int writeUrl(int row, int column, const QUrl &url, Format *format=0, const QString &display=QString(), const QString &tip=QString()); int writeUrl(int row, int column, const QUrl &url, Format *format=0, const QString &display=QString(), const QString &tip=QString());
int insertImage(int row, int column, const QImage &image, const QPointF &offset=QPointF(), double xScale=1, double yScale=1);
bool setRow(int row, double height, Format* format=0, bool hidden=false); bool setRow(int row, double height, Format* format=0, bool hidden=false);
bool setColumn(int colFirst, int colLast, double width, Format* format=0, bool hidden=false); bool setColumn(int colFirst, int colLast, double width, Format* format=0, bool hidden=false);
@ -76,6 +82,12 @@ private:
void setSelected(bool select); void setSelected(bool select);
void saveToXmlFile(QIODevice *device); void saveToXmlFile(QIODevice *device);
QStringList externUrlList() const; QStringList externUrlList() const;
QStringList externDrawingList() const;
QList<QPair<QString, QString> > drawingLinks() const;
Drawing *drawing() const;
QList<XlsxImageData *> images() const;
void prepareImage(int index, int image_id, int drawing_id);
void clearExtraDrawingInfo();
WorksheetPrivate * const d_ptr; WorksheetPrivate * const d_ptr;
}; };

74
src/xlsx/xlsxworksheet_p.h

@ -26,6 +26,8 @@
#define XLSXWORKSHEET_P_H #define XLSXWORKSHEET_P_H
#include "xlsxworksheet.h" #include "xlsxworksheet.h"
#include <QImage>
namespace QXlsx { namespace QXlsx {
struct XlsxCellData struct XlsxCellData
@ -65,6 +67,67 @@ struct XlsxUrlData
QString tip; QString tip;
}; };
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, Format *format, bool hidden) : XlsxRowInfo(double height, Format *format, bool hidden) :
@ -104,15 +167,24 @@ public:
void writeSheetData(XmlStreamWriter &writer); void writeSheetData(XmlStreamWriter &writer);
void writeCellData(XmlStreamWriter &writer, int row, int col, XlsxCellData *cell); void writeCellData(XmlStreamWriter &writer, int row, int col, XlsxCellData *cell);
void writeHyperlinks(XmlStreamWriter &writer); void writeHyperlinks(XmlStreamWriter &writer);
void writeDrawings(XmlStreamWriter &writer);
int rowPixelsSize(int row);
int colPixelsSize(int col);
XlsxObjectPositionData objectPixelsPosition(int col_start, int row_start, double x1, double y1, double width, double height);
XlsxObjectPositionData pixelsToEMUs(const XlsxObjectPositionData &data);
Workbook *workbook; Workbook *workbook;
Drawing *drawing;
QMap<int, QMap<int, XlsxCellData *> > cellTable; QMap<int, QMap<int, XlsxCellData *> > cellTable;
QMap<int, QMap<int, QString> > comments; QMap<int, QMap<int, QString> > comments;
QMap<int, QMap<int, XlsxUrlData *> > urlTable; QMap<int, QMap<int, XlsxUrlData *> > urlTable;
QStringList externUrlList; QStringList externUrlList;
QStringList externDrawingList;
QList<XlsxImageData *> imageList;
QMap<int, XlsxRowInfo *> rowsInfo; QMap<int, XlsxRowInfo *> rowsInfo;
QList<XlsxColumnInfo *> colsInfo; QList<XlsxColumnInfo *> colsInfo;
QMap<int, XlsxColumnInfo *> colsInfoHelper;//Not owns the XlsxColumnInfo QMap<int, XlsxColumnInfo *> colsInfoHelper;//Not owns the XlsxColumnInfo
QList<QPair<QString, QString> > drawingLinks;
int xls_rowmax; int xls_rowmax;
int xls_colmax; int xls_colmax;
@ -124,6 +196,8 @@ public:
int previous_row; int previous_row;
QMap<int, QString> row_spans; QMap<int, QString> row_spans;
QMap<int, double> row_sizes;
QMap<int, double> col_sizes;
int outline_row_level; int outline_row_level;
int outline_col_level; int outline_col_level;

Loading…
Cancel
Save