Hi there, hoping someone can help me. I have several different Print Layouts for one table and I would like to be able to press a button and have the system print all of them in one go and merge them in to one document. Is that possible without making a master print layout?
11replies
Danjamesmedia
2 yrs ago
Reported - view
This isn’t possible natively in Ninox.
To merge a PDF then you’d need to use an external service such as cloudconvert via make.com.
It’s important to bear in mind that the PDFs must be generated by human interaction inside Ninox rather than via make.com/API as PDF generation is a client-side feature only.
danielmarine
8 mths ago
Reported - view
Hi,
The following formula displays a html button which achieves the desired result. Most of the functionality which Ninox currently misses can be worked around using the html() formula field and a bit of javascript.
You need URLs of each file you would like to combine. You then create a list of these URLs ensuring that they are separated by ','
For example, if you had 3 pdf files the array would need to look like this, making sure to include the single quotes:
What if I have to merge PDF from Ninox and not from url? How I have to change the code?
Walcher_Messebau
5 mths ago
Reported - view
I've tried to upload 3 pdf to Dropbox to test your HTML-Code. I've changed let URLarray:
let URLarray := "'https://www.dropbox.com/scl/fi/j18kasp1yx9nv7ste2x4j/Amerikana-H30V-TUV-certificate-bis-15.09.2025.pdf?rlkey=506wyyv40cindak4j41cc22ab&st=lwjyjz4u&dl=0', 'https://www.dropbox.com/scl/fi/ykm5jgvsdrwuvvct71197/BODENBELAG-PVC-ONE-scad.-13.03.2028.pdf?rlkey=cn6n7sdtest5u5eisskdx010f&st=f96v1qks&dl=0', 'https://www.dropbox.com/scl/fi/3ic1fsyo8pvri9f1qkurk/BODENPLATTEN-lackiert-Pavlux-10-mm-SAL-SRL-bis-26.06.2028.pdf?rlkey=7ldptnqsq51cz83hpf40y3gi0&st=33cdjpe2&dl=0'";
html("<!DOCTYPE html>
<html lang=""en"">
<head>
<meta charset=""UTF-8"">
<meta name=""viewport"" content=""width=device-width, initial-scale=1.0"">
<title>Combine PDF Files from URLs</title>
</head>
<body>
<button onclick=""combinePDF()"">Combine PDF Files from URLs</button>
<script src=""https://cdnjs.cloudflare.com/ajax/libs/pdf-lib/1.16.0/pdf-lib.min.js""></script>
<script>
async function combinePDF() {
const urls = [
" +
URLarray +
"
];
const pdfDoc = await PDFLib.PDFDocument.create();
for (let i = 0; i < urls.length; i++) {
const response = await fetch(urls[i]);
const arrayBuffer = await response.arrayBuffer();
const tempPdfDoc = await PDFLib.PDFDocument.load(arrayBuffer);
const copiedPages = await pdfDoc.copyPages(tempPdfDoc, tempPdfDoc.getPageIndices());
copiedPages.forEach((page) => pdfDoc.addPage(page));
if (i === urls.length - 1) {
const pdfBytes = await pdfDoc.save();
const blob = new Blob([pdfBytes], { type: ""application/pdf"" });
const url = URL.createObjectURL(blob);
const a = document.createElement(""a"");
a.href = url;
a.download = ""combined.pdf"";
a.click();
}
</script>
</body>
</html>
I tried inserting the code both as a button and in a formula field, but absolutely nothing happens when I press on it.
danielmarine
5 mths ago
Reported - view
Hi Walcher, the pdf URLs from dropbox are not raw PDF files, rather they are a link to the file stored on Dropbox.
For the sake of testing, try using these shared test PDF documents:
let URLarray := "'https://asc.ninoxdb.com/share/zj9gp6z7vpkvsrhwrk0w4pf3093m0znkmoa6','https://asc.ninoxdb.com/share/c3znh4ia69syy58y2ozk6s58j16ygqss8pto'";
I will follow up with further details shortly.
Walcher_Messebau
5 mths ago
Reported - view
OK we can make it with URL. I've made a shareFile(File) to get the url. Anyway if I trie your code it doesn't work
Walcher_Messebau
5 mths ago
Reported - view
Can you help me to fix this problem?
Walcher_Messebau
5 mths ago
Reported - view
Thanks for your quick reply. I've tested also yours but nothing happens on click. It would still be better if I could use the PDFs saved directly in Ninox instead of URLs.
danielmarine
5 mths ago
Reported - view
Could you explain why you do not want to use URLs?
Your html code should look like this:
let URLarray := "'https://asc.ninoxdb.com/share/zj9gp6z7vpkvsrhwrk0w4pf3093m0znkmoa6','https://asc.ninoxdb.com/share/c3znh4ia69syy58y2ozk6s58j16ygqss8pto'";
html("<!DOCTYPE html>
<html lang=""en"">
<head>
<meta charset=""UTF-8"">
<meta name=""viewport"" content=""width=device-width, initial-scale=1.0"">
<title>Combine PDF Files from URLs</title>
</head>
<body>
<button onclick=""combinePDF()"">Combine PDF Files from URLs</button>
<script src=""https://cdnjs.cloudflare.com/ajax/libs/pdf-lib/1.16.0/pdf-lib.min.js""></script>
<script>
async function combinePDF() {
const urls = [
" +
URLarray +
"
];
const pdfDoc = await PDFLib.PDFDocument.create();
for (let i = 0; i < urls.length; i++) {
const response = await fetch(urls[i]);
const arrayBuffer = await response.arrayBuffer();
const tempPdfDoc = await PDFLib.PDFDocument.load(arrayBuffer);
const copiedPages = await pdfDoc.copyPages(tempPdfDoc, tempPdfDoc.getPageIndices());
copiedPages.forEach((page) => pdfDoc.addPage(page));
if (i === urls.length - 1) {
const pdfBytes = await pdfDoc.save();
const blob = new Blob([pdfBytes], { type: ""application/pdf"" });
const url = URL.createObjectURL(blob);
const a = document.createElement(""a"");
a.href = url;
a.download = ""combined.pdf"";
a.click();
}
}
}
</script>
</body>
</html>")
Walcher_Messebau
5 mths ago
Reported - view
As mentioned previously, I created url fields in Ninox that I can use. Since the PDF attachment it refers can change beacuse maybe I have to change the pdf file, I can't take the static URL but I have to refer to the field. My problem now is that I need only != null urls to be considered to create the url
if length(URLarray) > 0 then
URLarray := left(URLarray, length(URLarray) - 1)
end;
This part gives me errors though. Maybe you can help me on how to create the URLarray. The complete code is:
let x := for loop in numbers(Zertifikate) do
record(Allegati, loop)
end;
let americana := count(x[Descrizione = "Americana"]) > 0;
let bodenplatten := count(x[Descrizione = "Bodenplatten lackiert SAL"]) > 0;
let dibond := count(x[Descrizione = "Dibond"]) > 0;
let klebefolieGlaenzend := count(x[Descrizione = "Klebefolie & Klebeschrift (Glänzend)"]) > 0;
let klebefolieMatt := count(x[Descrizione = "Klebefolie & Klebeschrift (Matt)"]) > 0;
let elektroKettenzuege := count(x[Descrizione = "Elektro - Kettenzüge"]) > 0;
let elektroZertifikat := count(x[Descrizione = "Elektro - Zertifikat"]) > 0;
let forex := count(x[Descrizione = "Forex"]) > 0;
let haengepunkte := count(x[Descrizione = "Hängepunkte"]) > 0;
let klebefolieEN48 := count(x[Descrizione = "Klebefolie EN48 (nur auf schwarze Wände)"]) > 0;
let lackGrundierung := count(x[Descrizione = "Lack Grundierung"]) > 0;
let lackVerinlegno := count(x[Descrizione = "Lack Verinlegno"]) > 0;
let laminatbodenOblige := count(x[Descrizione = "Laminatboden OBLIGE"]) > 0;
let leuchtballonAirstar := count(x[Descrizione = "Leuchtballon Airstar"]) > 0;
let linoleumPvcOne := count(x[Descrizione = "Linoleum PVC ONE"]) > 0;
let pedaneMdfSalp := count(x[Descrizione = "Pedane MDF - SALP"]) > 0;
let podesteBuetec := count(x[Descrizione = "Podeste Bütec"]) > 0;
let polionda := count(x[Descrizione = "Polionda"]) > 0;
let stoffBacklight := count(x[Descrizione = "Stoff Backlight"]) > 0;
let stoffFlagFahnen := count(x[Descrizione = "Stoff Flag - Fahnen"]) > 0;
let stoffPointexDanubio := count(x[Descrizione = "Stoff Pointex Danubio"]) > 0;
let stoffWaendeMdf := count(x[Descrizione = "Stoff Wände MDF"]) > 0;
let teppichAlma := count(x[Descrizione = "Teppich Alma"]) > 0;
let teppichMontecolinoMagic := count(x[Descrizione = "Teppich Montecolino MAGIC"]) > 0;
let teppichMontecolinoTrisSpagna := count(x[Descrizione = "Teppich Montecolino TRIS SPAGNA"]) > 0;
let tuerenLaminat := count(x[Descrizione = "Türen Laminat"]) > 0;
let vdsSprinklerNetzPointexRio := count(x[Descrizione = "VdS - Sprinkler Netz POINTEX RIO"]) > 0;
let vdsSprinklerNetzRabenring := count(x[Descrizione = "VdS - Sprinkler Netz RABENRING"]) > 0;
let waendeTuerenMdfSalp := count(x[Descrizione = "Wände und Türen MDF SALP"]) > 0;
let urlAmericana := if americana then (select Allegati where ID = 1).URL else null end;
let urlBodenplatten := if bodenplatten then (select Allegati where ID = 2).URL else null end;
let urlDibond := if dibond then (select Allegati where ID = 3).URL else null end;
let urlKlebefolieGlaenzend := if klebefolieGlaenzend then (select Allegati where ID = 9).URL else null end;
let urlKlebefolieMatt := if klebefolieMatt then (select Allegati where ID = 10).URL else null end;
let urlElektroKettenzuege := if elektroKettenzuege then (select Allegati where ID = 4).URL else null end;
let urlElektroZertifikat := if elektroZertifikat then (select Allegati where ID = 5).URL else null end;
let urlForex := if forex then (select Allegati where ID = 7).URL else null end;
let urlHaengepunkte := if haengepunkte then (select Allegati where ID = 8).URL else null end;
let urlKlebefolieEN48 := if klebefolieEN48 then (select Allegati where ID = 11).URL else null end;
let urlLackGrundierung := if lackGrundierung then (select Allegati where ID = 12).URL else null end;
let urlLackVerinlegno := if lackVerinlegno then (select Allegati where ID = 13).URL else null end;
let urlLaminatbodenOblige := if laminatbodenOblige then (select Allegati where ID = 14).URL else null end;
let urlLeuchtballonAirstar := if leuchtballonAirstar then (select Allegati where ID = 15).URL else null end;
let urlLinoleumPvcOne := if linoleumPvcOne then (select Allegati where ID = 23).URL else null end;
let urlPedaneMdfSalp := if pedaneMdfSalp then (select Allegati where ID = 16).URL else null end;
let urlPodesteBuetec := if podesteBuetec then (select Allegati where ID = 17).URL else null end;
let urlPolionda := if polionda then (select Allegati where ID = 18).URL else null end;
let urlStoffBacklight := if stoffBacklight then (select Allegati where ID = 19).URL else null end;
let urlStoffFlagFahnen := if stoffFlagFahnen then (select Allegati where ID = 6).URL else null end;
let urlStoffPointexDanubio := if stoffPointexDanubio then (select Allegati where ID = 20).URL else null end;
let urlStoffWaendeMdf := if stoffWaendeMdf then (select Allegati where ID = 21).URL else null end;
let urlTeppichAlma := if teppichAlma then (select Allegati where ID = 22).URL else null end;
let urlTeppichMontecolinoMagic := if teppichMontecolinoMagic then (select Allegati where ID = 24).URL else null end;
let urlTeppichMontecolinoTrisSpagna := if teppichMontecolinoTrisSpagna then (select Allegati where ID = 26).URL else null end;
let urlTuerenLaminat := if tuerenLaminat then (select Allegati where ID = 27).URL else null end;
let urlVdsSprinklerNetzPointexRio := if vdsSprinklerNetzPointexRio then (select Allegati where ID = 28).URL else null end;
let urlVdsSprinklerNetzRabenring := if vdsSprinklerNetzRabenring then (select Allegati where ID = 29).URL else null end;
let urlWaendeTuerenMdfSalp := if waendeTuerenMdfSalp then (select Allegati where ID = 31).URL else null end;
let URLarray := [];
if urlAmericana then URLarray := URLarray + urlAmericana + "," end;
if urlBodenplatten then URLarray := URLarray + urlBodenplatten + "," end;
if urlDibond then URLarray := URLarray + urlDibond + "," end;
if urlKlebefolieGlaenzend then URLarray := URLarray + urlKlebefolieGlaenzend + "," end;
if urlKlebefolieMatt then URLarray := URLarray + urlKlebefolieMatt + "," end;
if urlElektroKettenzuege then URLarray := URLarray + urlElektroKettenzuege + "," end;
if urlElektroZertifikat then URLarray := URLarray + urlElektroZertifikat + "," end;
if urlForex then URLarray := URLarray + urlForex + "," end;
if urlHaengepunkte then URLarray := URLarray + urlHaengepunkte + "," end;
if urlKlebefolieEN48 then URLarray := URLarray + urlKlebefolieEN48 + "," end;
if urlLackGrundierung then URLarray := URLarray + urlLackGrundierung + "," end;
if urlLackVerinlegno then URLarray := URLarray + urlLackVerinlegno + "," end;
if urlLaminatbodenOblige then URLarray := URLarray + urlLaminatbodenOblige + "," end;
if urlLeuchtballonAirstar then URLarray := URLarray + urlLeuchtballonAirstar + "," end;
if urlLinoleumPvcOne then URLarray := URLarray + urlLinoleumPvcOne + "," end;
if urlPedaneMdfSalp then URLarray := URLarray + urlPedaneMdfSalp + "," end;
if urlPodesteBuetec then URLarray := URLarray + urlPodesteBuetec + "," end;
if urlPolionda then URLarray := URLarray + urlPolionda + "," end;
if urlStoffBacklight then URLarray := URLarray + urlStoffBacklight + "," end;
if urlStoffFlagFahnen then URLarray := URLarray + urlStoffFlagFahnen + "," end;
if urlStoffPointexDanubio then URLarray := URLarray + urlStoffPointexDanubio + "," end;
if urlStoffWaendeMdf then URLarray := URLarray + urlStoffWaendeMdf + "," end;
if urlTeppichAlma then URLarray := URLarray + urlTeppichAlma + "," end;
if urlTeppichMontecolinoMagic then URLarray := URLarray + urlTeppichMontecolinoMagic + "," end;
if urlTeppichMontecolinoTrisSpagna then URLarray := URLarray + urlTeppichMontecolinoTrisSpagna + "," end;
if urlTuerenLaminat then URLarray := URLarray + urlTuerenLaminat + "," end;
if urlVdsSprinklerNetzPointexRio then URLarray := URLarray + urlVdsSprinklerNetzPointexRio + "," end;
if urlVdsSprinklerNetzRabenring then URLarray := URLarray + urlVdsSprinklerNetzRabenring + "," end;
if urlWaendeTuerenMdfSalp then URLarray := URLarray + urlWaendeTuerenMdfSalp + "," end;
if length(URLarray) > 0 then
URLarray := left(URLarray, length(URLarray) - 1)
end;
html("<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
<title>Combine PDF Files from URLs</title>
</head>
<body>
<button onclick='combinePDF()'>Combine PDF Files from URLs</button>
<script src='https://cdnjs.cloudflare.com/ajax/libs/pdf-lib/1.16.0/pdf-lib.min.js'></script>
<script>
async function combinePDF() {
const urls = [" + URLarray + "].filter(url => url !== 'null'); // Filtra gli URL nulli
if (urls.length === 0) {
alert('Nessun URL valido trovato.');
return;
}
const pdfDoc = await PDFLib.PDFDocument.create();
for (let i = 0; i < urls.length; i++) {
const response = await fetch(urls[i]);
const arrayBuffer = await response.arrayBuffer();
const tempPdfDoc = await PDFLib.PDFDocument.load(arrayBuffer);
const copiedPages = await pdfDoc.copyPages(tempPdfDoc, tempPdfDoc.getPageIndices());
copiedPages.forEach((page) => pdfDoc.addPage(page));
}
const pdfBytes = await pdfDoc.save();
const blob = new Blob([pdfBytes], { type: 'application/pdf' });
const url = URL.createObjectURL(blob);
// Creare un link temporaneo per il download
const a = document.createElement('a');
a.href = url;
a.download = 'combined.pdf';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
// Messaggio per aprire il file con Safari
alert('Il file PDF è stato creato. Se non si apre automaticamente, apri il file manualmente con Safari.');
}
</script>
</body>
</html>
Next problem is that I can download the file if I use Ninox in Safari but in Ninox for Mac i get an erro because it can not open blob link.
danielmarine
5 mths ago
Reported - view
Hi, apologies for the delay. Ninox blocked me from posting for some reason.
Have you tried using a select where clause rather than a loop?
For example,
let Array:= select Table1 where 'URL field' != null;
let URLFormat:= "'"+join(Array.'URL field', "','")+"'";
I haven't had time to test this exact code but this could be the right sort of idea to create your URL array. It also means it can run in a formula field rather than needing to use a button to initiate a loop, so the array will change as the files change. It can become a little complex where files are stored within individual fields.
Another option is to have a separate table for files and adding categories or descriptions to label and sort them accordingly. This could be a subtable in the record you want the files to relate to.
I would recommend using Google Chrome as your browser with Ninox. I have encountered issues with Safari and the Native App