I'm just starting to play around with creating some web apps (after a long history of Delphi VCL), and have come across an issue when trying to save image files on IOS devices. Below is some code that loads in a photo on Button1 click (from a selection of images with resolutions 500x500 pixels up to 7000x7000 pixels), draws a red line through the middle of the selected image, then saves it on Button2 click. On windows and linux desktops, and android mobile devices, all images up to 7000x7000 save ok. But on my two iPhones (one brand new) and iPad there seems to be a size limit, with anything greater than about 1000x1000 not being saved (and with no indication of an error - though for the 7000x7000 image the IOS download prompt does show, but the file size is 0kb). I suspect being brand new to this game I'm missing something fundamental. Any thoughts?
Cheers, Stephen.
procedure TForm1.WebButton1Click(Sender: TObject);
begin
//Load the image
webimagecontrol1. URL := WebComboBox1.text;
//a file of either 500x500, 1000x1000, 2000x2000, 3000x3000, 5000x5000,
//7000x7000 pixels
end;
procedure TForm1.WebImageControl1Loaded(Sender: TObject);
begin
// Edit the image
Graphic.LoadFromURL(webimagecontrol1.url);
BM.SetSize(Graphic.Width, Graphic.Height);
BM.Canvas.Draw(0,0,Graphic);
BM.Canvas.pen.color:=clred;
BM.Canvas.pen.width:=5;
BM.Canvas.Moveto(0,0);
BM.Canvas.Lineto(BM.Width,BM.Height);
end;
procedure TForm1.WebButton2Click(Sender: TObject);
var el: TJSHTMLElement;
begin
// Save the file
el:= TJSHTMLElement(document.createElement('A'));
el.setAttribute('href', BM.GetBase64Image);
el.setAttribute('download', 'Edited.png');
el.style.setProperty('display', 'none');
document.body.appendChild(el);
el.click;
document.body.removeChild(el);
end;
I think in the direction of async behavior of DOM. You do appendChild() then click and then immediately removeChild(). I'm wondering if the download could fully execute before you call removeChild(). I suggest to test by commenting this last call to see if it helps. If so, try to remove this element only after a certain timeout.
Thanks for the suggestion. I tried commenting out the document.body.removeChild line but the IOS problems persist. In testing a bit more I did find that Safari could accept a download much larger than Chrome or Edge (the biggest of my files Safari could save was 3000x3000, whereas Chrome and Edge balked at 1000x1000). I also made sure to re-start all my devices before testing too.
Maybe it is related to time required before the image is ready for download after setting el.setAttribute('href', BM.GetBase64Image) and appending it to the DOM.
You might want to try delaying the el.click call to see if this helps.
That sounded promising but alas it didn't work. I prefaced the onclick event with [async] and edited the event code to that below. After loading the 7000x7000 file, then pressing the save button, as expected nothing happened for 10 seconds; then the download prompt appeared, but again with a 0kb file (or for the other file sizes > 500x500, no prompt appearing at all). So exactly the same behavior as before, just delayed 10 seconds.
procedure TForm1.WebButton2Click(Sender: TObject);
var el: TJSHTMLElement;
begin
// Save the file
el:= TJSHTMLElement(document.createElement('A'));
el.setAttribute('href', BM.GetBase64Image);
el.setAttribute('download', 'Edited.png');
el.style.setProperty('display', 'none');
document.body.appendChild(el);
Await(boolean, AsyncSleep(10000));
el.click;
// document.body.removeChild(el);
end;
Thanks for taking a look, I'll keep experimenting.
I did do a bit more digging and it seems to be an issue with image storage in the tBitmap. On IOS BM.GetBase64Image for the large files returns a string length of just 6 (containing just 'data:,'), whereas on desktop and android the full image string is returned.
The exact maximum size of a <canvas> element depends on the browser and environment. While in most cases the maximum dimensions exceed 10,000 x 10,000 pixels, notably iOS devices limit the canvas size to only 4,096 x 4,096 pixels.
Ah yes, that looks very relevant indeed. Perplexing limitation given how well the code works on android, but there you go. Thanks again for following up.