Turning HTML into PDF using CDN libraries and external stylesheets

https://i.sstatic.net/TKmYV.pngAfter creating my resume using Bootstrap and Material Design Lite, I am now looking for a way to convert the HTML page into a PDF file. Despite trying various libraries like jsPDF and tools such as html2pdf and PrinceXML, the resulting PDF does not match the appearance of the original HTML page.

The issue lies in the fact that the styles are missing from the PDF output, making it resemble a simple browser print preview (Ctrl+P). Therefore, I am seeking advice on whether there are any other tools or libraries available to address this problem, or if there are specific options within the aforementioned tools that I should be utilizing.

You can view examples of the PDF outputs here: https://i.sstatic.net/taGHs.png

Answer №1

If you're looking for a reliable converter, consider giving WKHTMLTOPDF a try on your backend system. It captures exactly what is displayed in your browser and supports HTML, CSS, and even JavaScript as well. WKHTMLTOPDF is built based on WebKit which makes it a powerful tool for converting web content into PDF files.

To use it during runtime, simply run the command:

wkhtmltopdf http://YourWebsiteURL.com OutputFileName.pdf

If you encounter issues where the CSS styles are not being applied, double-check the path of how the CSS files are included. Make sure to use absolute paths rather than relative paths for better compatibility.

Answer №2

The issue lies within the Bootstrap library itself, rather than being related to any plugins or PDF tools being utilized. When "printing" a web page, including print to PDF, Bootstrap tends to remove most styles. At my company, we offer the DocRaptor HTML to PDF service which has a helpful blog post outlining suggested solutions for ensuring Bootstrap styles are maintained when printing. These solutions can be summarized as follows:

  • Utilize screen CSS mode/rules for printing instead of print mode. This will save you from having to make numerous overrides in order to get Bootstrap to display correctly. Opting for screen mode is a more straightforward approach.
  • Bootstrap often perceives PDFs as being displayed on an extra small device such as a cell phone, thus requiring adjustments to breakpoints or column definitions in your code.
  • In cases where the last column shifts to a new row, it may be due to Bootstrap setting column widths to XX.66666667%. When these percentages are added up by the PDF engine, they exceed 100%, causing the final column to wrap to a new line. To address this, overriding Bootstrap's column widths is necessary (handy Gist file available here).

Answer №3

Plugins can be used with jsPDF. To enable the printing of HTML, certain plugins must be included by following these steps:

  1. Visit https://github.com/MrRio/jsPDF and download the latest version.
  2. Add the following scripts to your project:
    • jspdf.js
    • jspdf.plugin.from_html.js
    • jspdf.plugin.split_text_to_size.js
    • jspdf.plugin.standard_fonts_metrics.js

If you wish to exclude specific elements, they must be given an ID to be ignored through a special element handler in jsPDF. Your HTML should then resemble this:

<!DOCTYPE html>
<html>
  <body>
    <p id="ignorePDF">don't print this to pdf</p>
    <div>
      <p><font size="3" color="red">print this to pdf</font></p>
    </div>
  </body>
</html>

Use the following JavaScript code to open the generated PDF in a new window:

var doc = new jsPDF();          
var elementHandler = {
  '#ignorePDF': function (element, renderer) {
    return true;
  }
};
var source = window.document.getElementsByTagName("body")[0];
doc.fromHTML(
    source,
    15,
    15,
    {
      'width': 180,'elementHandlers': elementHandler
    });

doc.output("dataurlnewwindow");

This resulted in a clean PDF that only contained the text 'print this to pdf' for me.

It's important to note that in the current version, the special element handlers only recognize IDs, as mentioned in a GitHub Issue. It states:

Matching is done against every element in the node tree, focusing on speed. Only element IDs are matched in jQuery style "#id", not all jQuery selectors.

Using class selectors like '.ignorePDF' did not work for me, so individual handlers had to be added for each ignored element:

var elementHandler = {
  '#ignoreElement': function (element, renderer) {
    return true;
  },
  '#anotherIdToBeIgnored': function (element, renderer) {
    return true;
  }
};

From the examples, it's noted that selecting tags like 'a' or 'li' is possible but may be too broad for some cases:

Special element handlers can be registered using jQuery-style ID selector for either ID or node name. No support for other selectors like class or compound at present.

An important point to mention is that CSS styling is lost when converting to PDF. However, jsPDF can format headings (h1, h2, h3, etc.) and only prints text nodes, omitting textarea values. For example:

<body>
  <ul>
    <!-- This is printed as it contains a textnode -->        
    <li>Print me!</li>
  </ul>
  <div>
    <!-- Not printed because jsPDF doesn't handle value attribute -->
    <input type="textarea" value="Please print me, too!">
  </div>
</body>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Error message: Vercel is unable to locate the module or its corresponding type declarations when running the command "npm run build"

I've encountered a problem with various components similar to this one. When I run "npm run build" on my computer locally, everything runs smoothly. I have tested node versions 14, 16, and 18 on Vercel, but unfortunately, the issue persists. This is ...

