diff --git a/model/ModelImageObject.cpp b/model/ModelImageObject.cpp index 68d3f2c..8b7da2e 100644 --- a/model/ModelImageObject.cpp +++ b/model/ModelImageObject.cpp @@ -115,6 +115,8 @@ namespace glabels mImage = nullptr; mSvgRenderer = nullptr; + + loadImage(); } @@ -427,7 +429,7 @@ namespace glabels painter->drawImage( destRect, *shadowImage ); delete shadowImage; } - else if ( mImage || inEditor ) + else if ( mImage || mSvgRenderer || inEditor ) { painter->setBrush( shadowColor ); painter->setPen( QPen( Qt::NoPen ) ); @@ -436,26 +438,28 @@ namespace glabels } else { - // Look for image file relative to project file 1st then CWD 2nd - auto* model = dynamic_cast( parent() ); - QDir::setSearchPaths( "images", {model->dirPath(), QDir::currentPath()} ); - QString filename = QString("images:") + mFilenameNode.text( record, variables ); - - auto* image = new QImage( filename ); - if ( !image->isNull() && image->hasAlphaChannel() && (image->depth() == 32) ) + QString filename = mFilenameNode.text( record, variables ); + QImage* image; + QSvgRenderer* svgRenderer; + QByteArray svg; + if ( readImageFile( filename, image, svgRenderer, svg ) ) { - QImage* shadowImage = createShadowImage( *image, shadowColor ); - painter->drawImage( destRect, *shadowImage ); - delete shadowImage; - } - else if ( !image->isNull() ) - { - painter->setBrush( shadowColor ); - painter->setPen( QPen( Qt::NoPen ) ); + if ( image && image->hasAlphaChannel() && (image->depth() == 32) ) + { + QImage* shadowImage = createShadowImage( *image, shadowColor ); + painter->drawImage( destRect, *shadowImage ); + delete shadowImage; + } + else + { + painter->setBrush( shadowColor ); + painter->setPen( QPen( Qt::NoPen ) ); - painter->drawRect( destRect ); + painter->drawRect( destRect ); + } + delete image; + delete svgRenderer; } - delete image; } } @@ -539,18 +543,10 @@ namespace glabels else if ( mFilenameNode.isField() ) { QString filename = mFilenameNode.text( record, variables ); - QFileInfo fileInfo( filename ); - if ( fileInfo.isRelative() ) - { - // Look for image file relative to project file 1st then CWD 2nd - auto* model = dynamic_cast( parent() ); - QDir::setSearchPaths( "images", {model->dirPath(), QDir::currentPath()} ); - filename = QString("images:") + filename; - } - QImage* image; QSvgRenderer* svgRenderer; - if ( readImageFile( filename, image, svgRenderer ) ) + QByteArray svg; + if ( readImageFile( filename, image, svgRenderer, svg ) ) { if ( image ) { @@ -598,7 +594,7 @@ namespace glabels if ( !mFilenameNode.isField() ) { QString filename = mFilenameNode.data(); - if ( readImageFile( filename, mImage, mSvgRenderer ) ) + if ( readImageFile( filename, mImage, mSvgRenderer, mSvg ) ) { double aspectRatio = 0; if ( mSvgRenderer ) @@ -634,34 +630,47 @@ namespace glabels /// /// Read an image or svg file /// - bool ModelImageObject::readImageFile( const QString& fileName, QImage*& image, QSvgRenderer*& svgRenderer ) const + bool ModelImageObject::readImageFile( const QString& fileName, + QImage*& image, + QSvgRenderer*& svgRenderer, + QByteArray& svg ) const { image = nullptr; svgRenderer = nullptr; + svg.clear(); if ( !fileName.isEmpty() ) { QFileInfo fileInfo( fileName ); + if ( fileInfo.isRelative() ) + { + // Look for image file relative to project file 1st then CWD 2nd + auto* model = dynamic_cast( parent() ); + QDir::setSearchPaths( "images", {model ? model->dirPath() : "", QDir::currentPath()} ); + fileInfo.setFile( QString("images:") + fileName ); + } + if ( fileInfo.isReadable() ) { if ( fileInfo.suffix().toLower() == "svg" ) { - QFile file( fileName ); + QFile file( fileInfo.filePath() ); if ( file.open( QFile::ReadOnly ) ) { - QByteArray svg = file.readAll(); + svg = file.readAll(); file.close(); svgRenderer = new QSvgRenderer( svg ); if ( !svgRenderer->isValid() ) { delete svgRenderer; svgRenderer = nullptr; + svg.clear(); } } } else { - image = new QImage( fileName ); + image = new QImage( fileInfo.filePath() ); if ( image->isNull() ) { delete image; diff --git a/model/ModelImageObject.h b/model/ModelImageObject.h index 0f0fa15..3e982f5 100644 --- a/model/ModelImageObject.h +++ b/model/ModelImageObject.h @@ -153,7 +153,10 @@ namespace glabels /////////////////////////////////////////////////////////////// void loadImage(); - bool readImageFile( const QString& fileName, QImage*& image, QSvgRenderer*& svgRenderer ) const; + bool readImageFile( const QString& fileName, + QImage*& image, + QSvgRenderer*& svgRenderer, + QByteArray& svg ) const; QImage* createShadowImage( const QImage& image, const QColor& color ) const; diff --git a/model/XmlLabelParser.cpp b/model/XmlLabelParser.cpp index ed03d6d..436c66f 100644 --- a/model/XmlLabelParser.cpp +++ b/model/XmlLabelParser.cpp @@ -273,7 +273,8 @@ namespace glabels delete model; return nullptr; } - model->setTmplate( tmplate ); + model->setTmplate( tmplate ); // Copies arg + delete tmplate; } else if ( tagName == "Objects" ) { @@ -576,6 +577,7 @@ namespace glabels if ( !filename.isEmpty() ) { qWarning() << "Embedded file" << fn << "missing. Trying actual file."; + filenameNode.setData( fn ); } return new ModelImageObject( x0, y0, w, h, lockAspectRatio, filenameNode, diff --git a/model/XmlLabelParser_3.cpp b/model/XmlLabelParser_3.cpp index dca802a..fbea2d5 100644 --- a/model/XmlLabelParser_3.cpp +++ b/model/XmlLabelParser_3.cpp @@ -111,7 +111,8 @@ namespace glabels delete label; return nullptr; } - label->setTmplate( tmplate ); + label->setTmplate( tmplate ); // Copies arg + delete tmplate; } else if ( tagName == "Objects" ) { diff --git a/model/unit_tests/TestModelImageObject.cpp b/model/unit_tests/TestModelImageObject.cpp index d947d1c..a15a362 100644 --- a/model/unit_tests/TestModelImageObject.cpp +++ b/model/unit_tests/TestModelImageObject.cpp @@ -46,38 +46,121 @@ void TestModelImageObject::initTestCase() void TestModelImageObject::readImageFile() { - QImage paintDevice( 10, 160, QImage::Format_RGB32 ); - paintDevice.fill( Qt::white ); - QPainter painter( &paintDevice ); + QByteArray pngArray; + QImage png; + QByteArray svg; + QString svgTemplate = QDir::tempPath().append( QDir::separator() ).append( "TestModelImageObject_XXXXXX.svg" ); Model model; + // Needed for relative file names to work + QString modelFileName = QDir::tempPath().append( QDir::separator() ).append( "TestModelImageObject.glabels" ); + model.setFileName( modelFileName ); + ModelImageObject object; + + /// + /// Merge object, no shadow + /// object.setX0( 1 ); object.setY0( 1 ); object.setSize( 8, 8 ); - TextNode filenameNode( true, "image" ); - object.setFilenameNode( filenameNode ); + object.setFilenameNode( TextNode( true, "image" ) ); model.addObject( object.clone() ); + /// + /// Variable object, green pgn, gray shadow + /// object.setY0( 11 ); - object.setSize( 8, 8 ); - TextNode filenameNode2( true, "var" ); - object.setFilenameNode( filenameNode2 ); + object.setShadow( true ); + object.setShadowColorNode( ColorNode( Qt::gray ) ); + object.setShadowOpacity( 1 ); + TextNode( true, "var" ); + object.setFilenameNode( TextNode( true, "var" ) ); + + // Green 8x8 square pgn + pngArray = QByteArray::fromBase64( glabels::test::green_8x8_png ); + QVERIFY( png.loadFromData( pngArray, "PNG" ) ); + QTemporaryFile pngGreen; pngGreen.open(); pngGreen.close(); png.save( pngGreen.fileName(), "PNG" ); + QFileInfo pngGreenFileInfo( pngGreen.fileName() ); + + Variable var( Variable::Type::STRING, "var", pngGreenFileInfo.fileName(), Variable::Increment::PER_ITEM ); // Relative path + model.variables()->addVariable( var ); model.addObject( object.clone() ); - // Blue 8x8 square - QByteArray pngArray = QByteArray::fromBase64( glabels::test::blue_8x8_png ); - QImage png; + /// + /// Variable object 2, magenta svg, yellow shadow + /// + object.setY0( 21 ); + object.setShadow( true ); + object.setShadowColorNode( ColorNode( Qt::yellow ) ); + object.setShadowOpacity( 1 ); + object.setFilenameNode( TextNode( true, "var2" ) ); + + // Magenta 8x8 square svg + svg = glabels::test::magenta_8x8_svg; + QTemporaryFile svgMagenta( svgTemplate ); svgMagenta.open(); svgMagenta.write( svg ); svgMagenta.close(); + QFileInfo svgMagentaFileInfo( svgMagenta.fileName() ); + + Variable var2( Variable::Type::STRING, "var2", svgMagentaFileInfo.fileName(), Variable::Increment::PER_ITEM ); // Absolute path + model.variables()->addVariable( var2 ); + + model.addObject( object.clone() ); + + /// + /// Filename object, yellow png, cyan shadow + /// + object.setY0( 31 ); + object.setShadow( true ); + object.setShadowColorNode( ColorNode( Qt::cyan ) ); + object.setShadowOpacity( 1 ); + + // Yellow 8x8 square pgn + pngArray = QByteArray::fromBase64( glabels::test::yellow_8x8_png ); + QVERIFY( png.loadFromData( pngArray, "PNG" ) ); + QTemporaryFile pngYellowFile; pngYellowFile.open(); pngYellowFile.close(); png.save( pngYellowFile.fileName(), "PNG" ); + + QFileInfo pngYellowFileInfo( pngYellowFile.fileName() ); + + // Need to set object parent for relative paths to work + object.setParent( &model ); + + object.setFilenameNode( TextNode( false, pngYellowFileInfo.fileName() ) ); // Relative path + + model.addObject( object.clone() ); + + /// + /// Filename object, cyan svg, magenta shadow + /// + object.setY0( 41 ); + object.setSize( 8, 8 ); + object.setShadow( true ); + object.setShadowColorNode( ColorNode( Qt::magenta ) ); + object.setShadowOpacity( 1 ); + + // Cyan 8x8 square svg + svg = glabels::test::cyan_8x8_svg; + QTemporaryFile svgCyanFile( svgTemplate ); svgCyanFile.open(); svgCyanFile.write( svg ); svgCyanFile.close(); + + QFileInfo svgCyanFileInfo( svgCyanFile.fileName() ); + object.setFilenameNode( TextNode( false, svgCyanFileInfo.filePath() ) ); // Absolute path + + model.addObject( object.clone() ); + + /// + /// Set up merge + /// + + // Blue 8x8 square pgn + pngArray = QByteArray::fromBase64( glabels::test::blue_8x8_png ); QVERIFY( png.loadFromData( pngArray, "PNG" ) ); QTemporaryFile png1; png1.open(); png1.close(); png.save( png1.fileName(), "PNG" ); QTemporaryFile png2; png2.open(); png2.close(); png.save( png2.fileName(), "PNG" ); - // Red 8x8 square - QByteArray svg = glabels::test::red_8x8_svg; - QString svgTemplate = QDir::tempPath().append( QDir::separator() ).append( "TestModelImageObject_XXXXXX.svg" ); + // Red 8x8 square svg + svg = glabels::test::red_8x8_svg; QTemporaryFile svg1( svgTemplate ); svg1.open(); svg1.write( svg ); svg1.close(); QTemporaryFile svg2( svgTemplate ); svg2.open(); svg2.write( svg ); svg2.close(); @@ -86,9 +169,6 @@ void TestModelImageObject::readImageFile() QFileInfo svg1FileInfo( svg1.fileName() ); QFileInfo svg2FileInfo( svg2.fileName() ); - QString modelFileName = png1FileInfo.dir().absolutePath().append( QDir::separator() ).append( "TestModelImageObject.glabels" ); - model.setFileName( modelFileName ); - QTemporaryFile csv; csv.open(); csv.write( "id,image,type\n" ); @@ -108,41 +188,67 @@ void TestModelImageObject::readImageFile() merge->setSource( csv.fileName() ); model.setMerge( merge ); - // Green 8x8 square - pngArray = QByteArray::fromBase64( glabels::test::green_8x8_png ); - QVERIFY( png.loadFromData( pngArray, "PNG" ) ); - QTemporaryFile png3; png3.open(); png3.close(); png.save( png3.fileName(), "PNG" ); - QFileInfo png3FileInfo( png3.fileName() ); - - Variable var( Variable::Type::STRING, "var", png3FileInfo.fileName(), Variable::Increment::PER_ITEM ); - model.variables()->addVariable( var ); - + /// + /// Draw + /// const QList records = merge->selectedRecords(); QCOMPARE( records.size(), 8 ); + + QImage paintDevice( 10, 10 * model.objectList().size() * records.size(), QImage::Format_RGB32 ); + paintDevice.fill( Qt::white ); + QPainter painter( &paintDevice ); + int i, cnt; + int yTranslate = 10 * model.objectList().size(); for ( i = 0, cnt = records.size(); i < cnt; i++ ) { model.draw( &painter, false, records[i], model.variables() ); - painter.translate( 0, 20 ); + painter.translate( 0, yTranslate ); } - paintDevice.save( "/tmp/readimagefile.png", "PNG" ); - QColor color, white = Qt::white; + QColor color, white = Qt::white, grayShadow = Qt::gray, yellowShadow = Qt::yellow, cyanShadow = Qt::cyan, magentaShadow = Qt::magenta; for ( i = 0, cnt = records.size(); i < cnt; i++ ) { + // Merge qDebug() << "record" << i; color = records[i]->value( "type" ) == "png" ? Qt::blue : Qt::red; - QCOMPARE( white, paintDevice.pixelColor( 1, 0 + i * 20 ) ); - QCOMPARE( color, paintDevice.pixelColor( 1, 1 + i * 20 ) ); - QCOMPARE( color, paintDevice.pixelColor( 1, 8 + i * 20 ) ); - QCOMPARE( white, paintDevice.pixelColor( 1, 9 + i * 20 ) ); + QCOMPARE( paintDevice.pixelColor( 1, 0 + i * yTranslate ), white ); + QCOMPARE( paintDevice.pixelColor( 1, 1 + i * yTranslate ), color ); + QCOMPARE( paintDevice.pixelColor( 1, 8 + i * yTranslate ), color ); + QCOMPARE( paintDevice.pixelColor( 1, 9 + i * yTranslate ), white ); + QCOMPARE( paintDevice.pixelColor( 9, 9 + i * yTranslate ), white ); // No shadow // Variable color = Qt::green; - QCOMPARE( white, paintDevice.pixelColor( 1, 10 + i * 20 ) ); - QCOMPARE( color, paintDevice.pixelColor( 1, 11 + i * 20 ) ); - QCOMPARE( color, paintDevice.pixelColor( 1, 18 + i * 20 ) ); - QCOMPARE( white, paintDevice.pixelColor( 1, 19 + i * 20 ) ); + QCOMPARE( paintDevice.pixelColor( 1, 10 + i * yTranslate ), white ); + QCOMPARE( paintDevice.pixelColor( 1, 11 + i * yTranslate ), color ); + QCOMPARE( paintDevice.pixelColor( 1, 18 + i * yTranslate ), color ); + QCOMPARE( paintDevice.pixelColor( 1, 19 + i * yTranslate ), white ); + QCOMPARE( paintDevice.pixelColor( 9, 19 + i * yTranslate ), grayShadow ); + + // Variable 2 + color = Qt::magenta; + QCOMPARE( paintDevice.pixelColor( 1, 20 + i * yTranslate ), white ); + QCOMPARE( paintDevice.pixelColor( 1, 21 + i * yTranslate ), color ); + QCOMPARE( paintDevice.pixelColor( 1, 28 + i * yTranslate ), color ); + QCOMPARE( paintDevice.pixelColor( 1, 29 + i * yTranslate ), white ); + QCOMPARE( paintDevice.pixelColor( 9, 29 + i * yTranslate ), yellowShadow ); + + // Filename pgn + color = Qt::yellow; + QCOMPARE( paintDevice.pixelColor( 1, 30 + i * yTranslate ), white ); + QCOMPARE( paintDevice.pixelColor( 1, 31 + i * yTranslate ), color ); + QCOMPARE( paintDevice.pixelColor( 1, 38 + i * yTranslate ), color ); + QCOMPARE( paintDevice.pixelColor( 1, 39 + i * yTranslate ), white ); + QCOMPARE( paintDevice.pixelColor( 9, 39 + i * yTranslate ), cyanShadow ); + + // Filename svg + color = Qt::cyan; + QCOMPARE( paintDevice.pixelColor( 1, 40 + i * yTranslate ), white ); + QCOMPARE( paintDevice.pixelColor( 1, 41 + i * yTranslate ), color ); + QCOMPARE( paintDevice.pixelColor( 1, 48 + i * yTranslate ), color ); + QCOMPARE( paintDevice.pixelColor( 1, 49 + i * yTranslate ), white ); + QCOMPARE( paintDevice.pixelColor( 9, 49 + i * yTranslate ), magentaShadow ); } delete model.merge(); diff --git a/model/unit_tests/TestXmlLabel.cpp b/model/unit_tests/TestXmlLabel.cpp index 01a468d..0199228 100644 --- a/model/unit_tests/TestXmlLabel.cpp +++ b/model/unit_tests/TestXmlLabel.cpp @@ -139,7 +139,7 @@ void TestXmlLabel::serializeDeserialize() QCOMPARE( objects.at(i)->filenameNode().isField(), outObjects.at(i)->filenameNode().isField() ); QCOMPARE( objects.at(i)->filenameNode().data().isEmpty(), outObjects.at(i)->filenameNode().data().isEmpty() ); - if ( objects.at(i)->filenameNode().data().isEmpty() || objects.at(i)->filenameNode().isField() || (!objects.at(i)->image() && objects.at(i)->svg().isEmpty()) ) + if ( objects.at(i)->filenameNode().data().isEmpty() || objects.at(i)->filenameNode().isField() ) { QVERIFY( objects.at(i)->filenameNode() == outObjects.at(i)->filenameNode() ); } diff --git a/model/unit_tests/Test_Constants.h b/model/unit_tests/Test_Constants.h index b9f063b..f57b640 100644 --- a/model/unit_tests/Test_Constants.h +++ b/model/unit_tests/Test_Constants.h @@ -29,7 +29,10 @@ namespace glabels const char* blue_8x8_png = "iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAABhGlDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw1AUhU9TpVIqDhYRcchQnSyIijhqFYpQIdQKrTqYvPQPmjQkKS6OgmvBwZ/FqoOLs64OroIg+APi4uqk6CIl3pcUWsR44fE+zrvn8N59gNCoMM3qGgc03TbTyYSYza2KoVeEEMYAAhBkZhlzkpSCb33dUx/VXZxn+ff9Wb1q3mJAQCSeZYZpE28QT2/aBud94igrySrxOfGYSRckfuS64vEb56LLAs+Mmpn0PHGUWCx2sNLBrGRqxFPEMVXTKV/Ieqxy3uKsVWqsdU/+wkheX1nmOq1hJLGIJUgQoaCGMiqwEaddJ8VCms4TPv4h1y+RSyFXGYwcC6hCg+z6wf/g92ytwuSElxRJAN0vjvMxAoR2gWbdcb6PHad5AgSfgSu97a82gJlP0uttLXYE9G0DF9dtTdkDLneAwSdDNmVXCtISCgXg/Yy+KQf03wLhNW9urXOcPgAZmlXqBjg4BEaLlL3u8+6ezrn929Oa3w/Q2XJm1/XlIwAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAuIwAALiMBeKU/dgAAAAd0SU1FB+MHChYzAoNXJCYAAAAWSURBVBjTY2Rg+P+fAQ9gYiAAhocCABBdAg7zMxsKAAAAAElFTkSuQmCC"; const char* green_8x8_png = "iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAABhGlDQ1BJQ0MgcHJvZmlsZQAAKJF9kTtIw1AUhv+mikUqDlYQcchQnSz4Qhy1CkWoEGqFVh1MbvqCJg1Jiouj4Fpw8LFYdXBx1tXBVRAEHyAurk6KLlLiuUmhRYwXDvfjv/f/OfdcQKiXmWZ1jAGabpupRFzMZFfFrleE0E81jpDMLGNOkpLwXV/3CPD9Lsaz/O/9uXrUnMWAgEg8ywzTJt4gnt60Dc77xBFWlFXic+JRkxokfuS64vEb54LLAs+MmOnUPHGEWCy0sdLGrGhqxFPEUVXTKV/IeKxy3uKslaus2Sd/YTinryxznWoICSxiCRJEKKiihDJsxGjXSbGQovO4j3/Q9UvkUshVAiPHAirQILt+8D/4PVsrPznhJYXjQOeL43wMA127QKPmON/HjtM4AYLPwJXe8lfqwMwn6bWWFj0CereBi+uWpuwBlzvAwJMhm7IrBamEfB54P6NvygJ9t0D3mje35jlOH4A0zSp5AxwcAiMFyl73eXeofW7/3mnO7wdSvnKatbS90wAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAAd0SU1FB+MIFQovFXnldrAAAAAWSURBVBjTY2T4z/CfAQ9gYiAAhocCABFcAg5KXrI7AAAAAElFTkSuQmCC"; + const char* yellow_8x8_png = "iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAABhGlDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw0AcxV9TpUUqIhYs4pChOlkQFXHUKhShQqgVWnUwufQLmjQkLS6OgmvBwY/FqoOLs64OroIg+AHi4uqk6CIl/i8ptIjx4Lgf7+497t4BQqPMNKtrHND0qplKxMVMdlUMvCKIQfjRj4jMLGNOkpLwHF/38PH1LsazvM/9OXrVnMUAn0g8ywyzSrxBPL1ZNTjvE4dZUVaJz4nHTLog8SPXFZffOBccFnhm2Eyn5onDxGKhg5UOZkVTI54ijqqaTvlCxmWV8xZnrVxjrXvyF4Zy+soy12kOI4FFLEGCCAU1lFBGFTFadVIspGg/7uEfcvwSuRRylcDIsYAKNMiOH/wPfndr5Scn3KRQHOh+se2PESCwCzTrtv19bNvNE8D/DFzpbX+lAcx8kl5va9EjoG8buLhua8oecLkDRJ4M2ZQdyU9TyOeB9zP6piwwcAv0rLm9tfZx+gCkqavkDXBwCIwWKHvd493Bzt7+PdPq7wcjL3KHuPu4MgAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAAd0SU1FB+MIFwMyBT7m+cwAAAAWSURBVBjTY/z/n+E/Ax7AxEAADA8FABdkAw08uCaCAAAAAElFTkSuQmCC"; const char* red_8x8_svg = ""; + const char* cyan_8x8_svg = ""; + const char* magenta_8x8_svg = ""; } }