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