Using indexOf() in JavaScript

Recently, I delved into learning JavaScript through the guide of Beginning Javascript 5th edition. However, I am stumped by a perplexing JavaScript script function getCookieValue(name) { var value = document.cookie; var cookieStartsAt = value.indexOf(" " ...

Monitor changes in the visible price range in lightweight-chart

Is there a way to detect when the visible price range changes in lightweight-chart, similar to how the timeScale's visible time range change can be detected through subscribeVisibleTimeRangeChange? I couldn't find anything related in the document ...

What is the proper way to utilize the "Long" type when declaring a schema in MongoDB?

Forgive my ignorance, but I am trying to achieve something like this: var mongoose = require('mongoose'); var Long = require("long"); var UserSchema = new mongoose.Schema({ id: Long(), name: String, completed: Long(), ...

What is the best way to ensure a stable background image using CSS?

I have successfully created my navigation section and now I am working on the main body of the website. However, when I try to add a background image, it ends up appearing on the navbar as well. Can someone please assist me with this issue? Below is the HT ...

How can I use React to switch the visibility of a nested component from the parent container component?

Objective I am in the process of replicating a List Control Component using React and Redux, following the guidelines outlined in Google Material Design layout. This list control will enable users to create, rename, and delete items within the list witho ...

Leverage the power of JavaScript within your PHP code

For instance, I am looking to use PHP to send an email and my message is obtained from a combination of PHP and JavaScript data... <?php $str= "Data: " . date("Y-m-d H:i:s") . "\n\n"; $str=$str . "system: " . $_SERVER['HTTP_USER ...

Performing Jasmine unit testing on a component that relies on data from a service, which itself retrieves data from another service within an Angular 2+ application

Attempting to implement unit testing for a service using httpmock has been challenging. The service in question utilizes a method to make http get calls, but I have encountered difficulties in writing the test cases. saveservice.service.ts -- file const ...

What is the best way to navigate to the next input field upon pressing the tab key in an Angular table?

In the Angular table, how can I make the cursor move to the next input field when the tab key is pressed? Essentially, when the tab key is pressed in a cell, I want the cursor to move to the next cell from left to right. For example, in the screenshot belo ...

Unable to retrieve information from JSON file utilizing AngularJS version 1.6

I'm having trouble retrieving data from my JSON file using AngularJs 1.6 myApp.controller("homeCtrl", function($scope, $http) { $scope.Data = []; var getJsonData = function() { $http.get('contactlist.json').then(func ...

How to implement multiple conditions with ng-if in HTML

I need to implement a button that is visible only for specific index numbers: <tbody data-ng-repeat="(wholesalerIndex, wholesaler) in wholesalers"> <tr> <td> <but ...

Having trouble with refreshing the div content when using jQuery's $.ajax() function

My goal is to refresh a div that has the id #todos after saving data in the database. I attempted to use .load() within $.ajax() $('.todo--checkbox').change(function () { let value = $(this).data('value'); $.ajax({ url: ...

Adding three components to the header of Bootstrap's modal: A quick guide

I would like my modal to contain three elements: On the left: a button In the center: a title On the right: a close button At first glance, it may appear that I have achieved this layout, but in reality, my title is not properly centered. Here's the ...

Coordinating multiple API requests for optimal performance

I have a task where I need to retrieve data from two different API endpoints. Once both sets of data are fetched, I need to compare the information obtained from each source. I am familiar with fetching data from a single API endpoint and using a callback ...

Error with displaying uib-datepicker within a table row

I need assistance with integrating a uib-datepicker into a table: <div class="table-responsive"> <table class="table table-striped" > <thead> <tr> <th class="col-md-2"><span>Date ...

What is the proper way to pass options using jquery.timeline.js?

My implementation of jquery.timeline.js involves initializing the timeline using the script below: <script> $(function () { $("#myTimeline").Timeline({ startDatetime: "2019-02-25 00:00", rows : 5 }) }) </script&g ...

What mechanism enables the scores on this sports ticker to refresh automatically without relying on ajax calls?

While browsing scores on , I found myself intrigued by the way they update their scores without using ajax calls or iframes. It's a mystery to me how this functionality is achieved. Can anyone shed some light on how it works? ...

Customize cursor using CSS

I have a div with overflow: hidden that I am making scrollable by allowing the user to click and drag the background. There are links and buttons within this space. Here is the CSS I am using for this: #div-grabscroll { cursor: url(../img/op ...

Including a logo and navigation bar in the header while collaborating with different subregions

I'm having trouble getting a picture to display in the header alongside a horizontal menu. I've divided the header into two sections: img and navbar (html). The navbar is showing up correctly, but the image isn't appearing. Any suggestions o ...

Tips for positioning text beneath an image in a visually appealing way

Looking to align text under a picture, like this: Previously, I attempted it this way, which was incorrect: This is my code: .test{ display:inline-block; } .test img{ display:block; } <span class="test"> <img src="http://cdn.sstatic. ...