Closed ariya closed 8 years ago
ariya.hi...@gmail.com commented:
Viewport size in PhantomJS follows the same meaning as viewport in WebKit. A web page here is more like a long sheet of paper where the content is capture. A browser however adds the concept of window viewport which is scrollable, hence making the behavior different.
We may need to come up with a different API name, probably something like windowSize.
ja...@fublo.net commented:
We have the same issue with page.render(); We'd like to generate square images which are locked to the top left of the web page, so we set a viewportSize to match the square that we need.
When Phantom returns the "long sheet of paper" we then pass that to some other process that chops it back to being a square. All in all it takes a lot of processor on our server - both from Phantom assembling the large image, and then chopping the large image down.
Would it be possible that page.render() could be extended to accept some arguments regarding the size of the desired output?
ariya.hi...@gmail.com commented:
By "the size of the desired output" do you mean having only specific portion of the page rendered? That can be done already using clipRect.
This issue is more about viewport size which affects placement of various elements in the page.
Metadata Updates
ja...@fublo.net commented:
Clearly I need to experiment more with render() and clipRect(), thanks for the pointer.
mi...@rhiza.com commented:
As Ariya points out you can use clipRect if you simply want to produce an ouput of an exact size. For example clipRect = {top:0, left:0, width:640, height:480} will produce an image exactly 640x480. The issue I'm having is related to content that dynamically scales based upon the window size.
I attempted to set the window size of the browser using javascript (window.resizeTo) but it doesn't appear to work. Does anyone have any pointers of using this within phantomjs?
mi...@rhiza.com commented:
Is there any update on this issue? Is there any plans on adding window size as a core ability in phantomjs?
We have ran into this problem again while taking screenshots of a leaflet generated map. The issue is that the map paints polygons based upon the window size and not the view port size. However, the tiles are rendered in the viewport size. It could be argued that this is a bug in leaflet but its only showing itself in the context of using phantomjs to take screenshots.
joebeuck...@gmail.com commented:
It would be very useful to be able to test at different window sizes. Is there a way to do that currently?
harolds...@gmail.com commented:
I hope that this one gets fixed soon. Making screenshots is now not as nice as it could be. Clipping the image will result in missing the bottom part of the website. If that website uses an footer thats fixed to the bottom of the browser its not captured right now.
ncoo...@gmail.com commented:
bump on this... Modals used in the site that are positioned based on viewport size are currently filling the entire height of the content, and clipRect is cutting them off...
ke...@iveys.org commented:
I'm running into this problem too. An example here: http://jsfiddle.net/kcivey/nf5r8/embedded/result/
In the browser, the circle is drawn around Dupont Circle, but when retrieved with
phantomjs rasterize.js 'http://jsfiddle.net/kcivey/nf5r8/embedded/result/' test.png
The PNG has the circle positioned southeast of where it should be.
ke...@iveys.org commented:
This Leaflet change seems to have fixed my problem, so I guess it wasn't about viewport size: https://github.com/Leaflet/Leaflet/pull/1501
I also just ran into this and was surprised at the behavior, then further surprised that there isn't an existing way to do this still. I know I certainly would prefer the described behavior as opposed to just clipRect
...
I'm having good luck using an iframe and clipRect to get what I want. https://github.com/jbeuckm/Splasher
That's .... interesting. I'll have to play with that...
I'm trying to make a Cucumber scenario which tests if something is scrolled into view, and this is preventing it from working. Everything is visible all the time.
render
renders all pages regardless of viewport size, just like a browser print.
You'll need to use clipRect
to render only a slice of it. See http://phantomjs.org/api/webpage
Thanks. To clarify: I was expecting the screenshot to show only what the viewport would show, so I could judge whether my tests were accurate i.e. My test is saying that the element is visible in the viewport, but does this mean really visible in the viewport, or just visible somewhere on the page? The clipRect thing means I need to determine viewport size, etc, doesn't it? It would be good to be able to pass a parameter to clipRect
so that it would use the viewport dimensions by default. I didn't see reference to anything like that in the docs.
clipRect
{object} This property defines the rectangular area of the web page to be rasterized whenWebPage#render
is invoked. If no clipping rectangle is set,WebPage#render
will process the entire web page.
So, perhaps you want to do something like the following:
function renderCurrentViewport(page, filename) {
var viewportSize = page.viewportSize;
var scrollOffsets = page.evaluate(function() {
return {
x: window.pageXOffset,
y: window.pageYOffset
};
});
page.clipRect = {
top: scrollOffsets.y,
left: scrollOffsets.x,
height: viewportSize.height,
width: viewportSize.width
};
page.render(filename);
}
page.open("http://yourUrlGoesHere.com/", function(status) {
renderCurrentViewport(this, "screenshot.png");
phantom.exit();
});
@JamesMGreene , the problem with your solution is that if we've some things drown on the top of the viewport, they'll get cut off.. since although you clip the viewport, the viewport is still incorrect..
@BlueHotDog: The last-mentioned workaround was strictly for @mattgibson's situation.
I use karma with phantomjs to run tests, and I have a test case in my code that requires me to check and see if the body
can scroll inside of the window
, but due to this I am unable to run the test in PhantomJS.
I couldn't find a workaround for this, so for now I skip this test (and log a warning) if /PhantomJS/.test(navigator.userAgent)
is true.
I'm running into the same problem. I've got tests that rely on checking behaviour when the page is scrolled. I can set the height of the test elements to something ridiculous to make sure there's scrolling room, but this doesn't work in Phantom because it ignores my set viewport height of 1200 and instead sets it to the height of the test element.
There really needs to be a reliable way to set the viewport size regardless of page contents.
Thank you Ariya, This solution has functioned for me:
In hopes that this helps someone else in the future, we mostly solved this by forcing the 'body' element to the page viewportSize:
var width = 1024;
var height = 768;
var webpage = require('webpage');
page = webpage.create();
page.viewportSize = {width: width, height: height};
page.open('http://harness.io', function(status) {
console.log(status);
page.evaluate(function(w, h) {
document.body.style.width = w + "px";
document.body.style.height = h + "px";
}, width, height);
page.clipRect = {top: 0, left: 0, width: width, height: height};
page.render('/tmp/test.png');
phantom.exit();
});
I tried the above (setting viewport width & height), but I still didnt fix the isse.
Still Im observing issue : https://github.com/sindresorhus/pageres/issues/53
This is literally driving me crazy. I resorted to trying an iframe by doing the following:
page.open('about:blank', function (status) {
if (status !== "success") {
console.log('Unable to load url');
phantom.exit();
} else {
page.includeJs(
"https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js",
function() {
evaluateJsWithArgs(
function(url, w, h) {
console.log(url + ', ' + w + ', ' + h);
$('body').append('<iframe frameBorder="0" src="' + url + '" width="'+ w + '" height="' + h + '" id="screen-frame" />');
console.log($('#screen-frame').width() + ', ' + $('#screen-frame').height());
},
url,
page.viewportSize.width,
page.viewportSize.height
);
}
);
}
})
The iframe is reported at the correct width and height in the console, but render()
still renders the iframe at the full height of the document that it contains and I can't get it not to.
Quick update to what I just posted, I realised it might be handling iframes the same way the iOS does and tried replacing with an <object>
and that does the trick.
Edit: Just noticed ralfthewise pointed out the method I mention in this comment. I tried his method on github.com but it cut half the page off, so there might be some middle-point between both of our solutions that might work better.
This doesn't really solve the original issue, but it's something that might be useful. You can actually trick PhantomJS into thinking it has a bigger viewport by manipulating certain attributes for the body and html DOM elements on the page that is being rendered:
// This is the name of "this" file that is being ran.
// This is just a convenience method so the injected JS is in the same file.
var this_file = "render_size.js";
// When I tried running my DOM manipulation code
// with window.onload, it didn't work properly, so I'm using
// jQuery to handle all of that for me. You need to have jquery.min.js in the same
// folder as your script.
var jquery = "jquery.min.js"
// I seemed to have some problems with injecting jQuery
// when it was already loaded in the site, so change this
// depending on if the page you're rendering already loads jQuery or not.
var jquery_needed = false;
var website = "http://github.com"
var filename = "github.png";
// ------------------------------
if ( typeof(phantom) == "undefined" ) {
$(function() {
$("body, html").css({
"width": "2300px",
"max-height": "500px",
"overflow-x": "hidden"
})
})
} else {
var page = require( "webpage" ).create();
page.open( website, function( status ) {
if ( status === "success" ) {
if ( jquery_needed ) {
if (!page.injectJs( jquery ) ) {
console.log( "!!! Failed to inject jQuery" );
phantom.exit();
}
}
if ( page.injectJs( this_file ) ) {
console.log( "Injected. Rendering to " + filename + "..." );
page.render( filename );
} else {
console.log ( "!!! Failed to inject myself." );
}
phantom.exit();
}
});
}
Running that file should output a file named github.png with a screenshot of the github.com homepage at exactly 2300px by 500px.
I've not tested this on a lot of websites, but it seemed to work on the few I tried it on.
@Sonshi You're right, I had tried @ralfthewise's method and it hadn't worked, however once you add set the overflow to hidden then it works, see my working test case at this gist (using a jsfiddle with some fixed elements and a cover background to test those):
https://gist.github.com/DEfusion/4cc10e23f9ed4d38244f
Only issue I can see is for some reason the version doing the body resize results in a slightly larger image (1032x776 for me) for the test file but isn't for other sites I've tried.
@DEfusion thanks for hints, I would never have thought of using the object tag and tore all my hair off!
Hi all, any plans to provide solution for this issue ?
In my case, I'm including external js file into arbitrary page, which renders a widget at the bottom of the page. Currently this widget is rendered at the end of the whole page and not the viewport. To resolve this I'm using an ugly hack, and after the widget is rendered, I'm moving it to the correct position(this hack feels real bad, as this whole process is supposed to test that the widget renders correctly, that's why I've started using phantomjs..)
I also need this to be fixed. I suggest two separate APIs / functions for this:
Those are two different things. And 2. can't be implemented with 1. + clipping, as for example "position: fixed" would not move with the scroll position as well as dynamic sized content (based on the viewport size) wouldn't scale like if you view it with the browser.
+1 for support fixed render viewport size
:+1:
+1
+1 Edit 1: I just realized that PNGs render fine. Only PDF rendering suffers from the wrong printing offsets. Edit 2: FYI: I could fix the PDF problem by setting up a (bigger) pageSize:
width = 1000
height = 600
page.set "viewportSize", width: width, height: height
page.set "clipRect", top: 0, left: 0, width: width, height: height
page.set "paperSize", width: "#{2*width}px", height: "#{2.1*height}px", margin: "0px"
:+1:
:+1: This is an incredibly frustrating issue. To see that ClipRect does not do what a person assumes it does, visit any site with an position:absolute;top:0 navigation bar, and try to render that image half way down the page.
+1 for ability to set rendered page size
Solution mentioned by @MrBeardy works perfectly. The trick is to set a max-height to the body, to force the content to shrink to that height.
I've just pushed #13422 that introduces a new mode
option to the render
API that can be valuated to:
page
: existing behavior that renders the page by capturing its whole content (default value for backward compatibility)viewport
: renders the page by capturing the viewport, hence honoring the viewport size.As @leemes stated it correctly, it is not possible to meet the later generally by using the clipRect
property. In some applications, like decktape that applies to pages having the position
CSS property set to fixed
or absolute
, or relying on the transform
CSS property, or pageres, this is a must-have.
I've just found out that SlimerJS has an onlyViewport
option that does exactly that. That may be valuable to align the proposed API on this.
I've just updated #13422 to align the API on that of SlimerJS onlyViewport
.
+1
@astefanutti are there any plan to open a PR for that, and: @ariya will this get into 2.1?
@nesQuick there is #13422 already opened.
thanks @astefanutti for pointing me to it, and for your work in fixing it :tada:
@nesQuick Until it gets merged, you can get recent binaries containing the fix from here: https://github.com/astefanutti/decktape/tree/gh-pages/downloads.
What's interesting with this issue is that when using VisualCeption, jQuery computes the right coordinates for floating elements but the screenshots size doesn't match. You end up with a screenshot of whatever would be under the floating element. We really need a fix for this viewport size problem.
Still an issue at version 2.1.1. PDF size is right, but its content is zoomed out, only top left area of the desired content is rendered.
mi...@rhiza.com commented:
Disclaimer: This issue was migrated on 2013-03-15 from the project's former issue tracker on Google Code, Issue #619. :star2: 8 people had starred this issue at the time of migration.