WKHTMLTOPDF: How to disable header on the first page - header

wkhtml doesn´t repeat table elements "th" on every page like it should. So I thought it could be possible to simply use the --header-html option and add the table headers manually this way. But I don´t want them on the first page, since there are table headers already, plus some other first page stuff... I found some JS solution, but its too much complicated for me, since I know just the very basics of JS... Any ideas?

Did you try the JS solution? It's actually not that complicated. I just did a test with a long html file that contained a table that is split into many different pages and I managed to remove the headers from page 1 and 3 using this header file:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<script>
function subst() {
var vars={};
var x=document.location.search.substring(1).split('&');
for (var i in x) {var z=x[i].split('=',2);vars[z[0]] = unescape(z[1]);}
var x=['frompage','topage','page','webpage','section','subsection','subsubsection'];
for (var i in x) {
var y = document.getElementsByClassName(x[i]);
for (var j=0; j<y.length; ++j) y[j].textContent = vars[x[i]];
if(vars['page'] == 1){ // If page is 1, set FakeHeaders display to none
document.getElementById("FakeHeaders").style.display = 'none';
}
if(vars['page'] == 3) { // If page is 3, set FakeHeaders display to none
document.getElementById("FakeHeaders").style.display = 'none';
}
}
}
</script>
</head>
<body style="border:0;margin:0;" onload="subst()">
<table style="border-bottom: 1px solid pink; width: 100%; margin-bottom:5px;" id="FakeHeaders">
<tr>
<th>Your awesome table column header 1</th>
<th>Column 2</th>
<th style="text-align:right">
Page <span class="page"></span>/<span class="topage"></span>
</th>
</tr>
</table>
</body>
</html>
They key points to not there is that the table "headers" are contained in a table that has the ID "FakeHeaders". The javascript function subst() is run when the body is loaded and during the function it checks if the current page is 1 or if the current page is 3 and if it is, the FakeHeaders is set invisible. You will need to play with the margins and CSS to get it to look like you want but this Should work.
This is a known problem with wkhtmltopdf and most likely it won't be fixed any time soon, see issue 566 in the issue tracker. I see the JavaScript option as the only usable workaround, but you can try playing around with divs or manually splitting the tables if your input html, style and page sizes/margins are very predictable - but be warned, it will be really annoying.

If you can split the first page alone as a separate html, you can do this by using 'cover' in WKHTMLTOPDF.
PDFKit.new(url, :header_html => header_url, :cover => cover_url).

I faced similar problem in which I had used WKHTMLTOPDF header/footer and I wanted to remove them from the cover page. The issue was that the maximum height of header/footer was still appearing on all pages including the cover page.
The solution that clicked my mind and saved the day was that I generated two WKHTMLTOPDF files, one with header/footer on all pages and the other one without any header/footer. I then picked cover page from WKHTMLTOPDF generated file without header/footer and rest of the pages from the other WKHTMLTOPDF generated file with header/footer on all pages. I used PDF Merger library in PHP to merge selected pages of two WKHTMLTOPDF generated 'PDF' files to generate single PDF file with cover page and header/footer on rest of the pages.

<script type="text/javascript">
var pdfInfo = {};
var x = document.location.search.substring(1).split('&');
for (var i in x) { var z = x[i].split('=',2); pdfInfo[z[0]] = unescape(z[1]); }
function getPdfInfo() {
var page = pdfInfo.page || 1;
if(page != 1) {
document.getElementById('pHeader').style.display = 'none';
}
}
getPdfInfo();
</script>

