Generate a PDF for saving/printing inside the form

From the latest beta and next Collect release, this is a worked example of a form utilising the new printer appearance for a text field.

With thanks to @Grzesiek2010 for the example repeat join

The attached zip contains an example form and an image, which is an example of collecting parent fields plus fields within a repeat and then combining all of this into a PDF that can be immediately printed out or saved as PDF, including generating a QR code to identify the document and link it to the record and displaying captured images.

Notes

  • I don't yet know how to refer to internal form media in the html so I have had to dynamically load a reference image to an image widget, and enforce annotation (open, save) and then use that submission image as the logo on the report
  • This is a barebones PDF with very little styling

print-widget-demo.zip (79.2 KB)

10 Likes

Wow, this update was incredible, can anyone tell me if I can change the layout of the PDF? If it is possible to place the sheet horizontally, and if I can customize the PDF to fit the format of my private document? Or is the entire header and division already standardized?

The PDF content / layout is entirely up to what you can build in the form. If you look at the example you'll see there's one part built from the repeat and one part that has the rest, plus that repeated element.

Replace all this content with your own html to suit your layout / template and you're away.

Landscape/portrait is selectable along with page size;

3 Likes

Hi @ahblake, did you manage to find a way to load the logo/media directly without having to annotate?

I'm not entirely sure that this is possible. The one thing I would try if you haven't already is prepending jr://images/ to the filename but I'm not sure whether the implementation accounts for that yet.

In the form ahblake is already trying something similar:

Did either of you try something like

<img width='80%' height='auto' src='", jr://images/logo.jpg, "'>

Note that it's important that the jr://images path is outside of quotes in the concat statement.

Yes, that makes me think he probably did already try it! Word of warning on the line that you're highlighting with the annotate trick -- it's going to not work very well with edits. The path to the logo should be in the default column, not calculation, I believe.

Ahblake, firstly, thank you for the clarifications, and I'm sorry, I don't think I fully understood what you said.
I'll try to reformulate my doubt: I saw in the video an example that I can choose the type of paper, size and orientation, however I would like to know if I can customize the PDF so that the form exports to a layout that I have standardized? In the example in the video, it seems to me that the pdf is assembled according to the order of the questions in the form, and this is something that I need to change in a personalized pdf.. understand?

@BrunoAngelo the text and layout in the created PDF is defined in the calculation field of the last text variable, the printer.
If in the form above you ask q1, q2 and q3 and then you want to show in the pdf q3, q1,q2, you just have to change the order.
Of course in the calculation you are using HTML tags to format the layout.
So if you have a PDF and you want the form to fill in the PDF, I think it is not possible.

Bruno,
The page size and orientation isn't specified in Collect, but in the device print dialogue after the html is created and sent to print.

If your html is relative (i.e. spacings / sizes etc) are % of page canvas size vs fixed (eg 500 pixels), then the document will adjust - however for a good looking report, you may need to use a combination of these for your desired layout, page size and orientation. This will involve some testing which you could speed by making some of those values entries in the form or having one or two different print widgets with varying layouts

Actual content and layout is purely up to what you can create in the html code, you can present fields in any order adn format - however if you are joining repeat fields and they have to be out of order then that will require some additional effort. If you have a preexisting template you will have to recreate it in html, not drop fields into an existing fillable PDF.

I'll double check all this soon, but from memory I did try referring to the form media directly without success and that's why I had to load it into the image widget to access it. There's very likely a way to do it that I'm not aware of.

1 Like

Hello, I have the following question. Following the instructions above, I tried to create my own HTML so that the questions would be included in the mentioned template. However, when I try to convert the XLSForm to XML using Central, it returns an error that initially seems to be related to the written HTML. Could someone help me with this? What could I be doing wrong that prevents my XLSForm from being uploaded? I will attach a screenshot of the steps I followed as instructed by "https://docs.getodk.org/form-question-types/" in the Printer widget.
I also tried removing the first two lines:
" meta charset="UTF-8">


Conferencia-Regional_Presidente_Prudente-01-06-2024.xlsx (21.4 KB)
meta name="viewport" content="width=device-width, initial-scale=1.0">"
but that didn't work either.***Attached is the print model via odkcollect and also the XLSForm

Hi @BrunoAngelo
sorry for the late response I hope you already solved your problem. If not the main issue (maybe there are more) is that you use double quotes " instead of single ones ' in the HTML. Double quotes should only be used to separate arguments passed in the concat function.
So your whole logic should be like:

concat("
<head>
    <meta charset=’UTF-8’>
    <meta name=’viewport’ content=’width=device-width, initial-scale=1.0’>
    <title>RelatĂłrio de Acompanhamento</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 0;
            padding: 0;
        }
        .container {
            width: 100%;
            max-width: 900px;
            margin: 0 auto;
            padding: 20px;
            border: 1px solid #000;
        }
        .header {
            text-align: center;
            margin-bottom: 20px;
        }
        .header img {
            width: 150px;
        }
        .header h1 {
            margin: 0;
            font-size: 24px;
        }
        .header p {
            margin: 0;
            font-size: 18px;
            color: blue;
        }
        table {
            width: 100%;
            border-collapse: collapse;
            margin-bottom: 20px;
        }
        table, th, td {
            border: 1px solid #000;
        }
        th, td {
            padding: 8px;
            text-align: left;
        }
        .section-title {
            background-color: #d3d3d3;
            text-align: center;
            font-weight: bold;
        }
        .footer {
            margin-top: 20px;
            text-align: center;
        }
        .footer div {
            display: inline-block;
            width: 30%;
            text-align: center;
        }
    </style>
</head>
<body>
    <div class='container'>
        <div class='header'>
            <img src='logo.png' alt='Logo'>
            <h1>R A</h1>
            <p>RELATĂ“RIO DE ACOMPANHAMENTO</p>
        </div>
        <table>
            <tr>
                <td>Contrato:</td>
                <td>__________</td>
                <td>Edital:</td>
                <td>__________</td>
                <td>Contratada:</td>
                <td>__________</td>
                <td>Lote:</td>
                <td>__________</td>
            </tr>
            <tr>
                <td>Rodovia:</td>
                <td>__________</td>
                <td>Objeto:</td>
                <td>__________</td>
                <td>Encerrado/Responsável pela frente:</td>
                <td>", ${Data},"</td>
                <td>Prazo:</td>
                <td>__________</td>
            </tr>
            <tr>
                <td>Telefone:</td>
                <td>__________</td>
                <td>Fiscal:</td>
                <td>__________</td>
            </tr>
        </table>
        <div class='section-title'>ATIVIDADES EXECUTADAS</div>
        <table>
            <tr>
                <td colspan='8'>Resumo dos Serviços Executados:</td>
            </tr>
            <tr>
                <td colspan='8' style='height: 200px;'>&nbsp;</td>
            </tr>
        </table>
        <table>
            <tr>
                <th>CONDIÇÕES METEOROLÓGICAS</th>
                <th>OCORRĂŠNCIAS</th>
            </tr>
            <tr>
                <td style='height: 100px;'>
                    ", ${valorQRcode}," <br> Bom
                </td>
                <td style='height: 100px;'>Obra finalizada.</td>
            </tr>
            <tr>
                <td>Quantos funcionários? <br> 0</td>
                <td></td>
            </tr>
            <tr>
                <td>Qual o efetivo de Máquinas? <br> 0</td>
                <td>", ${valorQRcode},"</td>
            </tr>
        </table>
        <div class='footer'>
            <div>Fiscal: __________</div>
            <div>CONSTRUTORA: __________</div>
            <div>DER/DR.12: __________</div>
        </div>
    </div>
</body>
")
2 Likes

I am just thinking out aloud using this printing feature. Can we incorporate a javascript script into the html content. Who has ever tried this using ODK collect? I am thinking of leveraging on advanced functionalities not yet implementable using odk alone.

I believe any javascript that you include will run as part of generating the PDF so you could do things like conditionally color certain elements using code. I'm curious to know more about what you might have in mind!

2 Likes

our !! This is very interesting to know, I didn't know it accepted anything from JavaScript or CSS, because I tried some styling and it wasn't accepted in JacaScript and also in CSS, the documentation also doesn't specify whether I can use an auxiliary language to simple HTML, it could be added in the documentation a more detailed example so that we have a more comprehensive idea on how to prepare a more personalized Widget Printer.

Could any developer clarify the potential or which languages ​​can be combined with WidgetPRinter's HTML?

I have one more question that is also not in the documentation, I would like to attach an image to the HTML and I am not able to, can anyone tell me what I could be doing wrong?

it looks like this in XLSForm:
image

and so on in HTML:
image