Commit e7b5098d authored by Lukáš Tvrdý's avatar Lukáš Tvrdý
Browse files

Fix missing images in groups

o use libmso to handle pictures
o fixes missing borders around pictures
o adds features from libmso like black and white mode

Known regressions: exposing more issues with z-index similar to bug 286607,
as z-index is not handled at all in the excel filter.

CCBUG:262865
CCBUG:244862
parent 2e3729c7
......@@ -364,7 +364,11 @@ QMap<QByteArray, QString> createPictures(KoStore* store, KoXmlWriter* manifest,
ref.uid = fbse->rgbUid;
}
}
manifest->addManifestEntry("Pictures/" + ref.name, ref.mimetype);
if (manifest) {
manifest->addManifestEntry("Pictures/" + ref.name, ref.mimetype);
}
fileNames[ref.uid] = ref.name;
}
#ifdef DEBUG_PICTURES
......
......@@ -196,7 +196,7 @@ public:
void processCharts(KoXmlWriter* manifestWriter);
void addManifestEntries(KoXmlWriter* ManifestWriter);
void insertPictureManifest(PictureObject* picture);
void insertPictureManifest(const QString& fileName);
QMap<QString,QString> manifestEntries;
KoXmlWriter* beginMemoryXmlWriter(const char* docElement);
......@@ -244,8 +244,8 @@ KoFilter::ConversionStatus ExcelImport::convert(const QByteArray& from, const QB
d->outputDoc->setOutputMimeType(to);
emit sigProgress(0);
QBuffer storeBuffer; // TODO: use temporary file instead
delete d->storeout;
d->storeout = KoStore::createStore(&storeBuffer, KoStore::Write);
......@@ -997,45 +997,6 @@ void ExcelImport::Private::processCellObjects(Cell* ic, Calligra::Tables::Cell o
{
bool hasObjects = false;
// handle pictures
foreach(PictureObject *picture, ic->pictures()) {
if (!hasObjects) {
shapesXml->startElement("table:table-cell");
shapesXml->addAttribute("table:row", oc.row());
shapesXml->addAttribute("table:column", oc.column());
hasObjects = true;
}
Sheet* const sheet = ic->sheet();
const unsigned long colL = picture->m_colL;
const unsigned long dxL = picture->m_dxL;
const unsigned long colR = picture->m_colR;
const unsigned long dxR = picture->m_dxR;
const unsigned long rwB = picture->m_rwB;
const unsigned long dyT = picture->m_dyT;
const unsigned long rwT = picture->m_rwT;
const unsigned long dyB = picture->m_dyB;
shapesXml->startElement("draw:frame");
//xmlWriter->addAttribute("draw:name", "Graphics 1");
shapesXml->addAttribute("table:end-cell-address", encodeAddress(sheet->name(), picture->m_colR, picture->m_rwB));
shapesXml->addAttributePt("table:end-x", offset(columnWidth(sheet, colR), dxR, 1024));
shapesXml->addAttributePt("table:end-y", offset(rowHeight(sheet, rwB), dyB, 256));
shapesXml->addAttribute("draw:z-index", "0");
shapesXml->addAttributePt("svg:x", offset(columnWidth(sheet, colL), dxL, 1024) );
shapesXml->addAttributePt("svg:y", offset(rowHeight(sheet, rwT), dyT, 256));
shapesXml->startElement("draw:image");
shapesXml->addAttribute("xlink:href", "Pictures/" + picture->fileName());
shapesXml->addAttribute("xlink:type", "simple");
shapesXml->addAttribute("xlink:show", "embed");
shapesXml->addAttribute("xlink:actuate", "onLoad");
shapesXml->endElement(); // draw:image
shapesXml->endElement(); // draw:frame
insertPictureManifest(picture);
}
// handle charts
foreach(ChartObject *chart, ic->charts()) {
Sheet* const sheet = ic->sheet();
......@@ -1328,10 +1289,9 @@ QPen ExcelImport::Private::convertBorder(const Pen& pen)
}
}
void ExcelImport::Private::insertPictureManifest(PictureObject* picture)
void ExcelImport::Private::insertPictureManifest(const QString& fileName)
{
QString mimeType;
const QString fileName = "Pictures/" + picture->fileName();
const QString extension = fileName.right(fileName.size() - fileName.lastIndexOf('.') - 1);
if( extension == "gif" ) {
......@@ -1357,7 +1317,7 @@ void ExcelImport::Private::insertPictureManifest(PictureObject* picture)
mimeType = "image/bmp";
}
manifestEntries.insert(fileName, mimeType);
manifestEntries.insert("Pictures/" + fileName, mimeType);
}
void ExcelImport::Private::addManifestEntries(KoXmlWriter* manifestWriter)
......@@ -1416,7 +1376,7 @@ KoXmlDocument ExcelImport::Private::endMemoryXmlWriter(KoXmlWriter* writer)
writer->endElement();
writer->endDocument();
QBuffer* b = static_cast<QBuffer*>(writer->device());
b->seek(0);
KoXmlDocument doc;
......
......@@ -24,7 +24,7 @@
#include "sheet.h"
#include "workbook.h"
#ifndef __GNUC__
#ifndef __GNUC__
#define __PRETTY_FUNCTION__ __FUNCTION__
#endif /* __PRETTY_FUNCTION__ only exists in gnu c++ */
......@@ -111,8 +111,19 @@ QRectF ODrawClient::getGlobalRect(const MSO::OfficeArtClientAnchor &clientAnchor
QString ODrawClient::getPicturePath(const quint32 pib)
{
qDebug() << "NOT YET IMPLEMENTED" << __PRETTY_FUNCTION__;
Q_UNUSED(pib);
quint32 offset = 0;
QByteArray rgbUid = getRgbUid(*m_sheet->workbook()->officeArtDggContainer(), pib, offset);
QString fileName;
if (rgbUid.isNull()) {
qDebug() << "Object in blipStore with pib: " << pib << "was not found.";
}else {
fileName = m_sheet->workbook()->pictureName(rgbUid);
}
if (!fileName.isEmpty()){
return "Pictures/" + fileName;
}
return QString();
}
......
......@@ -178,7 +178,7 @@ public:
void createDefaultColumnStyle( Sheet* sheet );
void processSheetBackground(Sheet* sheet, KoGenStyle& style);
void addManifestEntries(KoXmlWriter* ManifestWriter);
void insertPictureManifest(PictureObject* picture);
void insertPictureManifest(const QString &fileName);
bool isDateFormat(const QString& valueFormat);
......@@ -1395,37 +1395,6 @@ void ExcelImport::Private::processCellForBody(KoOdfWriteStore* store, Cell* cell
xmlWriter->endElement(); // office:annotation
}
// handle pictures
foreach(PictureObject *picture, cell->pictures()) {
Sheet* const sheet = cell->sheet();
const unsigned long colL = picture->m_colL;
const unsigned long dxL = picture->m_dxL;
const unsigned long colR = picture->m_colR;
const unsigned long dxR = picture->m_dxR;
const unsigned long rwB = picture->m_rwB;
const unsigned long dyT = picture->m_dyT;
const unsigned long rwT = picture->m_rwT;
const unsigned long dyB = picture->m_dyB;
xmlWriter->startElement("draw:frame");
//xmlWriter->addAttribute("draw:name", "Graphics 1");
xmlWriter->addAttribute("table:end-cell-address", encodeAddress(sheet->name(), picture->m_colR, picture->m_rwB));
xmlWriter->addAttributePt("table:end-x", offset(columnWidth(sheet, colR), dxR, 1024));
xmlWriter->addAttributePt("table:end-y", offset(rowHeight(sheet, rwB), dyB, 256));
xmlWriter->addAttribute("draw:z-index", "0");
xmlWriter->addAttributePt("svg:x", offset(columnWidth(sheet, colL), dxL, 1024) );
xmlWriter->addAttributePt("svg:y", offset(rowHeight(sheet, rwT), dyT, 256));
xmlWriter->startElement("draw:image");
xmlWriter->addAttribute("xlink:href", "Pictures/" + picture->fileName());
xmlWriter->addAttribute("xlink:type", "simple");
xmlWriter->addAttribute("xlink:show", "embed");
xmlWriter->addAttribute("xlink:actuate", "onLoad");
xmlWriter->endElement(); // draw:image
xmlWriter->endElement(); // draw:frame
insertPictureManifest(picture);
}
// handle charts
foreach(ChartObject *chart, cell->charts()) {
......@@ -1892,10 +1861,9 @@ void ExcelImport::Private::addManifestEntries(KoXmlWriter* manifestWriter)
}
}
void ExcelImport::Private::insertPictureManifest(PictureObject* picture)
void ExcelImport::Private::insertPictureManifest(const QString &fileName)
{
QString mimeType;
const QString fileName = picture->fileName();
const QString extension = fileName.right(fileName.size() - fileName.lastIndexOf('.') - 1);
if( extension == "gif" ) {
......
......@@ -236,21 +236,6 @@ void Cell::setNote(const QString &n)
d->note = n;
}
QList<PictureObject*> Cell::pictures() const
{
return d->sheet->pictures(d->column, d->row);
}
void Cell::setPictures(const QList<PictureObject*>& pics)
{
d->sheet->setPictures(d->column, d->row, pics);
}
void Cell::addPicture(PictureObject* picture)
{
d->sheet->addPicture(d->column, d->row, picture);
}
QList<ChartObject*> Cell::charts() const
{
return d->sheet->charts(d->column, d->row);
......@@ -286,22 +271,6 @@ bool Cell::operator==(const Cell &other) const
if (note() != other.note()) return false;
if (pictures().size() != other.pictures().size()) return false;
for(int i = pictures().size() - 1; i >= 0; --i) {
PictureObject* p1 = pictures()[i];
PictureObject* p2 = other.pictures()[i];
if(p1->id() != p2->id()) return false;
if(p1->fileName() != p2->fileName()) return false;
if(p1->m_colL != p2->m_colL) return false;
if(p1->m_dxL != p2->m_dxL) return false;
if(p1->m_rwT != p2->m_rwT) return false;
if(p1->m_dyT != p2->m_dyT) return false;
if(p1->m_colR != p2->m_colR) return false;
if(p1->m_dxR != p2->m_dxR) return false;
if(p1->m_rwB != p2->m_rwB) return false;
if(p1->m_dyB != p2->m_dyB) return false;
}
if (charts().size() != other.charts().size()) return false;
for(int i = charts().size() - 1; i >= 0; --i) {
ChartObject* c1 = charts()[i];
......@@ -315,7 +284,7 @@ bool Cell::operator==(const Cell &other) const
OfficeArtObject* o2 = other.drawObjects()[i];
if(*o1 != *o2) return false;
}
return true;
}
......
......@@ -34,7 +34,6 @@ namespace Swinder
class Workbook;
class Sheet;
class PictureObject;
class ChartObject;
class OfficeArtObject;
......@@ -112,7 +111,7 @@ public:
// If the is bigger then the defined number of following cells will be ignored/covered.
int columnRepeat() const;
void setColumnRepeat(int repeat);
// Defines if this cell has a hyperlink.
bool hasHyperlink() const;
Hyperlink hyperlink() const;
......@@ -121,11 +120,6 @@ public:
// Returns the optional note/comment/annotation of this cell.
QString note() const;
void setNote(const QString &n);
// Defines a list of pictures anchored to this cell.
QList<PictureObject*> pictures() const;
void setPictures(const QList<PictureObject*>&);
void addPicture(PictureObject*);
// Defines a list of charts anchored to this cell.
QList<ChartObject*> charts() const;
......
......@@ -969,7 +969,6 @@ void NameRecord::setData(unsigned size, const unsigned char* data, const unsigne
} else { // must satisfy same restrictions then name field on XLNameUnicodeString
const unsigned opts = readU8(data + 14);
const bool fHighByte = opts & 0x01;
Q_ASSERT((opts << 1) == 0x0);
// XLUnicodeStringNoCch
QString str = QString();
......@@ -1385,19 +1384,20 @@ void ObjRecord::setData(unsigned size, const unsigned char* data, const unsigned
startPict += 6;
break;
case Object::Picture: { // pictFormat and pictFlags
m_object = new PictureObject(id);
m_object = new Object(Object::Picture, id);
//const unsigned long ft = readU16(startPict);
//const unsigned long cb = readU16(startPict + 2);
// cf specifies Windows Clipboard format -- so far unused
const unsigned long cf = readU16(startPict + 4);
switch (cf) {
case 0x0002:
static_cast<PictureObject*>(m_object)->setType(PictureObject::EnhancedMetafile);
// enhanced metafile
break;
case 0x0009:
static_cast<PictureObject*>(m_object)->setType(PictureObject::Bitmap);
// bitmap
break;
case 0xFFFF:
static_cast<PictureObject*>(m_object)->setType(PictureObject::Unspecified);
// unspecified format, neither enhanced metafile nor a bitmap
break;
default:
std::cerr << "ObjRecord::setData: invalid ObjRecord Picture" << std::endl;
......@@ -1590,11 +1590,12 @@ void ObjRecord::setData(unsigned size, const unsigned char* data, const unsigned
if (fPrstm) { // iposInCtlStm specifies the zero-based offset of this object's data within the control stream.
const unsigned int cbBufInCtlStm = readU32(startPict + 4);
startPict += 8;
static_cast<PictureObject*>(m_object)->setControlStream(iposInCtlStm, cbBufInCtlStm);
Q_UNUSED(iposInCtlStm); // it was used in PictureObject as offset, but nobody used it
Q_UNUSED(cbBufInCtlStm); // it was used in PictureObject as size, but nobody used it
} else { // The object‘s data MUST reside in an embedding storage.
std::stringstream out;
out << std::setw(8) << std::setfill('0') << std::uppercase << std::hex << iposInCtlStm;
static_cast<PictureObject*>(m_object)->setEmbeddedStorage(out.str());
// out.str() was used as embedding storage, but nobody used it
}
}
......@@ -1766,7 +1767,7 @@ void MsoDrawingRecord::setData(unsigned size, const unsigned char* data, const u
setIsValid(false);
return;
}
// Finally remember the container to be able to extract later content out of it.
d->container = container;
}
......@@ -1779,7 +1780,7 @@ class MsoDrawingGroupRecord::Private
{
public:
MSO::OfficeArtDggContainer container;
QList< MsoDrawingBlibItem* > items;
QMap<QByteArray,QString> pictureNames;
};
MsoDrawingGroupRecord::MsoDrawingGroupRecord(Workbook *book) : Record(book)
......@@ -1797,11 +1798,12 @@ const MSO::OfficeArtDggContainer& MsoDrawingGroupRecord::dggContainer() const
return d->container;
}
QList<MsoDrawingBlibItem*> MsoDrawingGroupRecord::blibItems() const
const QMap< QByteArray, QString >& MsoDrawingGroupRecord::pictureNames() const
{
return d->items;
return d->pictureNames;
}
void MsoDrawingGroupRecord::dump(std::ostream& out) const
{
out << "MsoDrawingGroupRecord" << std::endl;
......@@ -1814,12 +1816,12 @@ void MsoDrawingGroupRecord::setData(unsigned size, const unsigned char* data, co
setIsValid(false);
return;
}
QByteArray byteArr = QByteArray::fromRawData(reinterpret_cast<const char*>(data), size);
QBuffer buff(&byteArr);
buff.open(QIODevice::ReadOnly);
LEInputStream lei(&buff);
try {
MSO::parseOfficeArtDggContainer(lei, d->container);
} catch (const IOException& e) {
......@@ -1827,20 +1829,10 @@ void MsoDrawingGroupRecord::setData(unsigned size, const unsigned char* data, co
setIsValid(false);
return;
}
if(d->container.blipStore.data() && m_workbook->store()) {
m_workbook->store()->enterDirectory("Pictures");
foreach(MSO::OfficeArtBStoreContainerFileBlock fb, d->container.blipStore->rgfb) {
PictureReference ref = savePicture(fb, m_workbook->store());
if (ref.name.length() == 0) {
std::cerr << "Empty name in picture reference for picture with uid=" << ref.uid << " mimetype=" << ref.mimetype << std::endl;
d->items << 0;
continue;
}
d->items << new MsoDrawingBlibItem(ref);
}
d->pictureNames = createPictures(m_workbook->store(), 0, &d->container.blipStore->rgfb);
m_workbook->store()->leaveDirectory();
}
}
......@@ -2606,10 +2598,6 @@ void ExcelReader::handleEOF(EOFRecord* record)
if (handler != d->globals) delete handler;
}
MsoDrawingBlibItem::MsoDrawingBlibItem(const PictureReference &picture)
: m_picture(picture)
{
}
#ifdef SWINDER_XLS2RAW
......
......@@ -838,13 +838,6 @@ private:
Private *d;
};
class MsoDrawingBlibItem
{
public:
//enum Type { Picture, ... };
PictureReference m_picture;
explicit MsoDrawingBlibItem(const PictureReference &picture);
};
class MsoDrawingGroupRecord : public Record
{
......@@ -861,8 +854,8 @@ public:
virtual void dump(std::ostream&) const;
virtual void setData(unsigned size, const unsigned char* data, const unsigned* continuePositions);
const QMap<QByteArray,QString>& pictureNames() const;
const MSO::OfficeArtDggContainer& dggContainer() const;
QList<MsoDrawingBlibItem*> blibItems() const;
private:
// no copy or assign
MsoDrawingGroupRecord(const MsoDrawingGroupRecord&);
......
......@@ -64,7 +64,7 @@ public:
// table of format
std::map<unsigned, QString> formatsTable;
// cache of formats
std::map<unsigned, int> formatCache;
......@@ -75,9 +75,6 @@ public:
// table of Xformat
std::vector<XFRecord> xfTable;
// table blib items
QList< MsoDrawingBlibItem* > drawingTable;
// list of chart sheets
QList< Sheet* > chartSheets;
};
......@@ -94,7 +91,6 @@ GlobalsSubStreamHandler::GlobalsSubStreamHandler(Workbook* workbook, unsigned ve
GlobalsSubStreamHandler::~GlobalsSubStreamHandler()
{
delete d->decryption;
qDeleteAll(d->drawingTable);
delete d;
}
......@@ -776,20 +772,20 @@ void GlobalsSubStreamHandler::handleMsoDrawingGroup(MsoDrawingGroupRecord* recor
{
if (!record) return;
printf("GlobalsSubStreamHandler::handleMsoDrawingGroup\n");
Q_ASSERT(d->drawingTable.size() == 0); // if this asserts then multiple MsoDrawingGroupRecord can exist what we need to handle!
d->drawingTable = record->blibItems();
d->workbook->setOfficeArtDggContainer(record->dggContainer());
}
MsoDrawingBlibItem* GlobalsSubStreamHandler::drawing(unsigned long pid) const
{
if (pid < 1 || pid > uint(d->drawingTable.size())) {
std::cerr << "GlobalsSubStreamHandler::drawing: Invalid index=" << (pid - 1) << std::endl;
return 0;
static int validMsoDrawingGroups = 0;
// if this pass then multiple MsoDrawingGroupRecord can exist what we need to handle!
if (validMsoDrawingGroups > 0) {
std::cerr << "Warning: multiple valid MsoDrawingGroupRecord exists : " << validMsoDrawingGroups << std::endl;
}
return d->drawingTable.at(pid - 1);
validMsoDrawingGroups++;
d->workbook->setPictureNames(record->pictureNames());
d->workbook->setOfficeArtDggContainer(record->dggContainer());
}
QList< Sheet* >& GlobalsSubStreamHandler::chartSheets()
{
return d->chartSheets;
......
......@@ -49,7 +49,6 @@ class PaletteRecord;
class SSTRecord;
class XFRecord;
class ProtectRecord;
class MsoDrawingBlibItem;
class MsoDrawingGroupRecord;
class Window1Record;
class PasswordRecord;
......@@ -91,7 +90,6 @@ public:
virtual QString nameFromIndex(unsigned index) const;
virtual QString externNameFromIndex(unsigned index) const;
MsoDrawingBlibItem* drawing(unsigned long pid) const;
QList< Sheet* >& chartSheets();
KoStore* store() const;
......
......@@ -103,72 +103,6 @@ protected:
unsigned long m_id;
};
/**
* Picture objects are used to store bitmap, enhanced metafiles or
* other kind of images.
*/
class PictureObject : public Object
{
public:
PictureObject(unsigned long id) : Object(Picture, id), m_offset(0), m_size(0) {}
virtual ~PictureObject() {}
/// Enumeration of possible image types.
enum Type { EnhancedMetafile, Bitmap, Unspecified };
/// Returns the type of the image.
Type type() const {
return m_type;
}
/// Sets the type of the image.
void setType(const Type &t) {
m_type = t;
}
/// Returns the offset of the picture in the control stream.
uint controlStreamOffset() {
return m_offset;
}
/// Returns the size of the picture in the control stream.
uint controlStreamSize() {
return m_size;
}
/// Sets the offset and size of the picture in the control stream.
void setControlStream(uint offset, uint size) {
m_offset = offset;
m_size = size;
}
/**
* Returns the filename of the embedded storage. This can be empty if the
* picture is not located in an embedded but e.g. in a control stream.
* If not empty then thís is usually a concatenation of "MBD" and a eight
* byte hexadecimal representation.
*/
std::string embeddedStorage() const {
return m_storage;
}
/// Set the filename of the embedded storage.
void setEmbeddedStorage(const std::string &filename) {
m_storage = filename;
}
/**
* Returns the path and filename of the picture-file in the KoStore.
* This can be something like "Pictures/TheUniqueIdentifierOfThePicture.jpg" for example.
*/
QString fileName() const {
return m_filename;
}
void setFileName(const QString& name) {
m_filename = name;
}
private:
Type m_type;
uint m_offset, m_size;
std::string m_storage;
QString m_filename;
};
/**
* Note objects used to store comments attached to a cell or revision.
......
......@@ -53,7 +53,6 @@ public:
QHash<unsigned, Column*> columns;
QHash<unsigned, Row*> rows;
Calligra::Tables::PointStorage<Hyperlink> hyperlinks;
Calligra::Tables::PointStorage<QList<PictureObject*> > pictures;
Calligra::Tables::PointStorage<QList<ChartObject*> > charts;
Calligra::Tables::PointStorage<QList<OfficeArtObject*> > drawObjects;
......@@ -131,9 +130,6 @@ void Sheet::setAutoCalc(bool a)
void Sheet::clear()
{
// delete all cell data
for (int i = 0; i < d->pictures.count(); i++) {
qDeleteAll(d->pictures.data(i));