Wait for iframes to load in Puppeteer PDF generation - ruby-on-rails

I am trying to load some iframes during the generation of a PDF using Puppeteer for a Ruby on Rails project. I am using the puppeteer-ruby gem for this.
While the PDF gets generated successfully, the iframes are not loaded and therefore do not appear the output file.
The code I am using for the PDF generation is rather basic:
Puppeteer.launch(headless: true) do |browser|
page = browser.new_page
page.content = html(posts)
page.pdf(
format: 'A4',
margin: {
top: '1cm',
bottom: '1cm',
left: '1cm',
right: '1cm'
},
print_background: true,
display_header_footer: true,
header_template: '',
footer_template: footer_pagination
)
end
In turn, each post might have something like this:
<iframe
src="<%= some_path(some_object) %>"
loading='lazy'
class='w-100 border-0'>
</iframe>
This works properly when visited in a browser but not when rendered by Puppeteer.
How can I tell Puppeteer to wait for those iframes to load before generating the PDF? Thanks in advance!

'framenavigated' event will be fired after the content of iframe loaded completely. Probably you can observe the event with Page#on or Page#once.
https://github.com/puppeteer/puppeteer/issues/1361#issuecomment-343748051
Puppeteer creates PDF before all iframes have loaded

Related

Foreign characters displaying delayed on cordova ios application

We are using Webfont Loader to load google fonts. And inside the active callback of WebFont.load we load our main script and bootstrap angular.js application.
<script type="text/javascript">
WebFontConfig = {
google: {
families: ['Open Sans:400,700,700i,400i:latin-ext']
},
active: function() {
var mainScript = document.createElement('script');
mainScript.src = "/js/main.js";
mainScript.onload = function() {
angular.bootstrap(document, ['cob']);
}
document.body.appendChild(mainScript);
}
};
WebFont.load(WebFontConfig);
</script>
To make browser use default fonts until the google font file loaded, we set fonts for .wf-active class
html.wf-active {
font-family: 'Open Sans', sans-serif;
}
Inside angular run, we switch apploaded and clear screen from loaders and show the application.
angular.module('mymodule')
.run([function() {
$rootScope.appLoaded = true;
}
]);
html
<body>
<div class="app-loader" ng-hide="appLoaded">loading...</div>
<div class="page-container" ng-if="appLoaded">
application loaded. <button>GİRİŞ</button>
</div>
</body>
We see "loading..." until the font files loaded, then application bootstraps successfully.
But the button text is rendered as "G R " for a couple of seconds, after a while 'İ' and 'Ş' characters are loaded into screen and we see GİRİŞ on button.
I am sure the font file is loaded before application bootstraps, because the font doesn't change after the application is loaded.
May be irrelevant but, I got some image files on the page and foreign characters are rendered with the image files. So there is some kind of a wierd screen render latency.
We only have this issue on iOS application, I have tested it on iPhone 6 and 6s, and on xcode simulators.
This works as intended on browsers (safari, chrome and firefox) and on android application we create with the same cordova project.
After trying various preload methods, just as I was considering to give it up, I came accross to a fact here
most browsers download fonts when they're used in a page rather than
when they're declared in CSS.
It seems like latin-ext subset of the font is delayed until it is used inside the page.
I have solved the issue by putting a dummy character (İ) that will make it download latin-ext subset on load screen

Adding Jquery UI to XSLTForms

I was trying to add JQuery UI functionality and styling but I'm having problems with the CSS file. I use to attach a lot of libraries to XSLTForms files, but I really can't make it load. I already put the and tags inside the CSS file, as I use to do with other CSS files, but with no success.
Any idea what could be wrong? Functionality is OK, because Y can make it appear a dialog, but with no style. I copied the source code in a new file and change it to html (replacing the XForms tahgs with HTML ones) and everything worked ok. So I think there is something with XSLTForms.
<xf:trigger id="showDialog">
<xf:label>Show dialog!</xf:label>
<xf:load ev:event="DOMActivate" resource="javascript:
var dialog = document.createElement('div');
dialog.innerHTML = 'Your modal dialog';
$(dialog).dialog({
autoOpen: true,
modal: true,
title: 'Attention',
open: function(){
$('.ui-widget-overlay').bind('click',function(){
$(dialog).dialog('close');
})
}
});
"/>
</xf:trigger>
I also unsuccessfully tryed to load this way:
<style type="text/css">
#import url('res/jquery-ui.css');
</style>
XSLTForms requires CSS files to be XML Well-formed.
This option can be disabled including this processing instruction in your XForms:
<?css-conversion no?>
Take a look to this XSLTForms Wikibook article
This particular file had something than others don't: CSS comments. Just remove comments or make it as in the XML way (<!-- A comment! -->).
But for some reason, any image referenced is not loaded...