For some reason, Nenotlep's answer didn't worked for me. It removed only page number..
So I created a class with a display: none; and simply added it. It worked this way.
function pagination() {
var vars = {};
var x = document.location.search.substring(1).split('&');
for (var i in x) {
var z = x[i].split('=', 2);
vars[z[0]] = unescape(z[1]);
}
var x = ['frompage', 'topage', 'page', 'webpage', 'section', 'subsection', 'subsubsection'];
for (var i in x) {
var y = document.getElementsByClassName(x[i]);
for (var j = 0; j < y.length; ++j) y[j].textContent = vars[x[i]];
if (vars['page'] == 1) {
var element = document.getElementById("pager");
element.classList.add("pager-hidden");
}
}
}
.pager {
font-size: 0.09375in;
padding-right: 1.00003in;
text-align: right;
letter-spacing: 0.01042in;
padding-bottom: 0.37501in;
}
.pager.pager-hidden {
display: none;
}
<body onload="pagination()">
<div class="pager" id="pager">
<div class="pager-pages">Page <span class="page"></span> / <span class="topage"></span></div>
</div>
</body>

Related

Images not generating fast enough, no image data sent

This is probably a performance issue, but I'm wondering how I'd go about proving that.
I set up an Image Resizer website and it works fine if I'm clicking on links to images, but if I use the following code to display a bunch of pictures on a website, the images do not show.
I'm wondering if it's because ImageResizer is being called so many times so quickly (only 15x) and it's getting tripped up somehow. I know enough to implement this and be dangerous, but not enough to understand what the hangup is here. It's possible that Dynamics CRM 2013 (what this code is being run as part of) has some code timeout or something that just skips showing the images if they take too long to load... not sure on that end.
Any and all help would be appreciated! Thanks everyone!
<html>
<head>
<title>Image View</title>
<script language="javascript" type="text/javascript">
function getImages() {
var unitSerial = window.parent.Xrm.Page.getAttribute("new_serial").getValue();
var guid = window.parent.Xrm.Page.data.entity.getId();
guid = guid.substring(1, guid.length-1);
var imageHTML = "<table><tr>";
for (var i=1; i<16; i++) {
imageURL = "http://website/Images/" + guid + "-" + unitSerial + "-" + i + ".png";
imageHTML = imageHTML + "<td><a href='"+imageURL+"' target=_blank><img height=95px border=0px width=130px source='"+imageURL+"?maxwidth=130&maxheight=95' /><br /><center>Pic " + i +"</center></a></td>";
}
imageHTML = imageHTML + "</tr></table>";
document.getElementById("images").innerHTML = imageHTML;
}
</script>
</head>
<body>
<div id="images"></div><script language="javascript" type="text/javascript">getImages();</script>
</body></html>
I just noticed the attribute I have written is "source" in the img tag but it needs to be "src". Whaaaaat a dummy. changed that and it works, of course.

Google rending +1 button way above and left of page content

