Printing button

I’m trying to adapt this code to my softr site, the idea being to have a button that allows you to start printing a PDF file stored in airtable, without having to preview it

I saw on the net that print histories cause a lot of trouble for browsers, but I found this site with the following code (demo available here)

<html>
<head>
    <title>Print PDF using Dynamic iFrame</title>
</head>
<body>
    <input type="button" id="bt" 
        onclick="print('../sample.pdf')" 
            value="Print PDF" />
</body>

<script>
    let print = (doc) => {
    	let objFra = document.createElement('iframe');     // Create an IFrame.
        objFra.style.visibility = 'hidden';                // Hide the frame.
        objFra.src = doc;                   // Set source.

        document.body.appendChild(objFra);  // Add the frame to the web page.

        objFra.contentWindow.focus();       // Set focus.
        objFra.contentWindow.print();       // Print it.
    }
    
    // Using regular js features.
    
//     function print(doc) {
//         var objFra = document.createElement('iframe');
//         objFra.style.visibility = 'hidden';
//         objFra.src = doc;                  
//         document.body.appendChild(objFra);
//         objFra.contentWindow.focus();  
//         objFra.contentWindow.print();  
//     }
</script>
</html>

I’m trying to adapt a windows.records method, considering the name of the field in airtable is the.url.field I made this code

<html>
<head>
    <title>Print PDF using Dynamic iFrame</title>
</head>
<body>
    <input type="button" id="bt" 
        onclick="print(url)" //EDIT
            value="Print PDF" />
</body>

<script>
    var url = window.records.<record id>.record.fields.URL.<gdocs.last.version>; //EDIT
    let print = (url) => {
    let xhr = new XMLHttpRequest(); //EDIT
    xhr.open('GET', url, true); //EDIT
    xhr.responseType = 'blob'; //EDIT
    xhr.onload = (e) => { //EDIT
        if (xhr.status === 200) { //EDIT
            // Récupération du contenu du fichier PDF dans la réponse 
            let file = xhr.response; //EDIT

            let objFra = document.createElement('iframe');
            objFra.style.visibility = 'hidden';
            objFra.src = URL.createObjectURL(file);
            document.body.appendChild(objFra);
            objFra.contentWindow.focus();
            objFra.contentWindow.print();
        }
    };
    xhr.send(); //EDIT
}

    // Using regular js features.
    
//     function print(doc) {
//         var objFra = document.createElement('iframe');
//         objFra.style.visibility = 'hidden';
//         objFra.src = doc;                  
//         document.body.appendChild(objFra);
//         objFra.contentWindow.focus();  
//         objFra.contentWindow.print();  
//     }
</script>
</html>

I annotated with //EDIT my edits, I’m a noob so I’m stuck and don’t know how to debug, anyone have an idea? Soon the end of my galleys :pray:

Are you able to tell if the value of the url variable is correct?

Yep i’m sure, I tried with a display function of the URL and it is the good one :slight_smile: @dcoletta my code is probably wrong, the XMLHttpRequest() is my idea, not the idea of the original author…

Then in that case the likely error you are hitting is a cross-origin permissions issue, unless your URL is the same origin* as the one that served the page.

Do you see errors in the browser console?

*“origin” is a fancy term for the domain and port number. So if your app is at https://mygreatapp.softr.app, then the origin would be mygreatapp.softr.app:443.

Not a 403 but a 404 error @dcoletta, here the screen and the last update of the code (minor changes)

<html>
<body>
    <div id="code-message">test</div>
    <button id="open-button" class="myButton" onclick="display()">Display URL</button>
    <input type="button" id="bt" onclick="print()"    value="Print PDF" />
</body>
<style>
     #code-message {
        display: none;
      }
</style>
<script>

    function display() {
        const recordId = getUrlParam('recordId');
       var url = window.records[recordId].record.fields['lien'];
      var codeMessage = document.getElementById("code-message");
      codeMessage.innerHTML = url;
  codeMessage.style.display = "block";

        }

        let print = (url) => {
    let xhr = new XMLHttpRequest();
    xhr.open('GET', url, true);
    xhr.responseType = 'blob';
    xhr.onload = (e) => {
        if (xhr.status === 200) {
            // Récupération du contenu du fichier PDF dans la réponse
            let file = xhr.response;

            let objFra = document.createElement('iframe');
            objFra.style.visibility = 'hidden';
            objFra.src = URL.createObjectURL(file);
            document.body.appendChild(objFra);
            objFra.contentWindow.focus();
            objFra.contentWindow.print();
        }
    };
    xhr.send();
}
</script>
</html>

OK - I don’t thnk you’re running into a cross-origin problem, based on the error message.

It looks to me like the URL value is wrong. What I see from the screen shot is that the URL seems to end with /undefined - can that possibly be right?

The “url” that is display with my display()function is this one and looks normal as an URL from airtable. To be sure I also tried with a pdf stored in a classic CDN in another website and it’s same error 404

Thanks a lot @dcoletta :pray:

OK - I’m just saying that the reason you’re getting a 404 error is because the URL that the XHR request is actually hitting ends with the string undefined, which is usually the result of converting a variable with value null to a string.

Totally lost in the logs ahah

I tried different code on the web and now it launch the chrome printer and prints my 404 page ahah, and always this 404 error mmhh :apple:

