Forum How do I...?

Prince and rendered/dynamic HTML?

cwow101
I've been trying to figure this out for several days now, and would like to be able to eliminate (or not) Prince as a possible solution.

My environment:

I'm working in a CMS that generates PDFs using Prince. (I'm not sure what version, but assume the latest for right now).


I CAN:
  • Add Javascript to the page
  • Modify the Prince CSS

I CANNOT:
  • Access the Prince CLI (However, I'm willing to hassle our CMS account rep about enabling/disabling certain things)

Use Case:

We have a page where the users can select from a drop-down menu listing about 20 product solutions to see spec tables for that product. They can select one product, several, or all.

When they click "submit", Javascript on the page builds/returns the tables they selected, hides the ones they didn't. Pretty simple.

However, we'd like them to be able to create a PDF showing just those tables -- they don't like having a pdf with 20 tables when they are only interested in 6. Similarly, they don't want to go to 6 different pages and print each one individually.

Question:

Is there a way to do this with Prince (without access to the CLI)? From my initial reading of the user guides and testing, it doesn't seem that there is a way.

e.g. I don't want to have a script execute when the user opens the document ("prince-pdf-script"). It sounds like "prince-script" lets you add some generated plain text strings, but I'm not sure how/if that could work here, given that we're trying to respond to a user selection.

But then I ran across an answer on StackOverflow. It was posted several years ago, but says it also dealt with rendered HTML and Prince:

Render your html in the browser such as Chrome first. Get the entire document (not the source but actual rendered document) saved into html file and then use it as input to prince. You can get entire rendered document from browser javascript console.


This sounds like something I might be able to work with, but I can't find any documentation anywhere on how to go about doing it/what is possible/what isn't/etc.

If it's not possible, I'd just like to know so that I can focus on seeing how we can incorporate a solution like jsPDF (at least on pages like this).

(I'm also just very confused as this seems like an incredibly common use case and I can't find much discussion anywhere. Part of me suspects it's so obvious and simple that no one ever bothers discussing it....)



mikeday
If your server knows which tables should be displayed and which should be hidden, then it could apply CSS based on this when it converts the document to PDF with Prince. Alternatively it could apply a script to the document. Are you using any particular JavaScript framework? Basic structural transformations with jQuery should work fine with Prince, but more complex libraries like Angular and React are not supported yet.
cwow101
I'm not sure how we could implement the first option. For example:

  • John selects table#prodA, table#prodB, table#prodC //tables D -Z are hidden;
  • Julie selects table#prodB, table#prodD //tables A, C, E - Z are hidden;
  • Bob selects all tables. //show all tables

Is there a way we could account for that kind of variability in CSS rules?

Since John, Julie, and Bob might all be viewing the page and trying to print it at the same time, none of that will be 'hardcoded' in any way.

Alternatively it could apply a script to the document. Are you using any particular JavaScript framework?


Ah!

We're using jQuery with some ES2016 JS (but I'd be willing to roll that back if necessary).

So would it be possible to add something like this to an HTML header and have Prince process it (again, remembering we don't have access to the Prince CLI):

1. Julie selects Products A, B, and C from the menu

2. JavaScript/jQuery builds tables for each product and appends them to <div id="selected-tables">

3. When Julie clicks the "generate PDF" action, Prince processes the following function:

$(function() {
/*grabs the rendered HTML in Julie's browser*/
var userSelect = $("#selected-tables").prop('innerHTML');
/*adds it to a container that is exposed through Prince CSS*/
$(".pdfContent").append(userSelect);
});

That would be GREAT, but I'm not sure how/where to call it out so that it is only executed when the PDF is generated.



mikeday
The issue is that Prince runs on the server, not the browser, so it needs to be told the state of the page, and will not be able to see the currently rendered HTML.
hallvord
Is the page of product tables publicly available, or does Julie have to log in somewhere to see it? I'm asking because a possible solution if the page is public would be to make sure the URL includes information about what tables are shown/hidden and find a way Prince can be told to create a PDF from that URL. If you write JavaScript that shows/hides different tables based on the address, that script can likely run in Prince.

For example: say the table selection is done with a form, which on submit creates a URL like product-tables.html?show=product1&show=product2, which includes all the tables and some JS similar to this (needs some adjustment for correct class names and such):

    document.addEventListener('DOMContentLoaded', function(e){
        var tables = [];
        var queryParts = location.search.substr(1).split(/\&/), nv;
        for(var i=0; i<queryParts.length; i++){
            nv = queryParts[i].split('=');
            if(nv[0] === 'show') tables.push(nv[1]);
        }
        if (tables.length) {
            // hide all product tables by default
            $('.product-table').hide();
            // show only those whose id is in the list
            tables.forEach(function(table){
                $('#' + table).show();
            });
        }
    });


Now, if I call prince.exe with that URL as input, I'll get the right tables listed. Might this work for you?

Announcement: repos for tests/utils