We have implemented google +1 buttons on our site and they have served reliably for some time. However we recently noticed that the buttons are not serving reliably. We rarely see them appear in their designated spaces.
For example on this page: Sample Page : you'll see a gray box of social buttons to left of the page. In it, there is SUPPOSED to be a Google +1 button.
We've requested the button with this code:
<div id="social-google" class="social">
<script type="text/javascript">
(function() {
var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;
po.src = 'https://apis.google.com/js/plusone.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);
})();
</script>
<g:plusone size="medium"></g:plusone>
</div>
We've also tried this code:
<div id="social-google" class="social">
<!-- Place this tag where you want the share button to render. -->
<div class="g-plus" data-action="share" data-size="small" data-annotation="bubble"></div>
<!-- Place this tag after the last share tag. -->
<script type="text/javascript">
(function() {
var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;
po.src = 'https://apis.google.com/js/plusone.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);
})();
</script>
</div>
Occasionally we'll see a Google +1 button render but, more often than not, the space reserved for the button is apparently blank. When you examine things with firebug, you see that Google HAS attempted to render a button, but for some reason it has placed the button far above and left of the page boundaries.
Here is the top of the html Google generates for the button:
<div id="___plusone_0" style="position: absolute; width: 450px; left: -10000px;">
<iframe id="I0_1377554650466" width="100%" scrolling="no" frameborder="0" hspace="0 marginheight="0" marginwidth="0" style="position:absolute;top:-10000px;width:450px;margin:0px;border-style:none" tabindex="0" vspace="0" name="I0_1377554650466" src="https://apis.google.com/_/+1/fastbutton?bsv=o&usegapi=1&size=medium&hl=en-US&origin=http%3A%2F%2Fwww.comicbookresources.com&url=http%3A%2F%2Fwww.comicbookresources.com%2F%3Fpage%3Darticle%26id%3D47537&gsrc=3p&ic=1&jsh=m%3B%2F_%2Fscs%2Fapps- ...
As you can see Google gave its generated ___plusone_0 div a left position of -10000px and gave the inner iFrame a top position of -10000px. So the button is there. It's just floating out in space. If I manipulate theses position settings (to 0px) the button becomes visible in its appropriate spot.
Any idea why this would happen? Any idea how we can fix this?
You can try adding the following CSS declaration to your stylesheet:
#___plusone_0, #___plusone_0 iframe {
position:static !important;
}
This is a hackaround, so don't depend on it in long term.
Based on an old thread in Drupal Issues.
During the last few days I'm suffering from this problem too. I have a page building app. One of the widgets is google plus: users can enter a url, and the app generates a button. (So there can be more, than 1 button on the page.) Then user saves the page and can see it on Facebook.
Recommendations and observations...
Double check the protocol of google api script. For example, if your website is on https and you are trying to load http://apis.google.com/js/plusone.js, your buttons will probably fail to render.
When I tested this issue on my server, I occasionally opened the app in 2 browser tabs at the same time. Google buttons didn't appear in the first tab, but they did in the second one!
My app requires user to be authorized on Facebook. When I opened the app without authorization, the buttons were shown as expected. But when I logged in and refreshed the page - buttons disappeared.
When I opened the page on Facebook, buttons didn't appear, regardless of whether I was logged in or not.
I beg your pardon, if you think these notices have no sense, but they may save someone's time in future.
Workaround
Suppose, you're parsing the following code:
<!-- google button will be added into this div -->
<div class="googlePlus" data-href="http://google.com"></div>
jQuery function, which parse all .googlePlus divs.
$('.googlePlus').each(function () {
var $googleDiv = $(this);
// check, if button is already parsed
if (!$googleDiv.children().length) {
// add temporary id to the parent div
var $id = 'googlePlus-' + new Date().getTime();
$div.attr({
'id': $id
});
// create, add and render btn (IE compatible method)
var gPlusOne = document.createElement('g:plusone');
gPlusOne.setAttribute('href', $googleDiv.attr('data-href'));
document.getElementById($id).appendChild(gPlusOne);
gapi.plusone.go($id);
// function, correcting css styles
if (!$.isFunction($.fn.fixGooglePlus)) {
$.fn.fixGooglePlus = function () {
$(this).children('div').children('iframe').addBack().css({
position: 'static',
width: 106,
height: 24
});
}
}
// run function, until css is fixed
var $timer = setInterval(function () {
$googleDiv.fixGooglePlus();
if ($googleDiv.find('iframe').css('position') == 'static') {
clearInterval($timer);
$googleDiv.removeAttr('id');
}
}, 100);
} // button hasn't been parsed
});
Put the button code in a a new HTML file and put that file in an iframe. Compared to #U-D13's answer, it's less susceptible to changes by Google.

Using Nokia/Yelp Map API for finding POIs in an area

