Debao Zhang
11 years ago
10 changed files with 257 additions and 482 deletions
@ -1,382 +0,0 @@ |
|||
/****************************************************************************
|
|||
** 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 "xlsxpackage_p.h" |
|||
#include "xlsxworkbook.h" |
|||
#include "xlsxworksheet.h" |
|||
#include "xlsxworkbook_p.h" |
|||
#include "xlsxutility_p.h" |
|||
#include "xlsxcontenttypes_p.h" |
|||
#include "xlsxsharedstrings_p.h" |
|||
#include "xlsxdocpropscore_p.h" |
|||
#include "xlsxdocpropsapp_p.h" |
|||
#include "xlsxtheme_p.h" |
|||
#include "xlsxstyles_p.h" |
|||
#include "xlsxrelationships_p.h" |
|||
#include "xlsxzipwriter_p.h" |
|||
#include "xlsxdrawing_p.h" |
|||
#include "xlsxzipreader_p.h" |
|||
#include "xlsxdocument.h" |
|||
#include "xlsxformat_p.h" |
|||
#include <QBuffer> |
|||
#include <QDebug> |
|||
#include <QDir> |
|||
#include <QFileInfo> |
|||
|
|||
namespace QXlsx { |
|||
|
|||
//: TODO This class should be merged to Document class.
|
|||
|
|||
/*
|
|||
From Wikipedia: The Open Packaging Conventions (OPC) is a |
|||
container-file technology initially created by Microsoft to store |
|||
a combination of XML and non-XML files that together form a single |
|||
entity such as an Open XML Paper Specification (OpenXPS) |
|||
document. http://en.wikipedia.org/wiki/Open_Packaging_Conventions.
|
|||
|
|||
At its simplest an Excel XLSX file contains the following elements: |
|||
|
|||
____ [Content_Types].xml |
|||
| |
|||
|____ docProps |
|||
| |____ app.xml |
|||
| |____ core.xml |
|||
| |
|||
|____ xl |
|||
| |____ workbook.xml |
|||
| |____ worksheets |
|||
| | |____ sheet1.xml |
|||
| | |
|||
| |____ styles.xml |
|||
| | |
|||
| |____ theme |
|||
| | |____ theme1.xml |
|||
| | |
|||
| |_____rels |
|||
| |____ workbook.xml.rels |
|||
| |
|||
|_____rels |
|||
|____ .rels |
|||
|
|||
The Packager class coordinates the classes that represent the |
|||
elements of the package and writes them into the XLSX file. |
|||
*/ |
|||
|
|||
Package::Package(Document *document) : |
|||
m_document(document) |
|||
{ |
|||
m_workbook = m_document->workbook(); |
|||
m_worksheet_count = 0; |
|||
m_chartsheet_count = 0; |
|||
for (int i=0; i<m_workbook->worksheetCount(); ++i) { |
|||
Worksheet *sheet = m_workbook->worksheet(i); |
|||
if (sheet->isChartsheet()) |
|||
m_chartsheet_count += 1; |
|||
else |
|||
m_worksheet_count += 1; |
|||
} |
|||
} |
|||
|
|||
bool Package::parsePackage(QIODevice *packageDevice) |
|||
{ |
|||
ZipReader zipReader(packageDevice); |
|||
QStringList filePaths = zipReader.filePaths(); |
|||
|
|||
if (!filePaths.contains(QLatin1String("_rels/.rels"))) |
|||
return false; |
|||
|
|||
Relationships rootRels; |
|||
rootRels.loadFromXmlData(zipReader.fileData(QStringLiteral("_rels/.rels"))); |
|||
|
|||
//load core property
|
|||
QList<XlsxRelationship> rels_core = rootRels.packageRelationships(QStringLiteral("/metadata/core-properties")); |
|||
if (!rels_core.isEmpty()) { |
|||
//Get the core property file name if it exists.
|
|||
//In normal case, this should be "docProps/core.xml"
|
|||
QString docPropsCore_Name = rels_core[0].target; |
|||
|
|||
DocPropsCore props = DocPropsCore::loadFromXmlData(zipReader.fileData(docPropsCore_Name)); |
|||
foreach (QString name, props.propertyNames()) |
|||
m_document->setDocumentProperty(name, props.property(name)); |
|||
} |
|||
|
|||
//load app property
|
|||
QList<XlsxRelationship> rels_app = rootRels.documentRelationships(QStringLiteral("/extended-properties")); |
|||
if (!rels_app.isEmpty()) { |
|||
//Get the app property file name if it exists.
|
|||
//In normal case, this should be "docProps/app.xml"
|
|||
QString docPropsApp_Name = rels_app[0].target; |
|||
|
|||
DocPropsApp props = DocPropsApp::loadFromXmlData(zipReader.fileData(docPropsApp_Name)); |
|||
foreach (QString name, props.propertyNames()) |
|||
m_document->setDocumentProperty(name, props.property(name)); |
|||
} |
|||
|
|||
//load workbook now, Get the workbook file path from the root rels file
|
|||
//In normal case, this should be "xl/workbook.xml"
|
|||
QList<XlsxRelationship> rels_xl = rootRels.documentRelationships(QStringLiteral("/officeDocument")); |
|||
if (rels_xl.isEmpty()) |
|||
return false; |
|||
QString xlworkbook_Path = rels_xl[0].target; |
|||
QStringList xlworkbook_PathList = splitPath(xlworkbook_Path); |
|||
QString xlworkbook_Dir = xlworkbook_PathList[0]; |
|||
QString xlworkbook_Name = xlworkbook_PathList[1]; |
|||
m_document->workbook()->loadFromXmlData(zipReader.fileData(xlworkbook_Path)); |
|||
QList<XlsxSheetItemInfo> sheetNameIdPairList = m_document->workbook()->d_func()->sheetItemInfoList; |
|||
Relationships xlworkbook_Rels; |
|||
xlworkbook_Rels.loadFromXmlData(zipReader.fileData(xlworkbook_Dir+QStringLiteral("/_rels/")+xlworkbook_Name+QStringLiteral(".rels"))); |
|||
|
|||
//load styles
|
|||
QList<XlsxRelationship> rels_styles = xlworkbook_Rels.documentRelationships(QStringLiteral("/styles")); |
|||
if (!rels_styles.isEmpty()) { |
|||
//In normal case this should be styles.xml which in xl
|
|||
QString name = rels_styles[0].target; |
|||
QString path = xlworkbook_Dir + QLatin1String("/") + name; |
|||
QSharedPointer<Styles> styles (new Styles(true)); |
|||
styles->loadFromXmlData(zipReader.fileData(path)); |
|||
m_document->workbook()->d_ptr->styles = styles; |
|||
} |
|||
|
|||
//load sharedStrings
|
|||
QList<XlsxRelationship> rels_sharedStrings = xlworkbook_Rels.documentRelationships(QStringLiteral("/sharedStrings")); |
|||
if (!rels_sharedStrings.isEmpty()) { |
|||
//In normal case this should be sharedStrings.xml which in xl
|
|||
QString name = rels_sharedStrings[0].target; |
|||
QString path = xlworkbook_Dir + QLatin1String("/") + name; |
|||
m_document->workbook()->d_ptr->sharedStrings->loadFromXmlData(zipReader.fileData(path)); |
|||
} |
|||
|
|||
//load theme
|
|||
QList<XlsxRelationship> rels_theme = xlworkbook_Rels.documentRelationships(QStringLiteral("/theme")); |
|||
if (!rels_theme.isEmpty()) { |
|||
//In normal case this should be theme/theme1.xml which in xl
|
|||
QString name = rels_theme[0].target; |
|||
QString path = xlworkbook_Dir + QLatin1String("/") + name; |
|||
m_document->workbook()->theme()->loadFromXmlData(zipReader.fileData(path)); |
|||
} |
|||
|
|||
//load worksheets
|
|||
QList<XlsxRelationship> rels_worksheets = xlworkbook_Rels.documentRelationships(QStringLiteral("/worksheet")); |
|||
if (rels_worksheets.isEmpty()) |
|||
return false; |
|||
|
|||
for (int i=0; i<sheetNameIdPairList.size(); ++i) { |
|||
XlsxSheetItemInfo info = sheetNameIdPairList[i]; |
|||
QString worksheet_path = xlworkbook_Dir + QLatin1String("/") + xlworkbook_Rels.getRelationshipById(info.rId).target; |
|||
QString rel_path = getRelFilePath(worksheet_path); |
|||
Worksheet *sheet = m_document->workbook()->addWorksheet(info.name, info.sheetId); |
|||
//If the .rel file exists, load it.
|
|||
if (zipReader.filePaths().contains(rel_path)) |
|||
sheet->relationships().loadFromXmlData(zipReader.fileData(rel_path)); |
|||
sheet->loadFromXmlData(zipReader.fileData(worksheet_path)); |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
bool Package::createPackage(QIODevice *package) |
|||
{ |
|||
ZipWriter zipWriter(package); |
|||
if (zipWriter.error()) |
|||
return false; |
|||
|
|||
m_workbook->prepareDrawings(); |
|||
|
|||
writeWorksheetFiles(zipWriter); |
|||
zipWriter.addFile(QStringLiteral("xl/workbook.xml"), m_workbook->saveToXmlData()); |
|||
writeDrawingFiles(zipWriter); |
|||
writeDocPropsAppFile(zipWriter); |
|||
writeDocPropsCoreFile(zipWriter); |
|||
writeContentTypesFile(zipWriter); |
|||
|
|||
if (!m_workbook->sharedStrings()->isEmpty()) |
|||
zipWriter.addFile(QStringLiteral("xl/sharedStrings.xml"), m_workbook->sharedStrings()->saveToXmlData()); |
|||
zipWriter.addFile(QStringLiteral("xl/styles.xml"), m_workbook->styles()->saveToXmlData()); |
|||
zipWriter.addFile(QStringLiteral("xl/theme/theme1.xml"), m_workbook->theme()->saveToXmlData()); |
|||
|
|||
writeRootRelsFile(zipWriter); |
|||
writeWorkbookRelsFile(zipWriter); |
|||
writeDrawingRelsFiles(zipWriter); |
|||
writeImageFiles(zipWriter); |
|||
|
|||
zipWriter.close(); |
|||
return true; |
|||
} |
|||
|
|||
void Package::writeWorksheetFiles(ZipWriter &zipWriter) |
|||
{ |
|||
for (int i=0; i<m_workbook->worksheetCount(); ++i) { |
|||
Worksheet *sheet = m_workbook->worksheet(i); |
|||
if (sheet->isChartsheet()) |
|||
continue; |
|||
|
|||
zipWriter.addFile(QStringLiteral("xl/worksheets/sheet%1.xml").arg(i+1), sheet->saveToXmlData()); |
|||
Relationships &rel = sheet->relationships(); |
|||
if (!rel.isEmpty()) |
|||
zipWriter.addFile(QStringLiteral("xl/worksheets/_rels/sheet%1.xml.rels").arg(i+1), rel.saveToXmlData()); |
|||
} |
|||
} |
|||
|
|||
void Package::writeDrawingFiles(ZipWriter &zipWriter) |
|||
{ |
|||
for (int i=0; i<m_workbook->drawings().size(); ++i) { |
|||
Drawing *drawing = m_workbook->drawings()[i]; |
|||
zipWriter.addFile(QStringLiteral("xl/drawings/drawing%1.xml").arg(i+1), drawing->saveToXmlData()); |
|||
} |
|||
} |
|||
|
|||
void Package::writeContentTypesFile(ZipWriter &zipWriter) |
|||
{ |
|||
ContentTypes content; |
|||
|
|||
for (int i=0; i<m_workbook->worksheetCount(); ++i) { |
|||
Worksheet *sheet = m_workbook->worksheet(i); |
|||
if (sheet->isChartsheet()) { |
|||
|
|||
} else { |
|||
content.addWorksheetName(QStringLiteral("sheet%1").arg(i+1)); |
|||
} |
|||
} |
|||
|
|||
int drawing_index = 1; |
|||
foreach (Drawing *drawing, m_workbook->drawings()) { |
|||
Q_UNUSED(drawing); |
|||
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()) |
|||
content.addSharedString(); |
|||
|
|||
zipWriter.addFile(QStringLiteral("[Content_Types].xml"), content.saveToXmlData()); |
|||
} |
|||
|
|||
void Package::writeDocPropsAppFile(ZipWriter &zipWriter) |
|||
{ |
|||
DocPropsApp props; |
|||
|
|||
foreach (QString name, m_document->documentPropertyNames()) |
|||
props.setProperty(name, m_document->documentProperty(name)); |
|||
|
|||
if (m_worksheet_count) |
|||
props.addHeadingPair(QStringLiteral("Worksheets"), m_worksheet_count); |
|||
if (m_chartsheet_count) |
|||
props.addHeadingPair(QStringLiteral("Chartsheets"), m_chartsheet_count); |
|||
|
|||
//Add worksheet parts
|
|||
for (int i=0; i<m_workbook->worksheetCount(); ++i) { |
|||
Worksheet *sheet = m_workbook->worksheet(i); |
|||
if (!sheet->isChartsheet()) |
|||
props.addPartTitle(sheet->sheetName()); |
|||
} |
|||
|
|||
//Add the chartsheet parts
|
|||
for (int i=0; i<m_workbook->worksheetCount(); ++i) { |
|||
Worksheet *sheet = m_workbook->worksheet(i); |
|||
if (sheet->isChartsheet()) |
|||
props.addPartTitle(sheet->sheetName()); |
|||
} |
|||
|
|||
zipWriter.addFile(QStringLiteral("docProps/app.xml"), props.saveToXmlData()); |
|||
} |
|||
|
|||
void Package::writeDocPropsCoreFile(ZipWriter &zipWriter) |
|||
{ |
|||
DocPropsCore props; |
|||
|
|||
foreach (QString name, m_document->documentPropertyNames()) |
|||
props.setProperty(name, m_document->documentProperty(name)); |
|||
|
|||
zipWriter.addFile(QStringLiteral("docProps/core.xml"), props.saveToXmlData()); |
|||
} |
|||
|
|||
void Package::writeRootRelsFile(ZipWriter &zipWriter) |
|||
{ |
|||
Relationships rels; |
|||
rels.addDocumentRelationship(QStringLiteral("/officeDocument"), QStringLiteral("xl/workbook.xml")); |
|||
rels.addPackageRelationship(QStringLiteral("/metadata/core-properties"), QStringLiteral("docProps/core.xml")); |
|||
rels.addDocumentRelationship(QStringLiteral("/extended-properties"), QStringLiteral("docProps/app.xml")); |
|||
|
|||
zipWriter.addFile(QStringLiteral("_rels/.rels"), rels.saveToXmlData()); |
|||
} |
|||
|
|||
void Package::writeWorkbookRelsFile(ZipWriter &zipWriter) |
|||
{ |
|||
Relationships rels; |
|||
|
|||
int worksheet_index = 1; |
|||
int chartsheet_index = 1; |
|||
for (int i=0; i<m_workbook->worksheetCount(); ++i) { |
|||
Worksheet *sheet = m_workbook->worksheet(i); |
|||
if (sheet->isChartsheet()) { |
|||
rels.addDocumentRelationship(QStringLiteral("/chartsheet"), QStringLiteral("chartsheets/sheet%1.xml").arg(chartsheet_index)); |
|||
chartsheet_index += 1; |
|||
} else { |
|||
rels.addDocumentRelationship(QStringLiteral("/worksheet"), QStringLiteral("worksheets/sheet%1.xml").arg(worksheet_index)); |
|||
worksheet_index += 1; |
|||
} |
|||
} |
|||
|
|||
rels.addDocumentRelationship(QStringLiteral("/theme"), QStringLiteral("theme/theme1.xml")); |
|||
rels.addDocumentRelationship(QStringLiteral("/styles"), QStringLiteral("styles.xml")); |
|||
|
|||
if (!m_workbook->sharedStrings()->isEmpty()) |
|||
rels.addDocumentRelationship(QStringLiteral("/sharedStrings"), QStringLiteral("sharedStrings.xml")); |
|||
|
|||
zipWriter.addFile(QStringLiteral("xl/_rels/workbook.xml.rels"), rels.saveToXmlData()); |
|||
} |
|||
|
|||
void Package::writeDrawingRelsFiles(ZipWriter &zipWriter) |
|||
{ |
|||
for (int i=0; i<m_workbook->worksheetCount(); ++i) { |
|||
Worksheet *sheet = m_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()); |
|||
} |
|||
} |
|||
|
|||
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
|
@ -1,81 +0,0 @@ |
|||
/****************************************************************************
|
|||
** 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_PACKAGE_H |
|||
#define QXLSX_PACKAGE_H |
|||
|
|||
//
|
|||
// W A R N I N G
|
|||
// -------------
|
|||
//
|
|||
// This file is not part of the Qt Xlsx API. It exists for the convenience
|
|||
// of the Qt Xlsx. This header file may change from
|
|||
// version to version without notice, or even be removed.
|
|||
//
|
|||
// We mean it.
|
|||
//
|
|||
|
|||
#include "xlsxglobal.h" |
|||
#include <QString> |
|||
class QIODevice; |
|||
|
|||
namespace QXlsx { |
|||
|
|||
class Workbook; |
|||
class ZipWriter; |
|||
class ZipReader; |
|||
class Document; |
|||
class Relationships; |
|||
class DocPropsCore; |
|||
class DocPropsApp; |
|||
|
|||
class XLSX_AUTOTEST_EXPORT Package |
|||
{ |
|||
public: |
|||
Package(Document *document); |
|||
|
|||
bool parsePackage(QIODevice *packageDevice); |
|||
bool createPackage(QIODevice *package); |
|||
|
|||
private: |
|||
|
|||
void writeWorksheetFiles(ZipWriter &zipWriter); |
|||
void writeDrawingFiles(ZipWriter &zipWriter); |
|||
void writeDocPropsAppFile(ZipWriter &zipWriter); |
|||
void writeDocPropsCoreFile(ZipWriter &zipWriter); |
|||
void writeContentTypesFile(ZipWriter &zipWriter); |
|||
void writeRootRelsFile(ZipWriter &zipWriter); |
|||
void writeWorkbookRelsFile(ZipWriter &zipWriter); |
|||
void writeDrawingRelsFiles(ZipWriter &zipWriter); |
|||
void writeImageFiles(ZipWriter &zipWriter); |
|||
|
|||
Document *m_document; |
|||
Workbook *m_workbook; |
|||
int m_worksheet_count; |
|||
int m_chartsheet_count; |
|||
}; |
|||
|
|||
} // namespace QXlsx
|
|||
|
|||
#endif // QXLSX_PACKAGE_H
|
Loading…
Reference in new issue