Is there a page where I can check this and debug ? want to help need to find a starting point :slight_smile:

UPDATE :

Concerning the “undefined” error @dcoletta that I had with the functions using the URL of the attached airtable file, I solved the problem by integrating the variables defining the URL in the beginning of each function. Moreover, my record ID is retrieved in the first line of my script according to the well-known formula

// script for my URL
function myFunction ()
{
var string = window.records[recordId].record.fields['NameFieldAiartable'];
var urlPDF = string.match(/\(([^)]+)\)/)[1];
}

However my concern is not solved, I believe that the problem lies mainly in the fact that the javascript functions are not authorized to interact with an iframe whose content does not come from the same URL, I found this in several post stackoverflow and here is a quote from popular javascript library Print.js

PDF files must be served from the same domain as your app is hosted under. Print.js uses iframe to load files before printing them, therefore, it is limited by the Same Origin Policy. This helps preventing Cross-site scripting (XSS) attacks.

So maybe someone can help me solve this problem. How to store my pdf coming from airtable under the same URL as my softr site? I’m good at nocode, and less at CDN deployment haha. Any no code idea here? :slight_smile:

I don’t know enough about the details of how you have structured your page with its iframe to be able to help.

I think I would need to actually see the page as you have set it up and take a look at the browser errors you’re hitting.

Is there any way you can publish the page publicly so I can take a look at the problem?

@lea agree with @dcoletta if all you need is printing I’m sure there are ways, however we need to see the page and play with it. I will go over my DMs to see if there is anything.

Thank you for your answers, I was working on it, here is a demo page which exactly replicates the functioning of my original website, I can add you as collaborators if necessary but I this the console is what you want to see it more precisely.

Here is a little description of the view :

  1. Name of the record
  2. Classic softr filefield to display pdf
  3. Button to check the URL parameter’s script is working, just displaying URL of the pdf
  4. Button to change the src parameter and display the iframe
  5. Button to check recordid’s script is working
  6. Not interesting
  7. The button that launches the script to print the PDF
  8. The iframe

@artur I would be very happy that you are right, it is a key feature for me, I would like to succeed

@lea this is perfect now please add that part which doesn’t work as all functions you put there seems to be working :slight_smile:

Edit: Ok see the green button. Checking :slight_smile:

1 Like

Please add this into header

<script src="https://cdnjs.cloudflare.com/ajax/libs/print-js/1.6.0/print.js"></script>

Then call this function on your green button click

printJS('https://v5.airtableusercontent.com/v1/14/14/1673143200000/p2wlSdHVyWVk6a2xTDmNSw/FqMHkou7Z36Vx5ZVJh4eBSJq4368P9tsGeNBUfcZSmF8maKhAV0iU7Ga8q6O9Cv5fZxGt-R1tJVEUNK63JqW89jAkTMvolisIwuKXoclWoc/ZsWaR-lDe2TRHwmoZ7_3st_bhpHLB81ALdvxcjZWFZo');

Hey @artur
I tried (after updating the Airtable URL in your code, as it had expired due to their recent update)

Capture d’écran 2023-01-08 à 11.08.15

I created 2 new buttons and 2 new functions, described in the following code. For both functions I get the same error

<body>
        <input type="button" id="printJS" onclick="printJS()"    value="PrintJS (from artur)" />
        <input type="button" id="printJS2" onclick="printJSurl()"    value="PrintJS (from artur, with url from airtable)" />
</body>
<script>
function printJS() {    
        printJS('https://v5.airtableusercontent.com/v1/14/14/1673186400000/7MaqgJ_GgbIsLhstQtNdjg/8MuPo8ehU2QydUf0JNyMoJkW0yqvKPSF9sfSM7IjKiJtPPJfTWva-mdDGYhAEzxova-CzJIhli3iiHcztQi0WZctf5HiXhRfmDMxrf9B9o0/INN-qwIAlaL5Jit1dtHJP0xLD10KAXjvCLisNVWN2RI');
}
function printJSurl() {
        var string = window.records[recordId].record.fields['pdf'];
        var urlPDF = string.match(/\(([^)]+)\)/)[1];
        printJS('urlPDF')
}
<script>

Here is the error : maximum call stack size exceeded. So The printJS function calls itself endlessly, resulting in a stack call size overflow error. But I don’t know if that’s the problem that much, because the callback function isn’t mandatory, the documentation says you can omit it if you don’t want to perform any additional actions once the print completed. Anyway, the print does not start :frowning:

We’re making progress, but it’s not done yet :muscle:

this is wrong and causing errors pls call your functions differently from the library functions

@artur A good start, it works and confirms that you are right about my hypothesis (XSS) attacks I was talking about here
CleanShot 2023-01-08 at 15.36.17

Unfortunately, as we can see on the GIF, the button that injects the urlPDF variable (calculated from window.records) into the printJS print function does not work, we see 404 errors appear with urlPDF which is found in text in the site url. I think I’m injecting the variable wrong

function printcrablyurl() {
        var string = window.records[recordId].record.fields['pdf'];
        var urlPDF = string.match(/\(([^)]+)\)/)[1];
        printJS('urlPDF');
}

Thanks :muscle:

I think the best is to get a collaborator access and fix in your app directly :slight_smile: