How can I get the whole image of a canvas object?
Hello all,
In the web app I am testing, there is a <canvas/> object that displays as image only. The app only displays part of the image because this image is almost always way too big to display. The user can scroll in all directions to view the other regions of the image.
I want to use TestComplete to save the entire image so that I can make image-level comparisons.
Using pageObj.PagePicture(), as expected, is not the solution because it only shows the page of the app.
Calling canvasObj.Picture(0,0,-1,-1,false) returns an image of the correct size, but of what is currently there on the screen (Including black areas for the part of the image that is outside the PC monitor).
How can I get the whole image of a canvas object at once?
Notes / More info:
1) If possible, I would like to avoid scrolling in the automation and shooting multiple images that I then have to 'glue' together. For similar reasons, I would like to avoid using region checkpoints for image parts if possible.
2) Development will give me a way to read the data I want the application to show in the image from the page's javascript via get methods. However, this is an additional nicety. I really need to check the displayed image, though. (!) It is not enough that the application sends the correct data to the client: The application must also display this data appropriately on the image. Testing the image directly is an important requirement of the client. Therefore I need to read the whole image of the canvas.
Thank you very much for your time!
First of all: Thanks to rraghvani for the hints that put me on the right track.
Here is commented code with solution:
// File names const fileNameRoot = "C:\\Temp\\Canvas"; const fileNameHtml = fileNameRoot + ".html"; const fileNamePng = fileNameRoot + ".png"; const fileNameJpeg = fileNameRoot + ".jpeg"; const fileNameWebp = fileNameRoot + ".webp"; // get the page objekt - adapt parameters as needed... var pageObj = Sys.Browser(Project.Variables.Browser).Page(Project.Variables.HomePage + "*"); // get the canvas object - we use here a xpath expression. var xPath = "//canvas[contains(@class, 'shapes')]"; // adapt as needed var canvasObj = pageObj.FindChildByXPath(xPath); // following fails to get hidden canvas picture partitions // var pictureCanvas = canvasObj.Picture(0,0,-1,-1,false); ///////// // Get canvas picture using canva native method toDataURL(). See: // https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL // The return value of toDataURL() is ready to embed in a html page and // contains 'data:image/png;base64' image at the begining var imagePNGAsBase64WithPrefix = canvasObj.toDataURL("image/png", 1.0); // 1.0 not needed // JPEG Contains 'data:image/jpeg;base64,' and so on. var imageJPGAsBase64WithPrefix = canvasObj.toDataURL("image/jpeg", 1.0); // 1.0 == no compression var imageWEBPsBase64WithPrefix = canvasObj.toDataURL("image/webp", 1.0); // we need to dropp prefixes for image file creation const prefixHTML_imagePng = "data:image/png;base64,"; const prefixHTML_imageJpg = "data:image/jpeg;base64,"; const prefixHTML_imageWbp = "data:image/webp;base64,"; // dropp the prefixes var imagePNGAsBase64NoPrefix = aqString.Replace(imagePNGAsBase64WithPrefix, prefixHTML_imagePng, "", false); var imageJPGAsBase64NoPrefix = aqString.Replace(imageJPGAsBase64WithPrefix, prefixHTML_imageJpg, "", false); var imageWEBPsBase64NoPrefix = aqString.Replace(imageWEBPsBase64WithPrefix, prefixHTML_imageWbp, "", false); // Create html file showing the images var oFile = aqFile.OpenTextFile(fileNameHtml, aqFile.faWrite, aqFile.ctANSI, true); oFile.Write("<!DOCTYPE html><html><head><title>Canvas picture as PNG, JPEG and WebPage-Image</title></head><body>"); oFile.Write("<img style='width:100%; height:100%;' src='" + imagePNGAsBase64WithPrefix + "'/>"); oFile.Write("<img style='width:100%; height:100%;' src='" + imageJPGAsBase64WithPrefix + "'/>"); oFile.Write("<img style='width:100%; height:100%;' src='" + imageWEBPsBase64WithPrefix + "'/>"); oFile.Write("</body></html>"); oFile.Close(); // Attach the html file to log Log.Link(fileNameHtml, "HTML-File containing image from canvas only."); ////////////////////////////// // Image file creation // Create png-File directly var ps = dotNET.System_Management_Automation.PowerShell.Create(); var psCmd = "[IO.File]::WriteAllBytes('" + fileNamePng + "',[Convert]::FromBase64String('" + imagePNGAsBase64NoPrefix + "'))"; // log the actual command for fun. Log.Message("Power-Shell command", psCmd); ps.AddScript(psCmd); // 'type' it to PowerShell var results = ps.Invoke(); // Run. // Attach picture file to Log Log.File(fileNamePng, "Canvas picture as PNG."); // Create JPEG-File directly psCmd = "[IO.File]::WriteAllBytes('" + fileNameJpeg + "',[Convert]::FromBase64String('" + imageJPGAsBase64NoPrefix + "'))"; ps.AddScript(psCmd); results = ps.Invoke(); Log.File(fileNameJpeg, "Canvas picture as JPEG"); // Create WEBP-File directly psCmd = "[IO.File]::WriteAllBytes('" + fileNameWebp + "',[Convert]::FromBase64String('" + imageWEBPsBase64NoPrefix + "'))"; ps.AddScript(psCmd); results = ps.Invoke(); Log.File(fileNameWebp, "Canvas picture as WEBP"); /////////////////////////////// // Compare with file from Disk var pictActual = Utils.Picture; pictActual.LoadFromFile(fileNameJpeg); // pictExpected is the name of expected result file (created by first run, for example). var pictExpected = Utils.Picture; pictExpected.LoadFromFile(fileNameRoot + "_Expected.jpeg"); // the comparisson var bRes = pictActual.Compare(pictExpected) if (bRes) { Log.Checkpoint("Canvas picture as expected!"); } else { Log.Error("Canvas picture don't meet expected result"); }
A last remark on the files created: Perhaps we may use toBlob() method instead of toDataUrl() to avoid conversions. (I haven't try it yet.)
Good luck!