Puppeteer - How to change orientation of some pages in a document? - javascript

I'm using Puppeteer to create a 30-page long pdf and a few of the pages need to be landscape orientated. How can I specify it only for page x and page y ?

Pseudo Selectors for #page
According to the documentation or CSS spec, you can set up different orientation to some pages using CSS.
#page :pseudo-selector{
size: landscape;
}
The acceptable and working pseudo-selectors (that I tested with puppeteer and google chrome) includes,
:blank
:first
:left
:right
Result:
PS: At the moment of answer, other selectors like :nth-child and page identifies mentioned on the draft does not work on chrome version 73.
Alternative way
The only other way to deal with this is to print pages separately and then merge them later on. You can print specific pages with pageRanges,
page.pdf({pageRanges: '1-5', path: 'first.pdf'})
And use packages like pdf-merge to merge two pdf file.
const PDFMerge = require('pdf-merge');
PDFMerge(['first.pdf', 'second.pdf'], {output: `${__dirname}/3.pdf`})

Well, according to caniuse, you can use the page property with Chrome 85 and up
So you can use #page followed by a "named page name" in combination with the page property to set a different orientation (or any other properties) to any page you want.
example:
#page rotated {
size: landscape;
}
.rotated_class {
page: rotated;
}
Page example, just right click -> print to see it in action
EDIT
Well, after writing this solution I found a major problem with the #page approach. If you try to print the example page I linked to just above, you'll see that the viewport of the rotated pages is limited to the width of the first page if the first page is in portrait (and limited in height if the first page is in landscape).
The solution I ended up using for the page rotation is a bit whack and needs a second library, but it works :
Set a different size on the pages you want to be rotated (here I change it by 0.1 inch which is about 1 pixel in the end) and assign it to your corresponding pages
#page rotated {
size: 8.49in 11in;
}
.rotated_class {
page: rotated;
}
I use pdf-lib after the puppeteer generation to open the pdf and rotate every page smaller than a normal page (in my case I check if the width is smaller than 612 pixels)
const pages = pdf.getPages();
for (let i = 0; i < pages.length; ++i) {
const pageToRotate = pages[i];
if (pageToRotate.getWidth() < 612) {
pageToRotate.setRotation(degrees(-90));
}
}
pdfBuffer = Buffer.from(await pdf.save());
the Page size is the only info I found that could be "transferred" from the css to the pdf-lib to flag any page to be rotated, hence why I'm using that solution.
Hopefully the "viewport bug" will be fixed in the future and we'll be able to use the #page landscape/portrait property

We need to set 'landscape' property to 'true' in options.
var options = {
...
landscape: true
}
page.pdf(options);

Related

How to prevent partially loaded images from displaying?

If you have, let's say, 3MB image in img tag, it will take a few seconds to load. When the image is loading, browser is sort of "printing" it - it shows the top part first, then middle and then bottom. How do I prevent this from happening?
I'd rather have the image hidden and after second or two shown - when it is fully loaded.
One way would be to give them a class that gives them opacity: 0 so they don't show:
<img src="/path/to/image" class="loading">
And in CSS:
.loading {
opacity: 0;
}
In head, we override that if JavaScript is disabled (so we're not unfriendly to non-JavaScript visitors):
<noscript>
<style>
.loading {
opacity: 1;
}
</style>
</noscript>
...and then in code at the bottom of your page, find all your images and remove the class when they've loaded, and...(see comments):
(function() {
// Get an array of the images
var images = Array.prototype.slice.call(document.querySelectorAll("img.loading"));
// Hook their load and error events, even though they may have already fired
images.forEach(function(image) {
image.addEventListener("load", imageDone.bind(null, image));
image.addEventListener("error", imageDone.bind(null, image)); // Could handle errors differently
});
// Check to see if any images are already complete
checkImages();
function imageDone(img) {
img.classList.loading("remove");
images = images.filter(function(entry) { entry != img });
}
function checkImages() {
images.forEach(function(image) {
if (image.complete) {
imageDone(image);
}
});
if (images.length) {
// Check back in a second
setTimeout(checkImages, 1000);
}
}
})();
That's a belt-and-braces approach. It proactively checks to see if images have finished loading, and also reactively handles the load and error event of images. In theory, we shouldn't need the setTimeout, and you might do testing without it, but...
Notice how once an image is complete, we remove the class so it's visible.
Old school:
To avoid the partial display of an image as it renders, save your large images as progressive, rather than baseline jpgs.
a progressive jpg renders as a series of scans of increasing quality
a baseline jpg renders top to bottom (what you described as “printing”).
The progressive option is considered more user friendly than both the sudden appearance of the image or the slow top to bottom rendering you dislike. The progressive file variant can even be smaller than its baseline counterpart.
For more about this read: The Return of the Progressive JPEG.
I think everyone here gave you some good answers and I just want to add in. 3MB is fairly big for a web image. Don't use something that large for an image being used for logo or layout. That's a larger amount of pixel data that you should only stick with if you are loading something that is a nice, large scale real-life image you want to preserve the quality to (or providing a download to a high-quality graphic of something). Besides the above, if you do a Google search, you find tons of solutions for loading images. Something nice I would use for larger images is a jQuery/ajax solution.

Skrollr Image Flicker. Firefox preload issue

Im in the process of developing a 'flipbook-style' animation using Skrollr by triggering background image changes when the user scrolls to indicated positions on the page. The issue i'm having is that in browser the image changes are delayed, creating what can only be defined as a 'flicker' of white between the frames.
<div class="section" style="background: url('frame1.png')"
data-560-top="background-image:!url('frame1.png');"
data-440-top="background-image:!url('frame2.png');">
The HTML is simple; it basically states that at 560 pixels from the top of the div (in relation to the browser window), the background should be at frame 1, then as the user scrolls closer to the div (440 pixels from the top of the div) the background image changes to frame 2. I plan to use up to around 20 frames and the images are quite large.
I have created a JSBin here which includes a very simplified sample with images from placehold.it. This includes the Skrollr script and an example layout of a section of my project. The key difference being that the images in my project are of much larger scale.
(function($) {
var cache = [];
// Arguments are image paths relative to the current page.
$.preLoadImages = function() {
var args_len = arguments.length;
for (var i = args_len; i--;) {
var cacheImage = document.createElement('img');
cacheImage.src = arguments[i];
cache.push(cacheImage);
}
};
})(jQuery);
jQuery.preLoadImages(
'http://www.placehold.it/300x200.png',
'http://www.placehold.it/300x200.png'
);
The above snippet seems to be working on Chrome, however the flicker issue remains in Firefox. Based on research, firefox handles cached images differently from Chrome? (e.g Where an image is not considered needed by firefox at a given time, it is trashed?)
I would like to know how I could possibly force all browsers to preload the images efficiently, to potentially avoid the background image flicker upon change. I am still quite new to Javascript/JQuery.
I hope I have provided a clear explanation. All assistance appreciated.
Dan
You can preload images using CSS only, no need for JS. Check out this article for more info. Another interesting way to do it is in the comment section of the article. Basically you assign the background image to a pseudo-element so that is is cached and ready to be used whenever. See this code for an example:
#something:before {
content: url("./img.jpg");
width:0;
height:0;
visibility:hidden;
}

