Dynamic SVG graphics

Recently I have been using Scalable Vector Graphics (SVG) in my applications, in place of typical raster images or even text in some cases. SVG graphics are defined through code, as per the examples here: https://www.w3schools.com/graphics/svg_examples.asp

As the images are defined with code, you can CONCATENATE() strings of code together while including application variables, to make the images dynamic. In this way, you could have animated images which react to user input.

Another thing to note, is SVG graphics scale/resize losslessly (unlike raster images). So everything will always look crisp! Also, when properly optimized, the file-sizes are often smaller than raster images.

There is a trade-off, in that the device must decode and render the graphics. Older devices/computers may have a hard time rendering poorly optimized or complex SVG code. Also, older browsers do not support some SVG functionality.


Some examples:

Richer looking detail headers, and small filesize gradient backgrounds:

SVG thumbnails, without having to rely on external services like Image Placeholder:

Dynamic progress bars:

@morgan
@praveen

28 156 22.9K
  • UX
156 REPLIES 156

Thank you so much @Jonathon.

One question: what is the difference between SVG and base64? Is base64 also scalable?
When do you use SVG and when base64?

https://stackoverflow.com/questions/6792003/utf-8-encoding-vs-base-64-encoding?rq=1

UTF-8 is a text encoding - a way of encoding text as binary data.

Base64 is in some ways the opposite - itโ€™s a way of encoding arbitrary binary data as ASCII text.

If you need to encode arbitrary binary data as text, Base64 is the way to go - you mustnโ€™t try to treat arbitrary binary data as if itโ€™s UTF-8 encoded text data.

However, you may well be able to transfer the file to the server just as binary data in the first place - it depends on what transport youโ€™re using.


For static images I will sometimes use the base64 encoded equivalent to avoid any issues that may crop up as a result of not using ENCODEURL().

When storing svg code in a database, say for a table of icons, I will also store those as base64 strings.

Did someone try to use a SVG code for the Launch image?
I donโ€™t get it working.

My goal is to use dynamic SVG Code depending on the USERLOCALE(). Like a logo with some additional text like โ€œWelcomeโ€ in different languages, depending on the USERLOCALE().

You can use SVG here - remove the quotes at the start and end of the code.

Edit: I should clarify, while you can definitely use static SVG images as your launch image, I donโ€™t think you can make them dynamic. Launch images and backgrounds donโ€™t currently support logic.

Thank you Jonathon. I had to remove the quotes at the start and end of the code AND change the double quotes to single quotes.

PROBLEM: Even though the SVG is visible in the Editor under App logo / Launch image, it will not really work.
In Android you can not create shortcut, because the SVG fails.
Also the Launch image doesnโ€™t show up.

PROBLEM: Even though the SVG is visible in the Editor under App logo / Launch image, it will not really work.
In Android you can not create shortcut, because the SVG fails.
Also the Launch image doesnโ€™t show up.

When using SVGโ€™s in place of the brand logo loading screens there are some things to be aware of:

  • As you noted, SVGโ€™s donโ€™t work for app shortcuts, so you should use a .png logo in these cases
  • In general, there should be no issues with using SVGโ€™s for loading screens or app background as these items are only displayed once the app is loaded

Currently I have my app launcher using a .png logo to avoid these issues. However, all of my other apps which are accessed solely through the app launcher use svgโ€™s.

@Fabian

Thanks @Jonathon Iโ€™m using this very simple SVG as the Launch image and the Background image.

data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="200" height="100" version="1.1"> <rect width="200" height="100" stroke="black" stroke-width="6" fill="green"/> </svg>

Both are not working. The show up in the editor, but not in the App. There is also a warning message:

Are you sure BackgroundImage url 'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="200" height="100" version="1.1"> <rect width="200" height="100" stroke="black" stroke-width="6" fill="green"/> </svg>' refers to an image?

Are you sure PageBackgroundImage url 'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="200" height="100" version="1.1"> <rect width="200" height="100" stroke="black" stroke-width="6" fill="green"/> </svg>' refers to an image?

Here is a nice Video of what you can do with SVG: https://www.youtube.com/watch?v=z8EppLDkgtw
In the Video you can see some examples from codepen.io.

There I found this nice loader: https://codepen.io/ryanallen/pen/OPpbbR

Here is the AppSheet code for the first one. Note: If you want to use it in forms, you should choose column type SHOW and Category IMAGE. Because if you choose column type IMAGE, than you have a gray box around the SVG.

