Skip to content
Permalink
Browse files

[FEATURE] Allow embedding SVG images in projects in base64 format

Allows SVG images for symbology, labels, etc to be directly embedded
inside a project file (or QML style, or QPT print template!)
by encoding the svg as a standard base64 string.

Sponsored by SMEC/SJ
  • Loading branch information
nyalldawson committed Jul 18, 2018
1 parent 59ec2e0 commit 19f8a0078d2c90036178db71b8e9e1de547fee93
@@ -399,6 +399,13 @@ QByteArray QgsSvgCache::getImageData( const QString &path ) const
}
}

// maybe it's an embedded base64 string
if ( path.startsWith( QLatin1String( "base64:" ), Qt::CaseInsensitive ) )
{
QByteArray base64 = path.mid( 7 ).toLocal8Bit(); // strip 'base64:' prefix
return QByteArray::fromBase64( base64, QByteArray::OmitTrailingEquals );
}

// maybe it's a url...
if ( !path.contains( QLatin1String( "://" ) ) ) // otherwise short, relative SVG paths might be considered URLs
{
@@ -51,6 +51,7 @@ class TestQgsSvgCache : public QObject
void threadSafePicture();
void threadSafeImage();
void changeImage(); //check that cache is updated if svg source file changes
void base64();

};

@@ -234,6 +235,29 @@ void TestQgsSvgCache::changeImage()
QVERIFY( imageCheck( "svgcache_changed_before", img, 30 ) );
}