JW-Player and Rails 3.2

I'm trying to use JW-Player in my application. Researching the issue a bit, there seems to be several abandoned efforts to produce a gem, and the latest is undocumented. So, here's how I'm going about it:
I downloaded the JW-Player version 6, unzipped and copied the files in my /app/assets/javascripts directory as follows:
app/assets/javascripts/jwplayer/jwplayer.js
app/assets/javascripts/jwplayer.html5.js
app/assets/javascripts/jwplayer.flash.swf
In my app/views/layouts/application.html.erb, I have the following:
<head>
<%= javascript_include_tag "/assets/javascripts/jwplayer/" %>
</head>
and in app/views/pages/about.html.erb, I have the following:
<%= jw_player("http://xxxxx/video.mp4",
:width => 200, :height => 110) %>
Here's what happens when I click on the About page link:
Showing xxxxxxxx/app/views/pages/about.html.erb where line #10 raised:
undefined method `jw_player' for #<#<Class:0x007fe77e37c018>:0x007fe780c1f678>
First time user of JW-Player.
When implementing JWPlayer 6.6, we stood before the choice of putting the jwplayer.flash.swf file into the public folder, to make the flash mode work, but it seemed very messy to have the files separated like that. What I did in the end to make it work both on development and production was:
Put all 3 files to vendor/assets/javascripts/jwplayer
Rename jwplayer.js to jwplayer.js.erb
Inside jwplayer.js.erb, update the flash file path config like this (the 1st line with the html5 file path config is just for reference)
j={type:"html5",src:e.base+"jwplayer.html5.js"},
b={type:"flash",src:"<%= asset_path('jwplayer/jwplayer.flash.swf') %>"};
(note that the "e.base+" before the path was removed for the flash file path - that's the trick that allowed working relative paths in the development environemtn)
In my understanding, the JWPlayer license allows modifications like this:
"Adaptations
Publisher shall be permitted to make Adaptations reasonably necessary for the purpose of exercising its rights under these Terms of Service, such as Adaptations to integrate the Products into Publisher’s websites or other properties. All Adaptations created by Publisher are strictly for its own Use and Publisher is prohibited from Distributing any Adaptation it creates. The Company reserves the right to prohibit the Use of any Adaptation in its sole discretion."
I have just finished working on a gem started by choix and improved by mattherick called jwplayer-rails that probably worked in older version of rails. It wasn't working with the assets pipeline but mattherick did a great job at fixing that up and I went on to update JWPlayer to the newest version.
You can see the repository here.
The following instructions are right out of the repo above.
To add this gem to your rails app just add this line to your application's Gemfile:
gem 'jwplayer-rails', :git => 'git://github.com/dutgriff/jwplayer-rails.git'
To use it first include assets on the page
<%= jwplayer_assets %>
Then place a div with JW Player
<%= jwplayer %>
You can pass options to jwplayer helper to customize it:
<%= jwplayer({width: 500, height: 200}) %>
More information for customization could be found here.
It works great for me so far but if you find an issue let me know on here or github.
I've found a solution to this.
The main issue you need to work-around is that jwplayer.js wants to fetch jwplayer.flash.swf and jwplayer.html5.js based on the path of jwplayer.js.
You can see that in Chrome Developer Toolbar for jwplayer.js (with pretty print):
(h.embed.config = function(b) {
var e = {fallback: !0,height: 270,primary: "html5",width: 480,base: b.base ? b.base : j.getScriptPath("jwplayer.js"),aspectratio: ""};
b = j.extend(e, h.defaults, b);
var e = {type: "html5",src: b.base + "jwplayer.html5.js"},
g = {type: "flash",src: b.base + "jwplayer.flash.swf"};
You can use that base property as an undocumented api to tell jwplayer where the jwplayer.flash.swf and jwplayer.html5.js can be found.
Example:
jwplayer("player-id").setup({
width: 640,
height: 480,
file: "www.w3schools.com/html/movie.mp4",
base: "http://cloudfront.net/assets/vendor/jwplayer/"
};
Then it will look for http://cloudfront.net/assets/vendor/jwplayer/jwplayer.flash.swf. Note: jwplayer has no notion of the asset pipeline fingerprint filenames, so make sure you sync both the file with md5 and without.
This worked for me:
Place jwplayer folder in public (Downloaded from longtail video)
Include it like an external script, without using asset pipeline (HAML).
%script{:src => '/jwplayer/jwplayer.js'}
In your video partial (ERB)
<script type="text/javascript">
jwplayer.key="Your key here";
$(document).ready(function(){
jwplayer("video").setup({
height: 360,
width: 640,
playlist: [
<% videos.each do |v| %>
{
image: "<%= v.poster %>",
sources: [
{ file: "<%= v.url %>" },
]
},
<% end %>
]
});
})
</script>
<video id="video">Video Loading... Ensure JavaScript is enabled...</video>
Did you restart the server after downloading the player and including it in your layouts. This could be one reason of failure.
Download jwplayer from http://www.longtailvideo.com/jw-player/download/
Put these files to the particular directory:-
app/assets/jwplayer/jwplayer.flash.swf
vendor/assets/javascripts/jwplayer.js
vendor/assets/javascripts/jwplayer.html5.js
Then add these line in application.js
//= require jwplayer
//= require jwplayer.html5
On the page where you are playing video, add these lines
<script type="text/javascript">jwplayer.key="YOUR_JWPLAYER_KEY";</script>
<div id="video">Loading the player ...</div>
<script type="text/javascript">
jwplayer("video").setup({
flashplayer: "<%=asset_path('jwplayer.flash.swf')%>",
file: "<%= file_path %>",
height: 360,
width: 640,
analytics: {
enabled: false,
cookies: false
}
});
http://account.longtailvideo.com/#/home from where you can get your free self hosted key in signing up from Get Your License Key portion.
I also chose JWplayer.
Here are my steps.
I'm using https://github.com/choix/jwplayer-rails gem.
Added
gem 'jwplayer-rails', '1.0.1'
to my Gemfile.
Did all things from above page; in a show.html.slim view file included these lines:
= jwplayer_assets
br
br
= jwplayer({file:#lesson.media_file})
lesson.media_file attribute contains file location. For a video file project/public/videos/videoclip.webm, media_file contains string "/videos/videoclip.webm".
Hope this will be useful.

Generating a PDF in Rails with MathJax and wkhtmltopdf

I'm writing a website using Rails 3.2.6 and it needs to be able to display maths formatting on the website. To do this job I am using the mathjax-rails gem which works perfectly rendering the maths on, for example, questions/1. However, I would like the user to be able to download the question containing the maths content as a PDF. I have done a lot of searching and fiddling with my app and the closest I have come to a solution is wkhtmltopdf and the wicked_pdf gem. This works, in that a PDF is created which is saved to the user's computer. However, the maths is not rendered properly because, I presume, mathjax is not actually processing it.
Having done a bit of reading, I thought I found the answer elsewhere where it said to add a delay to the controller to give mathjax a chance to do its magic:
format.pdf do
render :pdf => 'filename',
:javascript_delay=>5000
end
However this doesn't work either and I end up with a PDF covered in things like:
[itex]3H_{2~(g)} + 2N_{2~(g)} ⇌ NH_{3~(g)}[/itex]
Rather than formatted maths. Has anyone got any clues on how I can get this to work?
Thanks in advance.
This is even a bit silly, but have you tried a longer delay?
With wkhtmltopdf this works:
wkhtmltopdf.exe --javascript-delay 15000 http://www.mathjax.org/demos/mathml-samples/ MML.pdf && MML.pdf
This does not work
wkhtmltopdf.exe --javascript-delay 5000 http://www.mathjax.org/demos/mathml-samples/ MML.pdf && MML.pdf
It's the same for the TeX samples as well
I finally solved it like this, the only thing is that I used the cdn instead of the gem:
This is my show action in the controller:
def show
#example = Exam.find(params[:id])
respond_to do |format|
format.html
format.pdf do
render pdf: #exam.name,
template: "exams/show.pdf.erb", // The template that you want to convert to pdf
:javascript_delay=>5000,
locals: {:exam => #exam},
layout: "application-pdf.html.erb"
end
end
end
And inside app/views/layouts/application-pdf.html.erb:
<!DOCTYPE html>
<html>
<head>
<title>MathTest</title>
<meta charset='utf-8' />
<%= stylesheet_link_tag "https://fonts.googleapis.com/icon?family=Material+Icons" %>
<script type="text/x-mathjax-config">
MathJax.Hub.Config({
extensions: ["tex2jax.js"],
jax: ["input/TeX", "output/HTML-CSS"],
tex2jax: {
inlineMath: [ ['$','$'], ["\\(","\\)"] ],
displayMath: [ ['$$','$$'], ["\\[","\\]"] ],
processEscapes: true
},
"HTML-CSS": { availableFonts: ["TeX"] }
});
</script>
<script type="text/javascript" async
src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-MML-AM_CHTML">
</script>
</head>
<body>
<%= yield %>
</body>
</html>
The key is to wrap your pdf template, in my case "exams/show.pdf.erb" in an application layout designed for your pdf, the cdn is loaded in the layout and you just have to give wickedpdf the option to delay the javascript processing so mathjax has enough time to process the page.
Hope it helps.
UPDATE
As Peter Krautzberger mentioned, cdn.mathjax.org is shutting down due to budget issues. The solution would stay the same, you just have to load the mathjax from your project internally or use another cdn. You can fiend some cdn alternatives here: https://www.mathjax.org/cdn-shutting-down/

Rails 3 and Jquery - how to use Application.js without document.ready()

I am using Rails 3 and JqueryUI-1.8.3). I have images on the page that I want the user to be able to resize.
Application.html.erb is as follows:
<%= javascript_include_tag "application.js" %>
<link class="jsbin" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1/themes/base/jquery-ui.css" rel="stylesheet" type="text/css" />
<script class="jsbin" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script class="jsbin" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.13/jquery-ui.min.js"></script>
When I write the code as below in my application.js, the image is resizable:
$(document).ready(function(){
$("#img1").resizable({ handles:'n,e,s,w,ne,se,nw,sw' , maxHeight: 300, aspectRatio: true });
});
However, the above code has problem because the image is sometimes not loaded correctly (the page had to be refreshed to display the image). This is because though the document is ready, image has not been loaded.
So I thought of using the following in application.js to fix the need for refresh:
$("#img1").load(function () {
$("#img1").resizable({ handles:'n,e,s,w,ne,se,nw,sw' , maxHeight: 300, aspectRatio: true });
});
However, this makes the image not resizable (only displays image, but no resize). The code seems to work on JSBin (http://jsbin.com/iboxoy/40/edit#source), but not in my Rails code. Anytime I do not use document.ready(), the image does not remain resizable in rails Jquery-UJS code.
Does application.js file always need to have document.ready()?
Why does (#img).load() not work in this case?
Is there any other solution which would be more efficient.
Thanks!
Try rebinding the resizable behavior both when the document is ready, and when the image has finished loading.
$(document).ready(function(){
$("#img1").resizable();
$("#img1").load(function () {
$("#img1").resizable();
});
}
Just in case someone comes across this question in the future --
I solved it by changing
$(document).ready()
to
$(window).load()
This is because we are doing actions on the images which need to be first loaded. This resolved the issue I was seeing.
Somehow $("#img1").load() within $(document).ready() is not working. It would be great if someone could explain why this is the case. My guess is that document is ready before image gets loaded (if image is not in the cache, typical for a new user of the page and the $("#img1").load() does not get fired (Since image is not loaded when document is ready).

Resources