diff --git a/examples/xlsx/image/image.pro b/examples/xlsx/image/image.pro new file mode 100644 index 0000000..5086d54 --- /dev/null +++ b/examples/xlsx/image/image.pro @@ -0,0 +1,6 @@ +TARGET = image + +#include(../../../src/xlsx/qtxlsx.pri) +QT += xlsx + +SOURCES += main.cpp diff --git a/examples/xlsx/image/main.cpp b/examples/xlsx/image/main.cpp new file mode 100644 index 0000000..9a05111 --- /dev/null +++ b/examples/xlsx/image/main.cpp @@ -0,0 +1,25 @@ +#include +#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; +} diff --git a/examples/xlsx/xlsx.pro b/examples/xlsx/xlsx.pro index ce62f3a..50c714b 100644 --- a/examples/xlsx/xlsx.pro +++ b/examples/xlsx/xlsx.pro @@ -1,4 +1,5 @@ TEMPLATE = subdirs SUBDIRS = hello style \ - documentproperty + documentproperty \ + image diff --git a/src/xlsx/qtxlsx.pri b/src/xlsx/qtxlsx.pri index c013dfc..4033050 100755 --- a/src/xlsx/qtxlsx.pri +++ b/src/xlsx/qtxlsx.pri @@ -21,7 +21,8 @@ HEADERS += $$PWD/xlsxdocpropscore_p.h \ $$PWD/xlsxworkbook_p.h \ $$PWD/xlsxworksheet_p.h \ $$PWD/xlsxformat_p.h \ - $$PWD/xlsxglobal.h + $$PWD/xlsxglobal.h \ + $$PWD/xlsxdrawing_p.h SOURCES += $$PWD/xlsxdocpropscore.cpp \ $$PWD/xlsxdocpropsapp.cpp \ @@ -36,4 +37,5 @@ SOURCES += $$PWD/xlsxdocpropscore.cpp \ $$PWD/xlsxworkbook.cpp \ $$PWD/xlsxworksheet.cpp \ $$PWD/xlsxzipwriter.cpp \ - $$PWD/xlsxpackage.cpp + $$PWD/xlsxpackage.cpp \ + $$PWD/xlsxdrawing.cpp diff --git a/src/xlsx/xlsxcontenttypes.cpp b/src/xlsx/xlsxcontenttypes.cpp index fdb5610..2cdd7be 100755 --- a/src/xlsx/xlsxcontenttypes.cpp +++ b/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")); } +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) { 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) { foreach (QString type, imageTypes) - addOverride(type, QStringLiteral("image/") + type); + addDefault(type, QStringLiteral("image/") + type); } void ContentTypes::addTableName(const QString &name) diff --git a/src/xlsx/xlsxdrawing.cpp b/src/xlsx/xlsxdrawing.cpp new file mode 100644 index 0000000..f4f8cfa --- /dev/null +++ b/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 diff --git a/src/xlsx/xlsxdrawing_p.h b/src/xlsx/xlsxdrawing_p.h new file mode 100644 index 0000000..546b295 --- /dev/null +++ b/src/xlsx/xlsxdrawing_p.h @@ -0,0 +1,53 @@ +#ifndef QXLSX_DRAWING_H +#define QXLSX_DRAWING_H + +#include +#include + +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 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 diff --git a/src/xlsx/xlsxpackage.cpp b/src/xlsx/xlsxpackage.cpp index 0e0fb57..60ec40a 100644 --- a/src/xlsx/xlsxpackage.cpp +++ b/src/xlsx/xlsxpackage.cpp @@ -33,6 +33,7 @@ #include "xlsxstyles_p.h" #include "xlsxrelationships_p.h" #include "xlsxzipwriter_p.h" +#include "xlsxdrawing_p.h" #include #include @@ -93,11 +94,13 @@ bool Package::createPackage(const QString &packageName) return false; m_workbook->styles()->clearExtraFormatInfo(); //These info will be generated when write the worksheet data. + m_workbook->prepareDrawings(); + writeWorksheetFiles(zipWriter); // writeChartsheetFiles(zipWriter); writeWorkbookFile(zipWriter); // writeChartFiles(zipWriter); -// writeDrawingFiles(zipWriter); + writeDrawingFiles(zipWriter); // writeVmlFiles(zipWriter); // writeCommentFiles(zipWriter); // writeTableFiles(zipWriter); @@ -110,9 +113,10 @@ bool Package::createPackage(const QString &packageName) writeThemeFile(zipWriter); writeRootRelsFile(zipWriter); writeWorkbookRelsFile(zipWriter); - writeWorksheetRelsFile(zipWriter); + writeWorksheetRelsFiles(zipWriter); // writeChartsheetRelsFile(zipWriter); -// writeImageFiles(zipWriter); + writeDrawingRelsFiles(zipWriter); + writeImageFiles(zipWriter); // writeVbaProjectFiles(zipWriter); zipWriter.close(); @@ -144,6 +148,19 @@ void Package::writeWorkbookFile(ZipWriter &zipWriter) zipWriter.addFile(QStringLiteral("xl/workbook.xml"), data); } +void Package::writeDrawingFiles(ZipWriter &zipWriter) +{ + for (int i=0; idrawings().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) { 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()<sharedStrings()->count()) content.addSharedString(); @@ -283,7 +309,7 @@ void Package::writeWorkbookRelsFile(ZipWriter &zipWriter) zipWriter.addFile(QStringLiteral("xl/_rels/workbook.xml.rels"), data); } -void Package::writeWorksheetRelsFile(ZipWriter &zipWriter) +void Package::writeWorksheetRelsFiles(ZipWriter &zipWriter) { int index = 1; foreach (Worksheet *sheet, m_workbook->worksheets()) { @@ -293,7 +319,8 @@ void Package::writeWorksheetRelsFile(ZipWriter &zipWriter) foreach (QString link, sheet->externUrlList()) rels.addWorksheetRelationship(QStringLiteral("/hyperlink"), link, QStringLiteral("External")); - + foreach (QString link, sheet->externDrawingList()) + rels.addWorksheetRelationship(QStringLiteral("/drawing"), link); QByteArray data; QBuffer buffer(&data); buffer.open(QIODevice::WriteOnly); @@ -302,4 +329,39 @@ void Package::writeWorksheetRelsFile(ZipWriter &zipWriter) 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 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; iimages().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 diff --git a/src/xlsx/xlsxpackage_p.h b/src/xlsx/xlsxpackage_p.h index 927471d..64973ab 100644 --- a/src/xlsx/xlsxpackage_p.h +++ b/src/xlsx/xlsxpackage_p.h @@ -44,7 +44,7 @@ private: // void writeChartsheetFiles(ZipWriter &zipWriter); void writeWorkbookFile(ZipWriter &zipWriter); // void writeChartFiles(ZipWriter &zipWriter); -// void writeDrawingFiles(ZipWriter &zipWriter); + void writeDrawingFiles(ZipWriter &zipWriter); // void writeVmlFiles(ZipWriter &zipWriter); // void writeCommentFiles(ZipWriter &zipWriter); // void writeTableFiles(ZipWriter &zipWriter); @@ -56,9 +56,10 @@ private: void writeThemeFile(ZipWriter &zipWriter); void writeRootRelsFile(ZipWriter &zipWriter); void writeWorkbookRelsFile(ZipWriter &zipWriter); - void writeWorksheetRelsFile(ZipWriter &zipWriter); + void writeWorksheetRelsFiles(ZipWriter &zipWriter); // void writeChartsheetRelsFile(ZipWriter &zipWriter); -// void writeImageFiles(ZipWriter &zipWriter); + void writeDrawingRelsFiles(ZipWriter &zipWriter); + void writeImageFiles(ZipWriter &zipWriter); // void writeVbaProjectFiles(ZipWriter &zipWriter); Workbook * m_workbook; diff --git a/src/xlsx/xlsxworkbook.cpp b/src/xlsx/xlsxworkbook.cpp index bf12ef1..96c432a 100755 --- a/src/xlsx/xlsxworkbook.cpp +++ b/src/xlsx/xlsxworkbook.cpp @@ -30,6 +30,7 @@ #include "xlsxformat.h" #include "xlsxpackage_p.h" #include "xlsxxmlwriter_p.h" +#include "xlsxworksheet_p.h" namespace QXlsx { @@ -221,6 +222,44 @@ Styles *Workbook::styles() return d->styles; } +QList Workbook::images() +{ + Q_D(Workbook); + return d->images; +} + +QList 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) { Q_D(Workbook); diff --git a/src/xlsx/xlsxworkbook.h b/src/xlsx/xlsxworkbook.h index dae9103..989560b 100755 --- a/src/xlsx/xlsxworkbook.h +++ b/src/xlsx/xlsxworkbook.h @@ -28,6 +28,7 @@ #include "xlsxglobal.h" #include #include +#include class QIODevice; namespace QXlsx { @@ -37,6 +38,7 @@ class Format; class SharedStrings; class Styles; class Package; +class Drawing; class WorkbookPrivate; class Q_XLSX_EXPORT Workbook : public QObject @@ -73,6 +75,9 @@ private: SharedStrings *sharedStrings(); Styles *styles(); + QList images(); + QList drawings(); + void prepareDrawings(); void saveToXmlFile(QIODevice *device); WorkbookPrivate * const d_ptr; diff --git a/src/xlsx/xlsxworkbook_p.h b/src/xlsx/xlsxworkbook_p.h index 81eb502..2405ea1 100644 --- a/src/xlsx/xlsxworkbook_p.h +++ b/src/xlsx/xlsxworkbook_p.h @@ -39,6 +39,8 @@ public: SharedStrings *sharedStrings; QList worksheets; Styles *styles; + QList images; + QList drawings; bool strings_to_numbers_enabled; bool date1904; diff --git a/src/xlsx/xlsxworksheet.cpp b/src/xlsx/xlsxworksheet.cpp index c0edc02..279d877 100755 --- a/src/xlsx/xlsxworksheet.cpp +++ b/src/xlsx/xlsxworksheet.cpp @@ -29,6 +29,7 @@ #include "xlsxutility_p.h" #include "xlsxsharedstrings_p.h" #include "xlsxxmlwriter_p.h" +#include "xlsxdrawing_p.h" #include #include @@ -45,6 +46,8 @@ namespace QXlsx { WorksheetPrivate::WorksheetPrivate(Worksheet *p) : q_ptr(p) { + drawing = 0; + xls_rowmax = 1048576; xls_colmax = 16384; xls_strmax = 32767; @@ -264,6 +267,18 @@ QStringList Worksheet::externUrlList() const return d->externUrlList; } +QStringList Worksheet::externDrawingList() const +{ + Q_D(const Worksheet); + return d->externDrawingList; +} + +QList > Worksheet::drawingLinks() const +{ + Q_D(const Worksheet); + return d->drawingLinks; +} + int Worksheet::write(int row, int column, const QVariant &value, Format *format) { Q_D(Worksheet); @@ -461,6 +476,14 @@ int Worksheet::writeUrl(int row, int column, const QUrl &url, Format *format, co 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) { Q_D(Worksheet); @@ -533,6 +556,7 @@ void Worksheet::saveToXmlFile(QIODevice *device) writer.writeEndElement();//sheetData d->writeHyperlinks(writer); + d->writeDrawings(writer); writer.writeEndElement();//worksheet writer.writeEndDocument(); @@ -681,6 +705,13 @@ void WorksheetPrivate::writeHyperlinks(XmlStreamWriter &writer) 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 equals 0 then format is ignored. @@ -729,4 +760,192 @@ bool Worksheet::setColumn(int colFirst, int colLast, double width, Format *forma return true; } +Drawing *Worksheet::drawing() const +{ + Q_D(const Worksheet); + return d->drawing; +} + +QList 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(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(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(width * (max_digit_width + padding) + 0.5); + else + pixels = static_cast(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(data.x1 * 9525 + 0.5); + result.y1 = static_cast(data.y1 * 9525 + 0.5); + result.x2 = static_cast(data.x2 * 9525 + 0.5); + result.y2 = static_cast(data.y2 * 9525 + 0.5); + result.x_abs = static_cast(data.x_abs * 9525 + 0.5); + result.y_abs = static_cast(data.y_abs * 9525 + 0.5); + result.width = static_cast(data.width * 9525 + 0.5); + result.height = static_cast(data.height * 9525 + 0.5); + + return result; +} + } //namespace diff --git a/src/xlsx/xlsxworksheet.h b/src/xlsx/xlsxworksheet.h index 6d1cb09..516e9c7 100755 --- a/src/xlsx/xlsxworksheet.h +++ b/src/xlsx/xlsxworksheet.h @@ -30,15 +30,19 @@ #include #include #include +#include class QIODevice; class QDateTime; class QUrl; +class QImage; namespace QXlsx { class Package; class Workbook; class XmlStreamWriter; class Format; +class Drawing; +struct XlsxImageData; class WorksheetPrivate; 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 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 setColumn(int colFirst, int colLast, double width, Format* format=0, bool hidden=false); @@ -76,6 +82,12 @@ private: void setSelected(bool select); void saveToXmlFile(QIODevice *device); QStringList externUrlList() const; + QStringList externDrawingList() const; + QList > drawingLinks() const; + Drawing *drawing() const; + QList images() const; + void prepareImage(int index, int image_id, int drawing_id); + void clearExtraDrawingInfo(); WorksheetPrivate * const d_ptr; }; diff --git a/src/xlsx/xlsxworksheet_p.h b/src/xlsx/xlsxworksheet_p.h index 874643c..462c053 100644 --- a/src/xlsx/xlsxworksheet_p.h +++ b/src/xlsx/xlsxworksheet_p.h @@ -26,6 +26,8 @@ #define XLSXWORKSHEET_P_H #include "xlsxworksheet.h" +#include + namespace QXlsx { struct XlsxCellData @@ -65,6 +67,67 @@ struct XlsxUrlData 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 { XlsxRowInfo(double height, Format *format, bool hidden) : @@ -104,15 +167,24 @@ public: void writeSheetData(XmlStreamWriter &writer); void writeCellData(XmlStreamWriter &writer, int row, int col, XlsxCellData *cell); 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; + Drawing *drawing; QMap > cellTable; QMap > comments; QMap > urlTable; QStringList externUrlList; + QStringList externDrawingList; + QList imageList; QMap rowsInfo; QList colsInfo; QMap colsInfoHelper;//Not owns the XlsxColumnInfo + QList > drawingLinks; int xls_rowmax; int xls_colmax; @@ -124,6 +196,8 @@ public: int previous_row; QMap row_spans; + QMap row_sizes; + QMap col_sizes; int outline_row_level; int outline_col_level;