From fd79d7c6bdc2b59a0ad267a92fe0f9e63a3019da Mon Sep 17 00:00:00 2001 From: Jim Evins Date: Sun, 12 Mar 2017 22:39:15 -0400 Subject: [PATCH] Embed image data in glabels file. --- glabels/CMakeLists.txt | 1 + glabels/DataCache.cpp | 82 +++++++++++++++++++++++++++++++ glabels/DataCache.h | 51 +++++++++++++++++++ glabels/LabelModelImageObject.cpp | 44 ++++++++++++++--- glabels/LabelModelImageObject.h | 2 + glabels/LabelModelObject.cpp | 20 ++++++++ glabels/LabelModelObject.h | 2 + glabels/XmlLabelCreator.cpp | 31 ++++++++++-- glabels/XmlLabelCreator.h | 2 +- glabels/XmlLabelParser.cpp | 71 ++++++++++++++++++++------ glabels/XmlLabelParser.h | 13 ++--- 11 files changed, 287 insertions(+), 32 deletions(-) create mode 100644 glabels/DataCache.cpp create mode 100644 glabels/DataCache.h diff --git a/glabels/CMakeLists.txt b/glabels/CMakeLists.txt index e688488..a49857e 100644 --- a/glabels/CMakeLists.txt +++ b/glabels/CMakeLists.txt @@ -36,6 +36,7 @@ set (glabels_sources ColorPaletteButtonItem.cpp ColorSwatch.cpp Cursors.cpp + DataCache.cpp Db.cpp Distance.cpp EnumUtil.cpp diff --git a/glabels/DataCache.cpp b/glabels/DataCache.cpp new file mode 100644 index 0000000..92a2181 --- /dev/null +++ b/glabels/DataCache.cpp @@ -0,0 +1,82 @@ +/* DataCache.cpp + * + * Copyright (C) 2017 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * gLabels-qt is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with gLabels-qt. If not, see . + */ + +#include "DataCache.h" + +#include "LabelModelImageObject.h" + + +namespace glabels +{ + + DataCache::DataCache() + { + // empty + } + + + DataCache::DataCache( const LabelModel* model ) + { + foreach( LabelModelObject* object, model->objectList() ) + { + if ( LabelModelImageObject* imageObject = dynamic_cast(object) ) + { + TextNode filenameNode = imageObject->filenameNode(); + if ( !filenameNode.isField() ) + { + if ( const QImage* image = imageObject->image() ) + { + addImage( filenameNode.data(), *imageObject->image() ); + } + else + { + // TODO handle SVG files + } + } + } + } + } + + + bool DataCache::hasImage( const QString& name ) const + { + return mImageMap.contains( name ); + } + + + QImage DataCache::getImage( const QString& name ) const + { + return mImageMap[ name ]; + } + + + void DataCache::addImage( const QString& name, const QImage& image ) + { + mImageMap[ name ] = image; + } + + + QList DataCache::imageNames() const + { + return mImageMap.keys(); + } + + +} diff --git a/glabels/DataCache.h b/glabels/DataCache.h new file mode 100644 index 0000000..45a752e --- /dev/null +++ b/glabels/DataCache.h @@ -0,0 +1,51 @@ +/* DataCache.h + * + * Copyright (C) 2017 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * gLabels-qt is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with gLabels-qt. If not, see . + */ + +#ifndef glabels_DataCache_h +#define glabels_DataCache_h + + +#include "LabelModel.h" + + +namespace glabels +{ + + class DataCache + { + public: + DataCache(); + + DataCache( const LabelModel* model ); + + bool hasImage( const QString& name ) const; + QImage getImage( const QString& name ) const; + void addImage( const QString& name, const QImage& image ); + QList imageNames() const; + + private: + QMap mImageMap; + + }; + +} + + +#endif // glabels_DataCache_h diff --git a/glabels/LabelModelImageObject.cpp b/glabels/LabelModelImageObject.cpp index be69510..c9e55d0 100644 --- a/glabels/LabelModelImageObject.cpp +++ b/glabels/LabelModelImageObject.cpp @@ -120,7 +120,16 @@ namespace glabels /// - /// Image Property Default Setter + /// Image image Property Getter + /// + const QImage* LabelModelImageObject::image() const + { + return mImage; + } + + + /// + /// Image Property Setter /// void LabelModelImageObject::setImage( const QImage& value ) { @@ -144,6 +153,30 @@ namespace glabels } + /// + /// Image Property Setter + /// + void LabelModelImageObject::setImage( const QString& name, const QImage& value ) + { + if ( !value.isNull() ) + { + if ( mImage ) + { + delete mImage; + } + if ( mSvg ) + { + delete mSvg; + } + + mImage = new QImage(value); + mFilenameNode = TextNode( false, name ); + + emit changed(); + } + } + + /// /// naturalSize Property Getter (assumes 72 DPI, i.e. 1pixel == 1pt) /// @@ -246,18 +279,15 @@ namespace glabels if ( mImage ) { delete mImage; + mImage = 0; } if ( mSvg ) { delete mSvg; - } - - if ( mFilenameNode.isField() ) - { - mImage = 0; mSvg = 0; } - else + + if ( !mFilenameNode.isField() ) { QString filename = mFilenameNode.data(); QFileInfo fileInfo( filename ); diff --git a/glabels/LabelModelImageObject.h b/glabels/LabelModelImageObject.h index 4548c32..41f1d38 100644 --- a/glabels/LabelModelImageObject.h +++ b/glabels/LabelModelImageObject.h @@ -65,7 +65,9 @@ namespace glabels // // Image Property: image // + virtual const QImage* image() const; virtual void setImage( const QImage& value ); + virtual void setImage( const QString& name, const QImage& value ); // // Property: naturalSize diff --git a/glabels/LabelModelObject.cpp b/glabels/LabelModelObject.cpp index 744f064..1b73208 100644 --- a/glabels/LabelModelObject.cpp +++ b/glabels/LabelModelObject.cpp @@ -600,6 +600,16 @@ namespace glabels } + /// + /// Virtual Image Property Default Getter + /// (Overridden by concrete class) + /// + const QImage* LabelModelObject::image() const + { + return 0; + } + + /// /// Virtual Image Property Default Setter /// (Overridden by concrete class) @@ -610,6 +620,16 @@ namespace glabels } + /// + /// Virtual Image Property Default Setter + /// (Overridden by concrete class) + /// + void LabelModelObject::setImage( const QString& name, const QImage& value ) + { + // empty + } + + /// /// Virtual Line Width Property Default Getter /// (Overridden by concrete class) diff --git a/glabels/LabelModelObject.h b/glabels/LabelModelObject.h index de2f124..1e3bd61 100644 --- a/glabels/LabelModelObject.h +++ b/glabels/LabelModelObject.h @@ -257,7 +257,9 @@ namespace glabels // // Virtual Image Property: image // + virtual const QImage* image() const; virtual void setImage( const QImage& value ); + virtual void setImage( const QString& name, const QImage& value ); /////////////////////////////////////////////////////////////// diff --git a/glabels/XmlLabelCreator.cpp b/glabels/XmlLabelCreator.cpp index efa354c..7dd98b1 100644 --- a/glabels/XmlLabelCreator.cpp +++ b/glabels/XmlLabelCreator.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include "EnumUtil.h" @@ -36,6 +37,7 @@ #include "LabelModelLineObject.h" #include "LabelModelImageObject.h" #include "LabelModelTextObject.h" +#include "DataCache.h" #include "XmlTemplateCreator.h" #include "XmlUtil.h" @@ -444,14 +446,37 @@ namespace glabels void XmlLabelCreator::createDataNode( QDomElement &parent, const LabelModel* label ) { - // TODO + QDomDocument doc = parent.ownerDocument(); + QDomElement node = doc.createElement( "Data" ); + parent.appendChild( node ); + + DataCache data( label ); + + foreach ( QString name, data.imageNames() ) + { + createPngFileNode( node, name, data.getImage( name ) ); + } } void - XmlLabelCreator::createPixdataNode( QDomElement &parent, const LabelModel* label, const QString& name ) + XmlLabelCreator::createPngFileNode( QDomElement &parent, const QString& name, const QImage& image ) { - // TODO + QDomDocument doc = parent.ownerDocument(); + QDomElement node = doc.createElement( "File" ); + parent.appendChild( node ); + + XmlUtil::setStringAttr( node, "name", name ); + XmlUtil::setStringAttr( node, "mimetype", "image/png" ); + XmlUtil::setStringAttr( node, "encoding", "base64" ); + + QByteArray ba; + QBuffer buffer(&ba); + buffer.open(QIODevice::WriteOnly); + image.save(&buffer, "PNG"); + QByteArray ba64 = ba.toBase64(); + + node.appendChild( doc.createTextNode( QString( ba64 ) ) ); } diff --git a/glabels/XmlLabelCreator.h b/glabels/XmlLabelCreator.h index 6b9f25c..f305753 100644 --- a/glabels/XmlLabelCreator.h +++ b/glabels/XmlLabelCreator.h @@ -68,7 +68,7 @@ namespace glabels static void createShadowAttrs( QDomElement &node, const LabelModelObject* object ); static void createMergeNode( QDomElement &parent, const LabelModel* label ); static void createDataNode( QDomElement &parent, const LabelModel* label ); - static void createPixdataNode( QDomElement &parent, const LabelModel* label, const QString& name ); + static void createPngFileNode( QDomElement &parent, const QString& name, const QImage& image ); static void createSvgFileNode( QDomElement &parent, const LabelModel* label, const QString& name ); }; diff --git a/glabels/XmlLabelParser.cpp b/glabels/XmlLabelParser.cpp index e3ad450..791eb9d 100644 --- a/glabels/XmlLabelParser.cpp +++ b/glabels/XmlLabelParser.cpp @@ -40,6 +40,7 @@ #include "LabelModelTextObject.h" #include "XmlTemplateParser.h" #include "XmlUtil.h" +#include "DataCache.h" #include "Merge/Factory.h" @@ -151,7 +152,7 @@ namespace glabels return list; } - return parseObjects( root ); + return parseObjects( root, DataCache() ); } @@ -217,11 +218,12 @@ namespace glabels LabelModel* label = new LabelModel(); /* Pass 1, extract data nodes to pre-load cache. */ + DataCache data; for ( QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling() ) { if ( child.toElement().tagName() == "Data" ) { - parseDataNode( child.toElement(), label ); + parseDataNode( child.toElement(), data ); } } @@ -242,7 +244,7 @@ namespace glabels } else if ( tagName == "Objects" ) { - parseObjectsNode( child.toElement(), label ); + parseObjectsNode( child.toElement(), data, label ); } else if ( tagName == "Merge" ) { @@ -264,7 +266,7 @@ namespace glabels QList - XmlLabelParser::parseObjects( const QDomElement &node ) + XmlLabelParser::parseObjects( const QDomElement &node, const DataCache& data ) { QList list; @@ -290,7 +292,7 @@ namespace glabels } else if ( tagName == "Object-image" ) { - list.append( parseObjectImageNode( child.toElement() ) ); + list.append( parseObjectImageNode( child.toElement(), data ) ); } #if 0 else if ( tagName == "Object-barcode" ) @@ -309,9 +311,9 @@ namespace glabels void - XmlLabelParser::parseObjectsNode( const QDomElement &node, LabelModel* label ) + XmlLabelParser::parseObjectsNode( const QDomElement &node, const DataCache& data, LabelModel* label ) { - QList list = parseObjects( node ); + QList list = parseObjects( node, data ); foreach ( LabelModelObject* object, list ) { @@ -441,7 +443,7 @@ namespace glabels LabelModelImageObject* - XmlLabelParser::parseObjectImageNode( const QDomElement &node ) + XmlLabelParser::parseObjectImageNode( const QDomElement &node, const DataCache& data ) { LabelModelImageObject* object = new LabelModelImageObject(); @@ -460,7 +462,21 @@ namespace glabels bool field_flag = !key.isEmpty(); QString filename = XmlUtil::getStringAttr( node, "src", "" ); - object->setFilenameNode( TextNode( field_flag, field_flag ? key : filename ) ); + if ( field_flag ) + { + object->setFilenameNode( TextNode( true, key ) ); + } + else + { + if ( data.hasImage( filename ) ) + { + object->setImage( filename, data.getImage( filename ) ); + } + else + { + object->setFilenameNode( TextNode( false, filename ) ); + } + } } /* affine attrs */ @@ -607,23 +623,48 @@ namespace glabels void - XmlLabelParser::parseDataNode( const QDomElement &node, LabelModel* label ) + XmlLabelParser::parseDataNode( const QDomElement &node, DataCache& data ) { - // TODO + for ( QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling() ) + { + QString tagName = child.toElement().tagName(); + + if ( tagName == "File" ) + { + parseFileNode( child.toElement(), data ); + } + else if ( !child.isComment() ) + { + qWarning() << "Unexpected" << node.tagName() << "child:" << tagName; + } + } } void - XmlLabelParser::parsePixdataNode( const QDomElement &node, LabelModel* label ) + XmlLabelParser::parsePixdataNode( const QDomElement& node, DataCache& data ) { - // TODO + // TODO, compatability with glabels-3 } void - XmlLabelParser::parseFileNode( const QDomElement &node, LabelModel* label ) + XmlLabelParser::parseFileNode( const QDomElement& node, DataCache& data ) { - // TODO + QString name = XmlUtil::getStringAttr( node, "name", "" ); + QString mimetype = XmlUtil::getStringAttr( node, "mimetype", "image/png" ); + QString encoding = XmlUtil::getStringAttr( node, "encoding", "base64" ); + + if ( mimetype == "image/png" ) + { + QByteArray ba64 = node.text().toUtf8(); + QByteArray ba = QByteArray::fromBase64( ba64 ); + QImage image; + image.loadFromData( ba, "PNG" ); + + data.addImage( name, image ); + } + } } diff --git a/glabels/XmlLabelParser.h b/glabels/XmlLabelParser.h index 07b5e7e..9694303 100644 --- a/glabels/XmlLabelParser.h +++ b/glabels/XmlLabelParser.h @@ -38,6 +38,7 @@ namespace glabels class LabelModelImageObject; class LabelModelBarcodeObject; class LabelModelTextObject; + class DataCache; /// @@ -55,21 +56,21 @@ namespace glabels private: static void gunzip( const QByteArray& gzippedData, QByteArray& data ); static LabelModel* parseRootNode( const QDomElement &node ); - static QList parseObjects( const QDomElement &node ); - static void parseObjectsNode( const QDomElement &node, LabelModel* label ); + static QList parseObjects( const QDomElement &node, const DataCache& data ); + static void parseObjectsNode( const QDomElement &node, const DataCache& data, LabelModel* label ); static LabelModelBoxObject* parseObjectBoxNode( const QDomElement &node ); static LabelModelEllipseObject* parseObjectEllipseNode( const QDomElement &node ); static LabelModelLineObject* parseObjectLineNode( const QDomElement &node ); - static LabelModelImageObject* parseObjectImageNode( const QDomElement &node ); + static LabelModelImageObject* parseObjectImageNode( const QDomElement &node, const DataCache& data ); static LabelModelBarcodeObject* parseObjectBarcodeNode( const QDomElement &node ); static LabelModelTextObject* parseObjectTextNode( const QDomElement &node ); static QString parsePNode( const QDomElement &node ); static void parseAffineAttrs( const QDomElement &node, LabelModelObject* object ); static void parseShadowAttrs( const QDomElement &node, LabelModelObject* object ); static void parseMergeNode( const QDomElement &node, LabelModel* label ); - static void parseDataNode( const QDomElement &node, LabelModel* label ); - static void parsePixdataNode( const QDomElement &node, LabelModel* label ); - static void parseFileNode( const QDomElement &node, LabelModel* label ); + static void parseDataNode( const QDomElement &node, DataCache& data ); + static void parsePixdataNode( const QDomElement &node, DataCache& data ); + static void parseFileNode( const QDomElement &node, DataCache& data ); };