Selenium To disable Position fixed Content To remove Duplicate data while taking Whole Webpage Screen shot

I am able to take screen shots of entire page by scrolling to window.innerheight but while combining facing proble of duplicate(position fixed Header/content) in all the pages.
Is their any solution to resolve this problem. please post your answer clearly.
FireFox open Developer Toolbar and hit shift + f2 then type this command.
screenshot d:\yash.png --fullpage
Then it reduces the clientWidth and takes screenshot.
Selenium-WebDriver FireFox-ScreenShot
Using selenium with IE(uncheck checkboxes of security & privacy and reduce Internet, Accept all cookies to zero).
use ieCapabilities.setCapability("ignoreZoomSetting", true);
then it takes screenshot of entire page by reducing the client width with out any Image pixel problem. but the Screen shot on this Site:http://help.dottoro.com/ljlumkqh.php is not as expected.
But in Chrome it takes only visible part of web page. In order to take
whole page screen shot we need to scroll to clientHeight and combine all
screen shots.
Remove Sticky Data
Window.getComputedStyle() method and its supported Browsers.
currentStyle object for IE before version 9
var elems = window.document.getElementsByTagName('*');
for(i = 0; i < elems.length; i++) {
if (window.getComputedStyle) {
var elemStyle = window.getComputedStyle(elems[i], null);
if(elemStyle.getPropertyValue('position') == 'fixed' && elems[i].innerHTML.length != 0 ){
elems[i].parentNode.removeChild(elems[i]);
}
alert (Works For all Browsers);
}else {
var elemStyle = elems[i].currentStyle;
if(elemStyle.position == 'fixed' && elems[i].childNodes.length != 0 ){
elems[i].parentNode.removeChild(elems[i]);
}
alert (Works for IE browsers version below 9);
}
}
First point is you don't need to scroll for taking full screen screen shot in selenium.
Approach for removing duplicate data:
Take complete window screen shot & don't save it to any file.
Observe the duplicate data (Header object)
Create bufferedImage object for the screen shot file using ImageIO.read() method.
Get the x & y co-ordinates for the duplicate part (Header) using .getLocation().getX()/getY() methods.
from whole screen shot remove the duplicate data by using .getSubimage() method.
then save the image to any file using ImageIO.write() method.
I think there should be some code to support your question.
But, assuming that you are scrolling your page explicitly to take the screenshot.
Solution/Suggestion/Tip: Selenium by default scrolls the page to take the screenshot of the complete page.
Code to take the above screenshot:
WebDriver driver = new FirefoxDriver();
driver.get("http://www.w3schools.com/html/default.asp");
File myScreenShot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
FileUtils.copyFile(myScreenShot, new File("D:\\ff.png"));
Refer this for more information on taking a screenshot.

How to run vbscript code at certain browser width