โ€œdata:image/svg+xml;utf8,
<svg xmlns=""http://www.w3.org/2000/svg"" 
	width=""770"" height=""200""
	viewBox=""0 0 80 80"" 
	preserveAspectRatio=""xMidYMin meet""
  xml:space=""preserve"">
	<path
	fill=""rgb(63,129,141)"" 
    stroke=""black""
    stroke-width=""0.5""
		d=""M40,72C22.4,72,8,57.6,8,40C8,22.4,
		22.4,8,40,8c17.6,0,32,14.4,32,32c0,1.1-0.9,2-2,2
		s-2-0.9-2-2c0-15.4-12.6-28-28-28S12,24.6,12,40s12.6,
		28,28,28c1.1,0,2,0.9,2,2S41.1,72,40,72z""
	>
		<animateTransform
			attributeType=""xml""
			attributeName=""transform""
			type=""rotate""
			from=""0 40 40""
			to=""360 40 40""
			dur=""1.0s""
			repeatCount=""indefinite""
		/>
	</path>
</svg>โ€

This will look like:
2X_1_161b33dc158a2c81d91eacecca7caa66998b006c.png

Note: By using the combination of width=""770"" and preserveAspectRatio=""xMidYMin meet"", the SVG will be always centered. Because 770px seems to be the highest width in AppSheet (in Detail View).
A nice side effect: On mobile, the SVG will be smaller than on big screens.

I have an image column in child table with " CONCATENATE("data:image/svg+xml;utf8, <svgโ€ฆ " appformula, and try to use VC in parent row to show the image. It works fine in editor and when using app in laptop Chrome, but not working on iPhone. Any idea why? VC is type image, and formula is ANY( [related childrows][ImageColumn] )

How is it not working? One thing to keep in mind when working with SVGโ€™s, is that different browsers render them differently. In some cases SVGโ€™s may appear different through the chrome browser than through Safari (iOS).

This would be one thing to check during troubleshooting. Have you had success getting any SVG to work, even a simple rectangle?

I got it working now, donโ€™t know how or whyโ€ฆ Image wasnโ€™t loading on iPhone at all, there was just the triangle w/ exclamation mark showing

Color Mixer


This could be used in USERSETTINGS as well to generate the desired color. Then this RGB could be used all over the App.

Wow Fabian, thatโ€™s excellent!

Hi @Fabian!

Sorry but I donโ€™t understand how to use such a color mixer myself. Is this something you made to work in AppSheet? Or, is it on the web? If you could tell me how to access it, Iโ€™d appreciate it.

Did someone work out a Pie Chart with SVG?
2X_a_a718fcd8cb6cfb69bf4f62fef3bbc9d7b8be7a0e.png

Hey Fabian, just did some experimenting with SVG donut charts:

2X_f_f3c67819765b032580774d4cf89a51e20dfcec3e.png

I have included two code snippets at the bottom of this post, โ€˜simpleโ€™ and โ€˜complexโ€™, although complex isnโ€™t much more difficult. In any case, let me know if you have any issues - I havenโ€™t actually tested this in my applications .

Some background info:

The circles are drawn with radius r="15.915...", which is the radius of a circle with circumference of 100. This will make your math easier in AppSheet as the input parameters can be numeric percentage values out of 100.

The stroke-dasharray parameter is used to make dashed lines. It takes two inputs: the length of the dash segment, and the gap between the next one. We always use a dash gap equal to the circles circumference, so we only see a single dash.

The stroke-dashoffset parameter, as the name implies, offsets the whole stroke along the path. This can be used to shift the donut chart segments so they arenโ€™t overlapping eachother.


Simple

In the simple solution we eliminate the need for stroke-dashoffset by using cumulative percentages and drawing the segments ontop of eachother. The downside here is that the layers may bleed through eachother making the resulting image less โ€˜crispโ€™ (aka potentially less pretty), particularly on lower DPI screens.

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 42 42">
  <g fill="none" stroke-width="3">
    <circle cx="21" cy="21" r="15.91549430918952" stroke="orange" stroke-dasharray="100 100"></circle>
    <circle cx="21" cy="21" r="15.91549430918952" stroke="green" stroke-dasharray="95 100"></circle>
    <circle cx="21" cy="21" r="15.91549430918952" stroke="red" stroke-dasharray="45 100"></circle>
    <circle cx="21" cy="21" r="15.91549430918952" stroke="blue" stroke-dasharray="25 100"></circle>
  </g>
</svg>

Complex

