Hi all,
Hoping some of the experts here can help me before I lose my last bit of sanity on this problem. It’s been driving me mad for a couple of weeks now.
Basically, I’ve created a tool to take full-page screenshots on a list of URLs on a number of desktop and device configs. Desktop, this all works swimmingly and I get full-page screenshots back with no issue.
The problem comes when trying to do this on mobile devices, due to the pixel ratio of the devices I’m running on.
My method takes the total height and width of the page, as well as the height and width of the viewport, and basically takes images of the viewport, scrolls where necessary, then at the end, stitches all this together in to a single image.
However, with the pixel ratio, which I’m retrieving via JavaScript, even when using a 3.0 pixel ratio on an iPhone 12 for example, the screenshots are never captured properly and don’t scroll correctly and/or don’t capture the entirety of the page. Widthwise it’s ok, it’s always height.
The code for this method is as follows:
public Image GetEntireScreenshot(ArrayList hiddenElementListAfterFirstViewport = null, bool isMobile = false)
{
var pixelRatio = 1;
if (isMobile) pixelRatio = (int) (long) ((IJavaScriptExecutor) driver).ExecuteScript("return window.devicePixelRatio");
// Size of page
var totalWidth = Convert.ToInt32((int)(long)((IJavaScriptExecutor)driver).ExecuteScript("return document.body.offsetWidth") * pixelRatio);
var totalHeight = Convert.ToInt32((int)(long)((IJavaScriptExecutor)driver).ExecuteScript("return document.body.parentNode.scrollHeight") * pixelRatio);
// Size of the viewport
var viewportWidth = Convert.ToInt32((int)(long)((IJavaScriptExecutor)driver).ExecuteScript("return document.body.clientWidth") * pixelRatio);
var viewportHeight = Convert.ToInt32((int)(long)((IJavaScriptExecutor)driver).ExecuteScript("return window.innerHeight") * pixelRatio);
var screenshot = (ITakesScreenshot)driver;
((IJavaScriptExecutor)driver).ExecuteScript("window.scrollTo(0, 0)");
if (hiddenElementListAfterFirstViewport != null)
{
foreach (string hiddenElementXPath in hiddenElementListAfterFirstViewport)
{
IList<IWebElement> elements = driver.FindElements(By.XPath(hiddenElementXPath));
foreach (var element in elements)
{
((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].style.visibility='hidden'", element);
}
}
}
if (totalWidth <= viewportWidth && totalHeight <= viewportHeight) return ScreenshotToImage(screenshot.GetScreenshot());
var rectangles = new List<Rectangle>();
// Loop until the totalHeight is reached
for (var y = 0; y < totalHeight; y += viewportHeight)
{
var newHeight = viewportHeight;
// Fix if the height of the element is too big
if (y + viewportHeight > totalHeight) newHeight = totalHeight - y;
// Loop until the totalWidth is reached
for (var x = 0; x < totalWidth; x += viewportWidth)
{
var newWidth = viewportWidth;
// Fix if the Width of the Element is too big
if (x + viewportWidth > totalWidth) newWidth = totalWidth - x;
// Create and add the Rectangle
rectangles.Add(new Rectangle(x, y, newWidth, newHeight));
}
}
var stitchedImage = new Bitmap(totalWidth, totalHeight);
var previous = Rectangle.Empty;
foreach (var rectangle in rectangles)
{
// Calculate scrolling (if needed)
if (previous != Rectangle.Empty) ((IJavaScriptExecutor) driver).ExecuteScript($"window.scrollBy({rectangle.Right - previous.Right}, {rectangle.Bottom - previous.Bottom})");
// Calculate the source Rectangle
var sourceRectangle = new Rectangle(viewportWidth - rectangle.Width,
viewportHeight - rectangle.Height,
rectangle.Width, rectangle.Height);
// Copy the Image
using (var graphics = Graphics.FromImage(stitchedImage))
{
graphics.DrawImage(ScreenshotToImage(screenshot.GetScreenshot()), rectangle, sourceRectangle, GraphicsUnit.Pixel);
}
previous = rectangle;
}
return stitchedImage;
}
I’m staggered by the fact that I can’t find any posts anywhere online with people who have had the same or similar issues. So either I’m over complicating it and it’s so trivial that it’s never required a post, or this is way harder than I think and people aren’t doing it for good reason
If anyone has any thoughts or ideas, they would be very much appreciated
Thanks!