I'm turning a clients website into a responsive site and they have lots of vbscript in the content of their home page. At mobile widths they've stripped out a lot of content which means there's lots of code that's being executed but not displayed thanks to display:none
Is there a way to run vbscript code when you hit a minimum width of 768px?
I thought about using javascript to get the screen width and store it as a cookie and use vbscript to get the cookie to obtain the screen width:
<SCRIPT LANGUAGE="javascript">
var width = screen.width;
document.cookie = 'YourDomain=ScreenWidthB='+width;
</SCRIPT>
<%Dim ScreenWidth%>
<%ScreenWidth=request.cookies("YourDomain")("ScreenWidthB")%>
but I feel there may be a better solution out there. Also the code above gives me the width of my monitor I believe, not the width of the browser
This isn't something you would do with any server side language.
You can either use Bootstrap Grid System for this, which has a built-in grid system to handle responsive sizing.
or you can simply use CSS to define your styles for elements with-in a certain viewport size, using the CSS #media tag:
Your CSS would look like this example:
div {width:100px;}
#media (min-width:768px) {
div { width: 50px; }
}
What this does is makes all div's at 100px width, but when the browser is 768px or larger it changes the div sizing to 50px, as defined with-in the #media tag.
Therefore, you can use VBScript to generate the CSS script in the page, without having to write any javascript code. But Bootstrap may be your best bet to help build a responsive design easily/seamlessly. You may want to check it out.
EDIT: Since OP has clarified not to even load the content
You can make a cookie in javascript, and read it in your VBScript to check the viewport.
You can use jQuery for this:
$(window).resize(function(e){
var w = $(this).width();
if(w>768) document.cookie = "viewport=768;";
else document.cookie = "viewport=;";
});
This will bind an event listener on any time the user resizes the window, to check it's size, and if above 768px, it will write the cookie or empty if not.
Then check for the viewport cookie using Request.Cookies("viewport")
Or better yet since you're concerned about performance, you can use Ajax to build your page when a certain viewport size is hit.
Again, you can use jQuery for this and bind to the window resize event.
contentloaded = false;
$(window).resize(function(e){
var w = $(this).width();
if(w>768 && !contentloaded) {
$.get(url,function(data){
$("div").html(data);
contentloaded = true;
});
}
});
I would use ajax to do this, since I'd want to show the content without the user having to refresh the screen as you would have to by using the cookie solution.

jQuery data() and resize() to change src attribute

I'm working on a script uses jQuery's data() function and HTML5 data attributes to dynamically switch an image's src attribute based on a media query. The idea behind this is to serve a lo-fi image by default (optimized for mobile), and serve a hi-fi image for larger screens. (This is not necessarily limited to images.) The script works 100% in Chrome/Opera/Safari/iOS but not completely in FF and IE.
<img src="ex1_lo-res.png" data-websrc="ex2_hi-res.png" alt="example">
<img src="ex2_lo-res.png" data-websrc="ex2_hi-res.png" alt="example">
A live example of this in use is responsetheme.com, where above 480px wide, the image should be pink, and below 480px wide, it should be yellow. I know that both data() and Modernizr.mq are supported in FF and IE—I tested them without the resize() function. So I think the issue has something to do with the trigger or the each() or resize() function. Any idea on what I'm missing?
jQuery(document).ready(function($) {
/* get elements that have a data-websrc attribute */
$websrc = $('[data-websrc]');
$websrc.each(function() {
/*
Set data-osrc equal to element's original src value.
This allows us the ability to access the original src
(even after we replace the attribute).
*/
var $this = $(this);
$this.data('osrc', $this.attr('src'));
});
$(window).resize(function() {
/*
Check breakpoint.
(Modernizr.mq checks the media query and returns boolean.)
*/
airsrcWEB = Modernizr.mq('screen and (min-width:480px)');
/*
Replace src with data-websrc (if above breakpoint).
Otherwise fallback to data-osrc (original src).
*/
$websrc.each(function() {
var $this = $(this);
var src = ( window.airsrcWEB ) ? $this.data('websrc') : $this.data('osrc');
$this.attr('src', src);
});
}).resize(); // trigger resize handlers
});
Also, I wasn't sure as to whether I have the functions in the most efficient way as far as which was inside which, so I'd also like to hear any tips for speeding this up. =)
Update 1: I also tried with the ternary like this and still the same issue:
var src = ( airsrcWEB ) ? $this.data('websrc') : $this.data('osrc');
Update 2: I figured out the issue with FF. Apparently a FF6 window won't resize below about 556px wide. I tested the script with a breakpoint above that and the switch worked. (Even the examples on mediaqueri.es won't shrink below 556px wide in FF6.)
You already found out that FF has a minimal window size. I don't know the exact value, but I believe it's a percentage of the initially available viewport width.
This is a restriction of XUL, the language in which FF was written.
The question is, is this really a problem in your case? The only persons that fiddle around with the window size are (front-end) webdevelopers. Normal users just load a page and stick with it, so basically I'm thinking that you might not really need to attach this functionality to a resize event.
Furthermore, this is only an issue when users are shrinking the window, not when expanding. If they already loaded the hi-res image, why bother loading the low-res aswell?

Categories

Resources