The complex solution utilizes the stroke-dashoffset parameter by drawing the segments at their exact percentage value and shifting them around the donut chart accordingly.

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 42 42">
  <g fill="none" stroke-width="3">
    <circle cx="21" cy="21" r="15.91549430918952" stroke="blue" stroke-dasharray="25 100" stroke-dashoffset="0"></circle>
    <circle cx="21" cy="21" r="15.91549430918952" stroke="red" stroke-dasharray="20 100" stroke-dashoffset="-25"></circle>
    <circle cx="21" cy="21" r="15.91549430918952" stroke="green" stroke-dasharray="50 100" stroke-dashoffset="-45"></circle>
    <circle cx="21" cy="21" r="15.91549430918952" stroke="orange" stroke-dasharray="5 100" stroke-dashoffset="-95"></circle>
  </g>
</svg>

Iโ€™m very sad that I have found this post because it seems like I now have a whole new thing I need to now explore. With no understanding of what exactly SVG is or what yaโ€™ll are doing, this is almost a
sudo-html-ish encoding into appsheet via images?

SVGโ€™s arenโ€™t officially supported by AppSheet. If youโ€™re newer to the platform you may be better off avoiding SVGโ€™s and seeing what else AppSheet has to offer.

I think itโ€™s on ASโ€™s roadmap to either support SVGโ€™s more natively or to add improved charting features which make SVGโ€™s less compelling.

Well the visual aspect of our apps is definitely something we are always looking to expand upon so anything we can find that can improve the appeal and ease of use of our apps is worth at least looking into.

Iโ€™m trying to use this to wrap text in a rendered SVG so I can use it in a deck view anyone know of a method?

@Austin_Kerr can you please add a screenshot of what you want? There are many possibilities.

โ€œdata:image/svg+xml;utf8,<svg width=โ€โ€œ200"โ€ height="โ€œ200"โ€ xmlns="โ€œhttp://www.w3.org/2000/svgโ€" xmlns:xlink="โ€œhttp://www.w3.org/1999/xlinkโ€"> <path id="โ€œpath1"โ€ d="โ€œM10,30 H190 M10,60 H190 M10,90 H190 M10,120 H190"โ€> <use xlink:href=""#path1"" x="โ€œ0"โ€ y="โ€œ35"โ€ stroke="โ€œblueโ€" stroke-width="โ€œ1"โ€ /> <text transform="โ€œtranslate(0,35)โ€" fill="โ€œredโ€" font-size="โ€œ20"โ€> <textPath xlink:href=""#path1"">This is a long long long text โ€ฆ "

@Fabian

Seems not to run in AppSheet nor in Google Chrome nor in Firefox.

This is the right SVG code:

<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<path id="path1" d="M10,30 H190 M10,60 H190 M10,90 H190 M10,120 H190"></path>
</defs>
<use xlink:href="#path1" x="0" y="35" stroke="blue" stroke-width="1" />
<text transform="translate(0,35)" fill="red" font-size="20">
<textPath xlink:href="#path1">This is a long long long text ......</textPath>
</text>
</svg>

You can enter it in this editor:
https://www.rapidtables.com/web/tools/svg-viewer-editor.html

To preview it in a browser you just have to add a data:image/svg+xml;utf8, in front like:

data:image/svg+xml;utf8,<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <defs> <path id="path1" d="M10,30 H190 M10,60 H190 M10,90 H190 M10,120 H190"></path> </defs> <use xlink:href="#path1" x="0" y="35" stroke="blue" stroke-width="1" /> <text transform="translate(0,35)" fill="red" font-size="20"> <textPath xlink:href="#path1">This is a long long long text ......</textPath> </text> </svg>

But this gives an error in Browser.

The problem seems to be with the textPath.

@Jonathon do you have any idea?

Some utf8 characters need to be encoded to work as a URL - this is what the ENCODEURL() function in appsheet accomplishes.

