Skip to content

Tutorial: Saving WebCanvas Images to Disk

The WebCanvas control is used for drawing graphics in web apps. It takes advantage of the HTML5 Canvas making it fast and powerful. Sometimes is can be useful to be able to save the graphics drawn in the WebCanvas to an image file, but unfortunately there is no built-in Xojo method to do this.

However, by using a little JavaScript you can easily add this capability to Xojo.

In order to follow this tutorial, create a new Web project and add a new WebCanvas control from the Library to the default Web Page added to the project: WebPage1. With the WebCanvas selected in the Navigator, change its name to cCanvas using the Inspector and add the Paint Event Handler to it using the Insert > Event Handler option from the menu. Use the following code to draw a black colored rectangle:

g.FillRect 0, 0, g.Width, g.Height

Obviously, you can draw anything you want in the WebCanvas or even paint a picture on it. The achieved result will be the same. For example, we can draw a picture added to our project (with the filename “landscape”), using this line of code in the Paint event of our cCanvas instance:

g.DrawPicture landscape, 0, 0, g.Width, g.Height

Retrieving the WebCanvas image data

The first interesting step is retrieving the underlying image represented by the WebCanvas and that is something we can get using a fragment of JavaScript code executed from the Xojo side. This is the process we will execute from the Action Event Handler of a WebButton added to the WebPage1 from the Library. In fact, this will be the button in charge of calling the process to save the image as a picture file on the user disk.

Once the WebButton has been added to the WebPage from the Library, put the following code in its Action Event Handler:

Dim js() As String
js.Append "var canvas = document.getElementById('" + cCanvas.ControlID + "_canvas');" // We get the item reference based on its ID
js.Append "var img = canvas.toDataURL(""image/png;base64;"");" // We access to the image data, encoded as a base64 string
js.Append "document.location.hash=img" // We assign that data to the document Hash property, so we can retrieve it from the Xojo side

Dim execute As String = Join(js,"")

ExecuteJavaScript execute // Finally, we execute the JavaScript code

As you can see, the first line of the JavaScript code is in charge of retrieving the cCanvas reference from the DOM (document object model) of the Web Page. For that, we use the ControlID property on our WebCanvas instance, adding the “canvas” appended by Xojo to all these kind of objects when generating the source code for the page.

Once we get the reference to our Canvas instance, the following line of code retrieves the picture content itself and assigns it to the img variable. The trick here is that we retrieve that information in its URL format and, thus, encoded as Base64 data that we can assign to other document properties without losing its original meaning.

Then, we assign the data to the document Hash property. This is the property that allows us to jump to other page sections previously tagged. However, we will use this property as a temporary storage container for passing the data between the HTML/JavaScript side of the web page and the Xojo side of our code. Why we do this? Well, because we can add the HashTagChanged Event Handler to the Session object so it will fire every time we change the value of the Hash property and we will be able to retrieve the data from that property.

Retrieving the image, from the Xojo side

So, the next step is adding the HashTagChanged event to the Session object of our example project, writing the following snippet of code in the associated Code Editor:

Dim data As String = DecodeBase64(Hashtag.NthField(",",2) ) // We retrieve the information saved in the Hash, but just the relevant data for the image and not the URL part.
wp = New WebPicture(data,"My_Picture.png") // We create a new WebPicture instance using the previously retrieved image data in the constructor, and giving a default file name for the image
wp.ForceDownload = True // We want to force the file download instead of showing it
ShowURL wp.URL // and finally we start the file download process.

Hashtag = "" // Let's empty the hash property, just to make sure that the event will fire even if the WebCanvas doesn't change its contents

As you can see, in the first line of code we retrieve the image data. We do that using the NthField command because we are only interested in the image data and not in the URL information that precedes it. In addition, we decode the previously encoded data as Base64, so we really get the bytes of the image in PNG format.

Once we get the picture data, we can use any of the available constructors for the WebPicture class; in this case the one that lets us create a new image from the passed data. In addition, we use this class because it is a subclass of WebFile, and that means that it is simpler to force the download as an image file instead of showing its contents in a new web page.

In fact, this is what we achieve setting the ForceDownload property to True, so when in the following line we use ShowURL we will be forcing the download of the file instead of displaying the image as a new page in the browser. Goal achieved!

Download persistence

You probably noted that the wp variable is not declared in the previous fragment of code. This is because we need to make sure that the object it points to is in scope during the file downloading process and won’t get destroyed once we exit the Event Handler (we would want a message error from the web browser in that case). The solution is pretty easy – we only need to add a new property to our Session object, using the following values:

  • Name: wp
  • Type: WebPicture
  • Scope: Private

Now, every time you run the app web and click the button, you’ll see how the web browser downloads the WebCanvas image as a PNG file to disk.

To summarize, in this tutorial we have seen a way to “communicate” between the data generated executing JavaScript code in our web app so we can retrieve and use it from the Xojo side of our Web App; in this case applied to the retrieval and saving of the underlying picture represented by a WebCanvas object.

*Watch a video of this Tutorial in Spanish

*Read this Tutorial in Spanish

Javier Rodri­guez has been the Xojo Spanish Evangelist since 2008, he’s also a Developer, Consultant and Trainer who has be using Xojo since 1998. He manages AprendeXojo.com and is the developer behind the GuancheMOS plug-in for Xojo Developers, Markdown Parser for Xojo, HTMLColorizer for Xojo and the Snippery app, among others