void TestQgsSvgCache::base64()
{
// test rendering svgs encoded in base 64
QgsSvgCache cache;
bool inCache = false;

// invalid base64 strings
QImage img = cache.svgAsImage( QStringLiteral( "base64:" ), 200, QColor( 0, 0, 0 ), QColor( 0, 0, 0 ), 1.0,
1.0, inCache );
QVERIFY( imageCheck( QStringLiteral( "null_image" ), img, 0 ) );

img = cache.svgAsImage( QStringLiteral( "base64:zzzzzzzzzzzzzzzzzzzz" ), 200, QColor( 0, 0, 0 ), QColor( 0, 0, 0 ), 1.0,
1.0, inCache );
QVERIFY( imageCheck( QStringLiteral( "null_image" ), img, 0 ) );

//valid base 64
img = cache.svgAsImage( QStringLiteral( "base64:PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxNi4wLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4KCjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM6c29kaXBvZGk9Imh0dHA6Ly9zb2RpcG9kaS5zb3VyY2Vmb3JnZS5uZXQvRFREL3NvZGlwb2RpLTAuZHRkIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgdmVyc2lvbj0iMS4xIgogICBpZD0ic3ZnMiIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiCiAgIHNvZGlwb2RpOmRvY25hbWU9InNob3BwaW5nX2RpeS5zdmciCiAgIGlua3NjYXBlOnZlcnNpb249IjAuOTEgcjEzNzI1IgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICB4PSIwcHgiCiAgIHk9IjBweCIKICAgd2lkdGg9IjMwNi4zMzQ3NSIKICAgaGVpZ2h0PSI0ODQuNzk5OTkiCiAgIHZpZXdCb3g9IjAgMCAzMDYuMzM0NzUgNDg0Ljc5OTk5IgogICBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCA1ODAgNTgwIgogICB4bWw6c3BhY2U9InByZXNlcnZlIj48bWV0YWRhdGEKICAgICBpZD0ibWV0YWRhdGEyNiI+PHJkZjpSREY+PGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPjxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PjxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz48ZGM6dGl0bGU+PC9kYzp0aXRsZT48L2NjOldvcms+PC9yZGY6UkRGPjwvbWV0YWRhdGE+PHNvZGlwb2RpOm5hbWVkdmlldwogICAgIHNob3dncmlkPSJmYWxzZSIKICAgICBpbmtzY2FwZTpjeT0iLTExNS4wNDM3MiIKICAgICBpbmtzY2FwZTpjeD0iMTMwLjIyMTYxIgogICAgIGlua3NjYXBlOnpvb209IjAuNDYwODM4NTYiCiAgICAgcGFnZWNvbG9yPSIjZmZmZmZmIgogICAgIGJvcmRlcmNvbG9yPSIjNjY2NjY2IgogICAgIGd1aWRldG9sZXJhbmNlPSIxMC4wIgogICAgIG9iamVjdHRvbGVyYW5jZT0iMTAuMCIKICAgICBncmlkdG9sZXJhbmNlPSIxMC4wIgogICAgIGJvcmRlcm9wYWNpdHk9IjEuMCIKICAgICBpZD0iYmFzZSIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJzdmcyIgogICAgIGlua3NjYXBlOndpbmRvdy15PSIzNCIKICAgICBpbmtzY2FwZTp3aW5kb3cteD0iNzUiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTAxNCIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSI3MTEiCiAgICAgZml0LW1hcmdpbi10b3A9IjAiCiAgICAgZml0LW1hcmdpbi1sZWZ0PSIwIgogICAgIGZpdC1tYXJnaW4tcmlnaHQ9IjAiCiAgICAgZml0LW1hcmdpbi1ib3R0b209IjAiCiAgICAgaW5rc2NhcGU6d2luZG93LW1heGltaXplZD0iMCIgLz48ZGVmcwogICAgIGlkPSJkZWZzNCIgLz48ZwogICAgIGlkPSJsYXllcjMiCiAgICAgdHJhbnNmb3JtPSJtYXRyaXgoNDguMTQ5NjksMCwwLDQ4LjE0OTY5LC02NzMuMTA0NTMsLTgwLjkwNTc1MikiCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheW91dCIKICAgICBkaXNwbGF5PSJub25lIgogICAgIHN0eWxlPSJkaXNwbGF5Om5vbmUiPjxyZWN0CiAgICAgICBpZD0icmVjdDQxMzQiCiAgICAgICB4PSIxIgogICAgICAgeT0iMSIKICAgICAgIGRpc3BsYXk9ImlubGluZSIKICAgICAgIHdpZHRoPSIxMCIKICAgICAgIGhlaWdodD0iMTAiCiAgICAgICBzdHlsZT0iZGlzcGxheTppbmxpbmU7ZmlsbDpub25lO3N0cm9rZTojNzU3NTc1O3N0cm9rZS13aWR0aDowLjEiIC8+PHJlY3QKICAgICAgIGlkPSJyZWN0NDEzNiIKICAgICAgIHg9IjIiCiAgICAgICB5PSIyIgogICAgICAgZGlzcGxheT0iaW5saW5lIgogICAgICAgd2lkdGg9IjgiCiAgICAgICBoZWlnaHQ9IjgiCiAgICAgICBzdHlsZT0iZGlzcGxheTppbmxpbmU7ZmlsbDpub25lO3N0cm9rZTojNzU3NTc1O3N0cm9rZS13aWR0aDowLjEiIC8+PC9nPjxwYXRoCiAgICAgZmlsbD0icGFyYW0oZmlsbCkiCiAgICAgc3Ryb2tlPSJwYXJhbShvdXRsaW5lKSIKICAgICBzdHJva2Utd2lkdGg9InBhcmFtKG91dGxpbmUtd2lkdGgpIgogICAgIGQ9Ik0gMzA2LjI5NTc0LDE5LjU1NCBDIDMwNi4yMTk3NCw4LjU4OSAyOTYuNjg3NzQsMCAyODQuNTk1NzQsMCBsIC0wLjE3OSwwLjAwMSBjIC0xMC4xOTEsMCAtMTguNzksNi40ODggLTIwLjg5MSwxNS41ODYgLTE0LjM2LC0wLjkxNSAtMjEuMTM2LC0zLjYwMiAtMjguMjk4LC02LjQ0MSAtOC44MzQsLTMuNTAzIC0xNy45NjksLTcuMTI1IC00MS40MjEsLTguMjgyIGwgLTc3Ljc5LC0wLjcyIEMgNjcuNzkyNzQyLDAuNjgyIDI4LjgwODc0MiwyOC42MjQgMC4xNDU3NDIwMSw4My4xOTMgYyAtMC4yNjksMC41MTIgLTAuMTU4LDEuMTQgMC4yNywxLjUyNyAwLjQyOCwwLjM4OSAxLjA2Mjk5OTk5LDAuNDM4IDEuNTQ1OTk5OTksMC4xMjMgbCAyLjU2MywtMS42NzggYyAwLjE4OCwtMC4xMjMgMC4zNCwtMC4yOTMgMC40NCwtMC40OTQgOS42MzgsLTE5LjMxNiA0NS43ODUsLTM2LjI2IDc3LjM1NSwtMzYuMjYgMjAuNzk0OTk4LDAgMzUuMjgzOTk4LDcuNDg4IDQwLjgxNDk5OCwyMS4wODggMS41MjYsNy44ODUgOS43OCwxNS4zNDUgMTguNzUzLDE3LjExMSBsIDAuMjMyLDEzNi4xNTkgYyAtOC4wNDcsMC44NTQgLTE2LjI2NSw5LjQ4NCAtMTYuNDI5LDE3LjY1NiBsIC00LjI2NCwyMjQuMjg2IGMgLTAuMTI5LDYuODQ0IDEuNjEyLDEyLjE2OSA1LjE3NSwxNS44MyA1Ljk3NSw2LjEzOCAxNS41NjgsNi4yMTQgMjEuMyw2LjI1OSAwLjAwNCwwIDAuMDA4LDAgMC4wMTIsMCBsIDQxLjA2MSwtMC4wNjIgYyA2LjU1NCwtMC4wMyAxNS43OTQsLTIuNzggMjEuNjYsLTguODU1IDQuMTAxLC00LjI0NiA2LjA4NCwtOS41MjcgNS44OTYsLTE1LjY5MyBsIC02LjE2NCwtMjIxLjgxNSBjIC0wLjIxNiwtOC4wMzUgLTguNDU2LC0xNi42MTYgLTE2LjQ1NCwtMTcuNTA3IGwgLTAuMTc5LC0xMzYuNjA1IGMgMTIuMDU3LC0wLjkzNCAyMC4zNDgsLTYuMDU2IDI2LjAyNywtMTYuMDUzIDQuMjM0LC03LjQ1IDEwLjk5OSwtMTAuNzczIDIxLjkzNSwtMTAuNzczIDQuNzE3LDAgOS43NTgsMC41OTggMTQuNjMzLDEuMTc2IDIuMTE4LDAuMjUxIDQuMjk3LDAuNTEgNi40MjIsMC43MTUgbCAtMC4wMzcsNS42NTggYyAwLjA3NSwxMC44MTMgOS44NjIsMTkuNjA5IDIxLjg1NCwxOS42MDkgMTIuMDE4LC0wLjAxOSAyMS43ODIsLTguODM1IDIxLjc2NywtMTkuNjUyIGwgLTAuMDM5LC00NS4zODkgeiIKICAgICBpZD0icGF0aDI0IgogICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+PC9zdmc+" ),
200, QColor( 0, 0, 0 ), QColor( 0, 0, 0 ), 1.0,
1.0, inCache );
QVERIFY( imageCheck( QStringLiteral( "svgcache_base64" ), img, 30 ) );

}

bool TestQgsSvgCache::imageCheck( const QString &testName, QImage &image, int mismatchCount )
{
//draw background
Binary file not shown.
Binary file not shown.
Binary file not shown.

0 comments on commit 19f8a00

Please sign in to comment.
You can’t perform that action at this time.