In the above example, the problematic character is the hashtag (#) symbol. We can manually encode them by replacing them with %23:

data:image/svg+xml;utf8,
<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <defs>
<path id="path1" d="M10,30 H190 M10,60 H190 M10,90 H190 M10,120 H190"></path>
</defs>
<use xlink:href="%23path1" x="0" y="35" stroke="blue" stroke-width="1" />
<text transform="translate(0,35)" fill="red" font-size="20"> <textPath xlink:href="%23path1">This is a long long long text ......</textPath>
</text>
</svg>

The above will run in a browser, and should run in an AppSheet app if properly double-quoted.

Argh! How could I forget. Thank you @Jonathon.
So this is working in AppSheet:

"data:image/svg+xml;utf8,
<svg width=""200"" height=""200"" xmlns=""http://www.w3.org/2000/svg"" xmlns:xlink=""http://www.w3.org/1999/xlink""> <defs>
<path id=""path1"" d=""M10,30 H190 M10,60 H190 M10,90 H190 M10,120 H190""></path>
</defs>
<use xlink:href=""%23path1"" x=""0"" y=""35"" stroke=""blue"" stroke-width=""1"" />
<text transform=""translate(0,35)"" fill=""red"" font-size=""20""> <textPath xlink:href=""%23path1"">This is a long long long text ......</textPath>
</text>
</svg>"

Hey guys think so much, Iโ€™m still a little confused, when I past exact what @Fabian wrote in the last one, Iโ€™m still getting the error, fabian did you try that exact code into a browser, tester or app sheets? Do I need to wrap it in an encode url, and then do I still use image as the column type?

@Austin_Kerr

Donโ€™t forget to wrap the expression in a CONCATENATE() to accommodate the textโ€ฆ

CONCATENATE(

โ€œdata:image/svg+xml;utf8,
<svg width=โ€โ€œ200"โ€ height="โ€œ200"โ€ xmlns="โ€œhttp://www.w3.org/2000/svgโ€" xmlns:xlink="โ€œhttp://www.w3.org/1999/xlinkโ€">
<path id="โ€œpath1"โ€ d="โ€œM10,30 H190 M10,60 H190 M10,90 H190 M10,120 H190"โ€>

<use xlink:href=""%23path1"" x="โ€œ0"โ€ y="โ€œ35"โ€ stroke="โ€œblueโ€" stroke-width="โ€œ1"โ€ />
<text transform="โ€œtranslate(0,35)โ€" fill="โ€œredโ€" font-size="โ€œ20"โ€> <textPath xlink:href=""%23path1"">",

[Text Field],

"

")

Otherwise - underline seems to work nicely too

I am wondering if anyone has been able to get Images to show within the SVG code in an AppSheet column?

I am trying to move images (like in an ad banner). With @Jonathon 's help I was able to create code that works in AppSheet that moves a generated circle along a defined path (sample AppSheet code below).

The same principle CAN be applied to Images. I was able to create an HTML web page to do it. But when inserting the code into AppSheet it didnโ€™t work.

To debug, I then reduced the code to simply show an Image inside of the SVG code. Overkill I know since it is used in an Image column but its just to try to get an image to show this way so that then I can apply motion to it. Needless to say I had no luck.

Sample code is below and again it works like this in an HTML web page.

Has anyone tried displaying images in this manner in Appsheet? Might any of you know what I need to do to correct the code below so it DOES display the image?

Sample AppSheet Motion code to Move a Circle along a Defined Path

"data:image/svg+xml;utf8, <svg xmlns=""http://www.w3.org/2000/svg"" width=""1000"" height=""400"" viewBox=""-10 -30 1000 400"">
  <circle cx="""" cy="""" r=""25"" fill=""blue"">
    <animateMotion path=""M-1,1.499h83c0,0,10.5,1,10.5,11.25s-10.25,12.25-10.25,12.25h-43.5c0,0-10.75,0.75-11.75,11.5s9.75,12.25,9.75,12.25H343.5"" dur=""2s"" repeatCount=""indefinite"" fill=""freeze"" />
  </circle>
</svg>"

AppSheet Code (double-quoted) attempting to display an Image directly from Google Drive in an AppSheet column

"data:image/svg+xml;utf8, <svg xmlns=""http://www.w3.org/2000/svg"">
  <image xlink:href=""https://drive.google.com/uc?export=view&id=1gxvUziCU3zqYpi2B3IiS4OZqcoPp8-NK"" width=""50%"" height=""50%""/>
</svg>"

14-months behind the times here, but hopefully this might be useful to someone (or at least make me feel a little better about the number of hours I sunk into getting it to work). Apologies if this is answered elsewhere. I should also say at the outset that Iโ€™ve done no testing outside of Chrome/Firefox.

The key is the SVG <foreignObject> tag, which, among other things, allows you to insert normal HTML into SVGs. You can also style it with modern CSS.

Here is some code:

"data:image/svg+xml;utf8, 
<svg width="100vw" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid meet">
  <foreignObject x="0" y="0" width="100%" height="100%">
    <!-- almost any HTML you want in here  -->
    <style xmlns="http://www.w3.org/1999/xhtml">
      ...
    </style>
    <body xmlns="http://www.w3.org/1999/xhtml">
       <img
          class="h-6 w-6 rounded-full object-cover"
            src=", TRIM("' "),
               
              SUBSTITUTE(
                SUBSTITUTE(
                  SUBSTITUTE(
                    [SOME_FIELD_STORING_AN_IMAGE_AS_DATA_URL],
                    "#",
                    "%2523"
                  ),
                  "<",
                  "&lt;"
                ),
                ">",
                "&gt;"
              ),
                
            TRIM("' "), " /> 

    </body>
  </foreignObject>
</svg>"

Iโ€™ve used <style> and <body> tags in this example. but I think it will work with most other HTML tags.

Some caveats:

  • It only works with images with a data URL as the src attribute. It will not dynamically download and render an external image file (it also doesnโ€™t seem to work with iframes for what I believe is the same reason, I triedโ€ฆ)
  • However, you can store base64-encoded images in a normal Google Sheet cell and it will work perfectly, so long as you stay under the 50,000-character/cell limit (Iโ€™ve tested SVGs, JPEGs and PNGs). This site was helpful for testing. Iโ€™ve also almost got an Apps Script function working to do the same
  • The xmlns="http://www.w3.org/1999/xhtml" attribute is necessary on each top-level tag within the <foreignObject> tag to let the renderer know that the foreign object is HTML. Maybe a little into the weeds, but I think that tag technically specifies XHTML and a quick read of this might save you a headache or two
  • To get it to work with non-base64-encoded strings such as SVGs, itโ€™s all in the quotes. The data URL will contain double quotes, so you must wrap the src attribute in single quotes, hence the TRIM("' ")s. I believe it will also work the other way around, i.e. single quotes within the data URLand double quotes enclosing the data URL
  • SVG is XML and XML hates certain characters, so you have to escape some characters, hence the nest of SUBSTITUTEs. I got it working by replacing > for &gt; and < for &lt;
  • You also have to replace # for %23 but for some reason Iโ€™ve not fully grasped yet, I also needed to double-escape (?) the % in the %23 with its corresponding character entity %25. So, # becomes %2523
  • I couldnโ€™t get it working using ENCODEURL()

This is also incredibly useful for text-heavy SVGs, as it makes text wrapping in an SVG as easy as using a <p> tag.

It does feel a little like pushing Appsheet/string concatenation to the limits, but this does also allow you to do some custom charting with plain HTML/CSS. Charts.css has been good for inspiration (though N.B. you need to manually copy the CSS, external stylesheets do not work). People do crazy things with pure CSS, even Gantt charts

@aforrester and @Jonathon , thanks to both of you for sharing your wealth of information on the topic.

Do you know if there is also currently some way to resolve even using the inline <script> tags in an SVG?. It works fine when just ran directly in a browser, but for some reason, Appsheet will only pick up the explicit SVG data like <line>, <g>, etc. But will seemingly ignore the <script> information. For reference (this is the preformatted version for use directly in browser and expected result):

3X_2_9_2966fe935e000ee29ba30556292fb8eadc7b0a96.png

<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
    <foreignObject x="0" y="0" width="100%" height="100%">
        <svg viewBox="0 0 100 100">
        <g id="vert-to-center" stroke="black" >
            <line id="vtc1" x1="50" y1="0" />
            <line id="vtc2" x1="100" y1="100" />
            <line id="vtc3" x1="0" y1="100" />
        </g>
        </svg>
        <script>
            //<![CDATA[        
            const svgns = "http://www.w3.org/2000/svg";
            const svg = document.querySelector("svg");
            
            //point data
            //list of border point values
            const bpx1 = 50;
            const bpy1 = 0;
            const bpx2 = 100;
            const bpy2 = 100;
            const bpx3 = 0;
            const bpy3 = 100;
            
            //list of center point values
            const ctrx = (bpx1 + bpx2 + bpx3)/3;
            const ctry = (bpy1 + bpy2 + bpy3)/3;
            
            //list of scaling values for internal rules
            const s0 = 1.00; 
            const s1 = 0.75;
            const s2 = 0.50;
            const s3 = 0.25;
            const s5 = 0.00;
            
            //list of values for internal rule points
            const ipx1 = bpx1 + (ctrx - bpx1) * s3;
            const ipy1 = bpy1 + (ctry - bpy1) * s3;
            const ipx2 = bpx2 + (ctrx - bpx2) * s3;
            const ipy2 = bpy2 + (ctry - bpy2) * s3;
            const ipx3 = bpx3 + (ctrx - bpx3) * s3;
            const ipy3 = bpy3 + (ctry - bpy3) * s3;
                    
            //list of internal points
            const ip1 = ipx1+","+ipy1;
            const ip2 = ipx2+","+ipy2;
            const ip3 = ipx3+","+ipy3;
            
            
            //list of border points
            const bp1 = bpx1+","+bpy1
            const bp2 = bpx2+","+bpy2
            const bp3 = bpx3+","+bpy3
            
            //create border        
            let border = document.createElementNS(svgns, "path");
            border.setAttribute("id", "border");
            border.setAttribute("stroke", "pink");
            border.setAttribute("fill", "#ff00000f");
            border.setAttribute("d", 
                "M"+bp1+
                "L"+bp2+
                "L"+bp3+
                "L"+bp1);
            
            svg.appendChild(border);
            
            
            //create internal rulers
            let intern1 = document.createElementNS(svgns, "path");
            intern1.setAttribute("id", "scale3q");
            intern1.setAttribute("stroke", "blue");
            intern1.setAttribute("stroke-width", .3);
            intern1.setAttribute("fill", "#00000000");
            intern1.setAttribute("d", 
                "M"+ip1+
                "L"+ip2+
                "L"+ip3+
                "L"+ip1);
            
            svg.appendChild(intern1);        
            
            
            //debugging point
            let devPoint1 = document.createElementNS(svgns, "circle");
            devPoint1.setAttribute("id", "point");
            devPoint1.setAttribute("stroke", "green");
            devPoint1.setAttribute("fill", "#ffffff0f");
            devPoint1.setAttribute("cx", ipx1);
            devPoint1.setAttribute("cy", ipy1);
            devPoint1.setAttribute("r", 3);
            
            
            let devPoint2 = document.createElementNS(svgns, "circle");
            devPoint2.setAttribute("id", "point");
            devPoint2.setAttribute("stroke", "green");
            devPoint2.setAttribute("fill", "#ffffff0f");
            devPoint2.setAttribute("cx", ipx2);
            devPoint2.setAttribute("cy", ipy2);
            devPoint2.setAttribute("r", 3);
            
            
            let devPoint3 = document.createElementNS(svgns, "circle");
            devPoint3.setAttribute("id", "point");
            devPoint3.setAttribute("stroke", "green");
            devPoint3.setAttribute("fill", "#ffffff0f");
            devPoint3.setAttribute("cx", ipx3);
            devPoint3.setAttribute("cy", ipy3);
            devPoint3.setAttribute("r", 3);
            
            svg.appendChild(devPoint1);
            svg.appendChild(devPoint2);
            svg.appendChild(devPoint3);
            
            
            //create quadrant separation lines
            let vtc1 = document.getElementById("vtc1");
            let vtc2 = document.getElementById("vtc2");
            let vtc3 = document.getElementById("vtc3");
            
            vtc1.setAttribute("x1", bpx1)
            vtc1.setAttribute("y1", bpy1)
            vtc1.setAttribute("x2", ctrx)
            vtc1.setAttribute("y2", ctry)
            
            vtc2.setAttribute("x1", bpx2)
            vtc2.setAttribute("y1", bpy2)
            vtc2.setAttribute("x2", ctrx)
            vtc2.setAttribute("y2", ctry)
            
            vtc3.setAttribute("x1", bpx3)
            vtc3.setAttribute("y1", bpy3)
            vtc3.setAttribute("x2", ctrx)
            vtc3.setAttribute("y2", ctry)
                    
            // ]]>
        </script>
    </foreignObject>
</svg>

This is the formatted SVG code for use in the Appsheet environment and its result:

3X_1_d_1d7f320578b156908f29b56c417d219ae3bed156.png

data:image/svg+xml;utf8,<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
    <foreignObject x="0" y="0" width="100%" height="100%">
        <svg viewBox="0 0 100 100">
        <g id="vert-to-center" stroke="black" >
            <line id="vtc1" x1="50" y1="0" />
            <line id="vtc2" x1="100" y1="100" />
            <line id="vtc3" x1="0" y1="100" />
        </g>
        </svg>
        <script>
            //<![CDATA[        
            const svgns = "http://www.w3.org/2000/svg";
            const svg = document.querySelector("svg");
            
            //point data
            //list of border point values
            const bpx1 = 50;
            const bpy1 = 0;
            const bpx2 = 100;
            const bpy2 = 100;
            const bpx3 = 0;
            const bpy3 = 100;
            
            //list of center point values
            const ctrx = (bpx1 + bpx2 + bpx3)/3;
            const ctry = (bpy1 + bpy2 + bpy3)/3;
            
            //list of scaling values for internal rules
            const s0 = 1.00; 
            const s1 = 0.75;
            const s2 = 0.50;
            const s3 = 0.25;
            const s5 = 0.00;
            
            //list of values for internal rule points
            const ipx1 = bpx1 + (ctrx - bpx1) * s3;
            const ipy1 = bpy1 + (ctry - bpy1) * s3;
            const ipx2 = bpx2 + (ctrx - bpx2) * s3;
            const ipy2 = bpy2 + (ctry - bpy2) * s3;
            const ipx3 = bpx3 + (ctrx - bpx3) * s3;
            const ipy3 = bpy3 + (ctry - bpy3) * s3;
                    
            //list of internal points
            const ip1 = ipx1+","+ipy1;
            const ip2 = ipx2+","+ipy2;
            const ip3 = ipx3+","+ipy3;
            
            
            //list of border points
            const bp1 = bpx1+","+bpy1
            const bp2 = bpx2+","+bpy2
            const bp3 = bpx3+","+bpy3
            
            //create border        
            let border = document.createElementNS(svgns, "path");
            border.setAttribute("id", "border");
            border.setAttribute("stroke", "pink");
            border.setAttribute("fill", "%23ff00000f");
            border.setAttribute("d", 
                "M"+bp1+
                "L"+bp2+
                "L"+bp3+
                "L"+bp1);
            
            svg.appendChild(border);
            
            
            //create internal rulers
            let intern1 = document.createElementNS(svgns, "path");
            intern1.setAttribute("id", "scale3q");
            intern1.setAttribute("stroke", "blue");
            intern1.setAttribute("stroke-width", .3);
            intern1.setAttribute("fill", "%2300000000");
            intern1.setAttribute("d", 
                "M"+ip1+
                "L"+ip2+
                "L"+ip3+
                "L"+ip1);
            
            svg.appendChild(intern1);        
            
            
            //debugging point
            let devPoint1 = document.createElementNS(svgns, "circle");
            devPoint1.setAttribute("id", "point");
            devPoint1.setAttribute("stroke", "green");
            devPoint1.setAttribute("fill", "%23ffffff0f");
            devPoint1.setAttribute("cx", ipx1);
            devPoint1.setAttribute("cy", ipy1);
            devPoint1.setAttribute("r", 3);
            
            
            let devPoint2 = document.createElementNS(svgns, "circle");
            devPoint2.setAttribute("id", "point");
            devPoint2.setAttribute("stroke", "green");
            devPoint2.setAttribute("fill", "%23ffffff0f");
            devPoint2.setAttribute("cx", ipx2);
            devPoint2.setAttribute("cy", ipy2);
            devPoint2.setAttribute("r", 3);
            
            
            let devPoint3 = document.createElementNS(svgns, "circle");
            devPoint3.setAttribute("id", "point");
            devPoint3.setAttribute("stroke", "green");
            devPoint3.setAttribute("fill", "%23ffffff0f");
            devPoint3.setAttribute("cx", ipx3);
            devPoint3.setAttribute("cy", ipy3);
            devPoint3.setAttribute("r", 3);
            
            svg.appendChild(devPoint1);
            svg.appendChild(devPoint2);
            svg.appendChild(devPoint3);
            
            
            //create quadrant separation lines
            let vtc1 = document.getElementById("vtc1");
            let vtc2 = document.getElementById("vtc2");
            let vtc3 = document.getElementById("vtc3");
            
            vtc1.setAttribute("x1", bpx1)
            vtc1.setAttribute("y1", bpy1)
            vtc1.setAttribute("x2", ctrx)
            vtc1.setAttribute("y2", ctry)
            
            vtc2.setAttribute("x1", bpx2)
            vtc2.setAttribute("y1", bpy2)
            vtc2.setAttribute("x2", ctrx)
            vtc2.setAttribute("y2", ctry)
            
            vtc3.setAttribute("x1", bpx3)
            vtc3.setAttribute("y1", bpy3)
            vtc3.setAttribute("x2", ctrx)
            vtc3.setAttribute("y2", ctry)
                    
            // ]]>
        </script>
    </foreignObject>
</svg>

Youโ€™ll notice that only a few lines at the top are dedicated to explicit SVG (what shows in the second image) whereas most else is supposed to be pulled from the inline JS but just gets omitted.

To my knowledge you canโ€™t use SVG that call scriptsโ€ฆ Security reasonsโ€ฆ

Hello! I used ChatGPT to translate the following text since I don't speak English very well. I apologize if my query isn't clear. I found these super interesting contributions while seeking help with SVG images in AppSheet. Thank you for sharing your ideas and experience with the community. My problem is that I haven't found a way to display an SVG image within another SVG image. I have a dynamic main SVG image where I display data from a table, which displays perfectly, and I also illustrate it with an icon, which also displays perfectly. The problem arises when I want to dynamically display an image within that SVG. In my case, these images are logos of institutions. These images were uploaded through the app and automatically stored in Drive by the same app.

I've attempted to insert that image into the SVG with the following code:

<image x="40" y="75" width="30%" height="30%"
xlink:href=
'CONCATENATE(
"https://www.appsheet.com/template/gettablefileurl",
"?appName=", ENCODEURL(CONTEXT("Application Name")),
"&tableName=", ENCODEURL(CONTEXT("Table Name")),
"&fileName=", ENCODEURL([Logo])
)'/>

But it doesn't work. I've also generated the logo in SVG format and saved it in another column, which displays correctly. However, when I try to integrate it into the main SVG, it breaks the main SVG image. I've also read that I should convert the logos' SVG to Base64, which I've also tried. When converting the PNG to SVG, it generates a very long code. I've had to divide it into 3 columns and then concatenate it into one virtual column, which also displays perfectly when viewed independently. However, when I try to embed it into the main SVG, it breaks the image again.

Has anyone tried something similar? Can anyone think of a solution?

Thank you very much for your responses!

This is a nice website where you can optimise your SVG Code to reduce file size.

https://petercollingridge.appspot.com/svg-editor

SVG functionality hasnt changed since this thread was made. What has changed is:

  1. There greater community support of SVGs as people have adopted them.
  2. Microsoft Edge browser, the spiritual successor to Internet Explorer, removes most of the headaches with SVGs not working in IE.
  3. AppSheet has made mention of SVG integration and have pushed some bugfixes in the past when an update they made broke SVGs - so they are at least maintaining functionality.

I have tested in Edge almost exclusively. I have had no issues. The only real limitation has been advanced SVG features because of the fact that its being wrapped in an HTML <img> tag. So, I canโ€™t use things like gradients or animations (at least I havenโ€™t found a way), but they everything else works so far.

You can do gradients with this:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<linearGradient id="bg" gradientUnits="userSpaceOnUse" x1="50" y1="100" x2="50" y2="0">
<stop  offset="0" style="stop-color:blue"/>
<stop  offset="1" style="stop-color:red"/>
</linearGradient>
<rect fill="url(#bg)" width="100" height="100"/>
</svg>

You will have to wrap it in ENCODEURL() as there as some troublesome characters (#)

Oooo, I was thinking I would have to do that in places, but wasnโ€™t thinking clearly on where/what. Damn you, now Iโ€™m gunna go back to the fiddling board. Since I also learned that the SVG calculations are not, in fact, the leading cause of my load time.

ENCODEURL() just translates those into unicode, right? Could I hardcode the unicode?

Encoding removes any special meaning the individual characters within some-value might have when used within a URL.

Characters with special meanings in URLs include but are not limited to: a space, ampersand (&), comma (,), hash (#), plus (+), question mark (?), semicolon (;), slash (/), and others.

For the most part you can avoid having to use ENCODEURL() by avoiding the use of the above special characters. One common place is using RGB color codes in place of hex color codes - rgb(120,120,120) vs #787878.

In the case of items like gradients, the url(#bg) cannot be substituted for (to my knowledge), so you cant avoid the use of ENCODEURL(). Another option would be to encode the entire svg string to BASE64, but then you lose the ability to keep it dynamic.

Yeah, I learned to use rgb() for the colors. So now to try and make gradients and animations work.

Letโ€™s see if this worksโ€ฆ
Example of SVG with custom embedded font, gradient, and animation. I canโ€™t directly upload SVGs here, and I canโ€™t copy the code because it goes well beyond character limits. So if youโ€™re curious click the link.

https://1drv.ms/u/s!AiuwT3WqasK1qfE6m10Yyib51d0dMg?e=wXhFx4

Note: OneDriveโ€™s preview breaks the SVG, so you have to download it to see it render properly.

Top Labels in this Space