I'm trying to find the number of POIs inside a particular area using a map API. It's been recommended that I use either Nokia or Yelp APIs but I'm struggling to work out how to start on this project.
The actual type of the Point of interest is unimportant, all are relevant in this case. If anyone has any experience of these map APIs and would know what I should look at, or examples that would be useful for me to start, I would be extremely grateful!
Thanks
The first place to look would be the developer documentation site of the relevant API. For the Nokia APIs you would be looking at the JavaScript documentation or the RESTful Places API, for Yelp try the links under http://www.yelp.com/developers/ (disclaimer - I don't personally use Yelp)
There are numerous examples using the Nokia Place apis held within the API explorer to play with and see the results on screen.
Here are a couple of useful examples to get you started
Nearby places REST response Example
Places to eat JavaScript Example
Both the Nokia Places API and the Yelp API are local search APIs - in other words they always answer the question "Where can I find an X near Y?", so finding total number of POIs in their database is not a realistic task (since many POIs will be considered irrelevant to a search location, what you could do is find the density of POIs within a specific location.
The code below will initially show bookshops in central Berlin, but if you change the Focus of the map it will find bookshops in other towns as well. You need to obtain your own free app id and token to get it to work.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<!--
Example from Nokia Maps API Playground, for more information visit http://api.maps.nokia.com
-->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=7; IE=EmulateIE9"/>
<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
<title>Nokia Maps API Example: Search by category</title>
<meta name="description" content="Search by category"/>
<meta name="keywords" content="search, services, places, category"/>
<!-- For scaling content for mobile devices, setting the viewport to the width of the device-->
<meta name=viewport content="width=device-width, height=device-height, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
<!-- By default we add ?with=all to load every package available, it's better to change this parameter to your use case. Options ?with=maps|positioning|places|placesdata|directions|datarendering|all -->
<script type="text/javascript" charset="UTF-8" src="http://api.maps.nokia.com/2.2.3/jsl.js?with=all"></script>
<!-- JavaScript for example container (NoteContainer & Logger) -->
<style type="text/css">
html {
overflow:hidden;
}
body {
margin: 0;
padding: 0;
overflow: hidden;
width: 100%;
height: 100%;
position: absolute;
}
#mapContainer {
width: 80%;
height: 80%;
left: 0;
top: 0;
position: absolute;
}
#progress {
width: 80%;
height: 10%;
left: 0;
top: 80%;
position: absolute;
}
#buttons {
width: 80%;
height: 10%;
left: 0;
top: 90%;
position: absolute;
}
</style>
</head>
<body>
<div id="mapContainer"></div>
<div id="progress"></div>
<div id="buttons">
<a onClick="searchByCategory( map.center, 'bookshop' );return false;" href="#">Find Bookshops</a>
</div>
<script type="text/javascript" id="exampleJsSource">
/* Set authentication token and appid
* WARNING: this is a demo-only key
* please register on http://api.developer.nokia.com/
* and obtain your own developer's API key
*/
nokia.Settings.set("appId", "YOUR APP_ID");
nokia.Settings.set("authenticationToken", "YOUR TOKEN");
// Get the DOM node to which we will append the map
var mapContainer = document.getElementById("mapContainer");
// Create a map inside the map container DOM node
var map = new nokia.maps.map.Display(mapContainer, {
// Initial center and zoom level of the map
center: [52.51, 13.4],
zoomLevel: 10,
components: [
new nokia.maps.map.component.Behavior()
]
});
var searchManager = nokia.places.search.manager,
resultSet;
var searchCat;
var maxDistance = 0;
// Function for receiving search results from places search and process them
var processResults = function (data, requestStatus, requestId) {
var i, len, locations, marker;
if (requestStatus == "OK") {
// The function findPlaces() and reverseGeoCode() of return results in slightly different formats
locations = data.results ? data.results.items : [data.location];
// We check that at least one location has been found
if (locations.length > 0) {
// Remove results from previous search from the map
if (resultSet) map.objects.remove(resultSet);
// Convert all found locations into a set of markers
resultSet = new nokia.maps.map.Container();
for (i = 0, len = locations.length; i < len; i++) {
marker = new nokia.maps.map.StandardMarker(locations[i].position, { text: i+1 });
resultSet.objects.add(marker);
if (locations[i].distance > maxDistance){
maxDistance = locations[i].distance;
}
}
// Next we add the marker(s) to the map's object collection so they will be rendered onto the map
map.objects.add(resultSet);
// We zoom the map to a view that encapsulates all the markers into map's viewport
map.zoomTo(resultSet.getBoundingBox(), false);
progressUiElt.innerHTML = locations.length + " places found in the '" + searchCat + "' category within " + maxDistance + "m of "+ data.search.location.address.city ;
} else {
alert("Your search produced no results!");
}
} else {
alert("The search request failed");
}
};
// Binding of DOM elements to several variables so we can install event handlers.
var progressUiElt = document.getElementById("progress");
searchByCategory = function(searchCenter , category){
// Make a place search request
searchCat = category;
progressUiElt.innerHTML = "Looking for places in the '" + category + "' category...'";
searchManager.findPlacesByCategory({
category: category,
onComplete: processResults,
searchCenter: searchCenter,
limit: 100,
});
}
// Search for Bookshops in Berlin
searchByCategory( new nokia.maps.geo.Coordinate(52.51, 13.4), "bookshop" );
</script>
</body>
</html>
So you can see that there are over 100 bookshops in Berlin, but only 37 in Potsdam for example.

