Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[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 23, 2018
1 parent 59ec2e0 commit 19f8a00
Show file tree
Hide file tree
Showing 5 changed files with 31 additions and 0 deletions.
7 changes: 7 additions & 0 deletions src/core/symbology/qgssvgcache.cpp
Expand Up @@ -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
{
Expand Down
24 changes: 24 additions & 0 deletions tests/src/core/testqgssvgcache.cpp
Expand Up @@ -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();

};

Expand Down Expand Up @@ -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
Expand Down
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 19f8a00

Please sign in to comment.