Change TinyMCE input height from textarea height

My form uses inputs and textareas, some of which I've added as TinyMCE elements. The problem is that the inputs are converted into the same size as the textareas. I'd like the inputs to be the same height as non-TinyMCE input fields (I'm using the latest version of TinyMCE - 3.5b2).
For example, TinyMCE adds this table to the inputs and textareas:
<table role="presentation" id="teaser_tbl" class="mceLayout" cellspacing="0" cellpadding="0" style="width: 590px; height: 100px; ">
How can I change this embedded style to reduce the height for inputs to 30px?
I've also posted this on the TinyMCE forums.
<table role="presentation" id="teaser_tbl" class="mceLayout" cellspacing="0" cellpadding="0" style="width: 590px; height: 100px; ">
That is exactly the element you will need to change. Tinymce has the width and height init param, but there are some cases where this setting is not sufficient.
Due to the fact that the editor iframe explicitly gets the same height assigned you will have to adjust the iframe too. You will need to call
var new_val = '30px';
// adjust table element
$('#' + 'my_editorid' + '_tbl').css('height', new_val);
//adjust iframe
$('#' + 'my_editorid' + '_ifr').css('height', new_val);
Idealy, this should be done right on editor initialization. So use:
tinyMCE.init({
...
setup : function(ed) {
ed.onInit.add(function(ed, evt) {
var new_val = '30px';
// adjust table element
$('#' + ed.id + '_tbl').css('height', new_val);
//adjust iframe
$('#' + ed.id + '_ifr').css('height', new_val);
});
}
});
Update: Solution without jQuery:
tinyMCE.init({
...
setup : function(ed) {
ed.onInit.add(function(ed, evt) {
var new_val = '30px';
// adjust table element
var elem = document.getElementById(ed.id + '_tbl');
elem.style.height = new_val;
// adjust iframe element
var iframe = document.getElementById(ed.id + '_ifr');
iframe.style.height = new_val;
});
}
});

Is there any way to automatically resize an iframe if the size of the content inside changes?

For example, I am trying to iframe the youtube subscription box on the homepage, and the problem is, if I make the iframe really long, then it wastes space, but if I make the size I want, then if the user clicks the "load more videos" button, then it gets cut off. So is there any way to make the iframe (or any alternatives) be a percentage of the size, or dynamically change when the page changes?
Create a file and call it iframe.html
<html>
<head>
<script type="text/javascript"></span>
function autoIframe(frameId){
try{
frame = document.getElementById(frameId);
innerDoc = (frame.contentDocument) ? frame.contentDocument : frame.contentWindow.document;
if (innerDoc == null){
// Google Chrome
frame.height = document.all[frameId].clientHeight + document.all[frameId].offsetHeight + document.all[frameId].offsetTop;
}
else{
objToResize = (frame.style) ? frame.style : frame;
objToResize.height = innerDoc.body.scrollHeight + 18;
}
}
catch(err){
alert('Err: ' + err.message);
window.status = err.message;
}
}
</script>
</head>
<body>
<iframe id="tree" name="tree" src="tree.html" onload="if (window.parent && window.parent.autoIframe) {window.parent.autoIframe('tree');}"></iframe>
</body>
</html>
Now create an html page called tree.html and put some dummy content in it.Make sure that the iframe.html and the tree.html are in the same directory. Open the .html files in browser and you will observe the o/p.
Some more useful links :
How to detect iframe resize?
How to detect iframe iframe resize