Issue attaching a generated PDF to email (SMTPAttachments)

Hello,

I am trying to attach a PDF generated with pdf-lib in my Five application and send it by email using SMTPAttachments.

The PDF itself is valid. If I download it locally from the browser it opens correctly in Foxit Reader.

However, when I attach the same document to an email in Five, the attachment received in the email is always about 1 KB and the PDF viewer reports that the file is corrupted or not a valid PDF.

Here is the workflow I am using:

Client side

I generate the PDF with pdf-lib:

let pdfBase64 = await pdfDocGlobal.saveAsBase64();

if (pdfBase64.indexOf(',') > -1) {
    pdfBase64 = pdfBase64.split(',')[1];
}

five.setVariable('PdfPiecesGlobal', pdfBase64);

The Base64 string is received correctly on the server side.
For example I logged:

  • Size: about 366,000 characters
  • Beginning of the string: JVBERi0xLj

So the Base64 looks valid.

Server side

I attach the PDF like this:

context.SMTPAttachments = [
    reportResult.report,
    {
        filename: "Documents_Fusionnes.pdf",
        data: five.variable.PdfPiecesGlobal,
        encoding: "base64",
        contentType: "application/pdf"
    }
];

The report generated by Five (reportResult.report) is attached correctly.

But the generated PDF attachment always arrives corrupted and around 1 KB.

I also tried several alternatives without success:

  • using content instead of data
  • converting Base64 to byte arrays
  • using Uint8Array
  • generating the PDF server-side

The result is always the same: attachment size about 1 KB.

My questions are:

  1. Is it supported to attach a dynamically generated file (Base64 or bytes) using SMTPAttachments?
  2. If yes, what is the correct format expected by Five for the attachment object?
  3. Is there a recommended way to attach a PDF generated with a JavaScript library like pdf-lib?

Thank you for your help!
Jean

Hi Jean,

Thank you for sharing this scenario.

You should be able to achieve it; once the data is in base64, it should be pretty much the same.

Just for curiosity, have you compared the beginning of the string of the pdfBase64 and the reportResult.report if they are different. Can you append the difference to the pdfBase64 and give it another try? For example:

This is the beginning of a string for a report result, it has data:application/pdf;base64,

“data:application/pdf;base64,JVBERi0xLjcKJYGBgYEKC“

If you prefer, and for a better investigation, you can also send me the pdfBase64 value**,** and if it’s possible, the client and server function to elton@five.co, so I can further investigate it.

Regards,

Elton S

Hi Jean,

Below is one example of how you can achieve it:

1 - Generate the PDF via pdf-lib. In my example, I already have a PDF Base64 (raw).
2 - Call the callback function and pass the Base64 to it.

function GeneratePDFClient(five, context, result)  {

//Base64-encoded PDF that includes metadata indicating it was produced by pdf-lib.

const  parms = {

    Base64PDF: 'JVBERi0xLjQKJYGBgYEKMSAwIG9iago8PCAvVHlwZSAvQ2F0YWxvZyAvUGFnZXMgMiAwIFIgL01ldGFkYXRhIDYgMCBSID4+CmVuZG9iagoKMiAwIG9iago8PCAvVHlwZSAvUGFnZXMgL0tpZHMgWzMgMCBSXSAvQ291bnQgMSA+PgplbmRvYmoKCjMgMCBvYmoKPDwgL1R5cGUgL1BhZ2UgCi9QYXJlbnQgMiAwIFIgCi9NZWRpYUJveCBbMCAwIDU5NSA4NDJdIAovQ29udGVudHMgNCAwIFIgCi9SZXNvdXJjZXMgPDwgL0ZvbnQgPDwgL0YxIDUgMCBSID4+ID4+ID4+CmVuZG9iagoKNCAwIG9iago8PCAvTGVuZ3RoIDEyMCA+PgpzdHJlYW0KQlQKL0YxIDI0IFRmCjcyIDc2MCBUZAooUHJvZHVjdCBSZXBvcnQpIFRqCjAgLTQwIFRkCihQcm9kdWN0ICAgICAgUHJpY2UpIFRqCjAgLTIwIFRkCihMYXB0b3AgICAgICAgJDEyMDApIFRqCjAgLTIwIFRkCihNb3VzZSAgICAgICAgJDI1KSBUagowIC0yMCBUZAooS2V5Ym9hcmQgICAgICQ1MCkgVGoKRVQKZW5kc3RyZWFtCmVuZG9iagoKNSAwIG9iago8PCAvVHlwZSAvRm9udCAvU3VidHlwZSAvVHlwZTEgL0Jhc2VGb250IC9IZWx2ZXRpY2EgPj4KZW5kb2JqCgo2IDAgb2JqCjw8IC9Qcm9kdWNlciAocGRmLWxpYikgL0NyZWF0b3IgKHBkZi1saWIpID4+CmVuZG9iagoKeHJlZgowIDcKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDE1IDAwMDAwIG4gCjAwMDAwMDAwNzQgMDAwMDAgbiAKMDAwMDAwMDE0MCAwMDAwMCBuIAowMDAwMDAwMzAwIDAwMDAwIG4gCjAwMDAwMDA0NzAgMDAwMDAgbiAKMDAwMDAwMDU2MCAwMDAwMCBuIAp0cmFpbGVyCjw8IC9TaXplIDcgL1Jvb3QgMSAwIFIgPj4Kc3RhcnR4cmVmCjY2MAolJUVPRg=='

}



five.executeFunction('GeneratePDFServer', parms, null, '', '', function (serverResult) {

    if (!serverResult) {

        five.showMessage('Error generating emails.');

    }

});

return five.success(result);

}

3 - In the server function, I am running a report, retrieving the Base64 string and appending the string to it ‘data:application/pdf;base64,‘.

4 - Then add both reports to an array and send them to the execution function.
When I received the email, both PDFs opened without issue.

function GeneratePDFServer(five, context, result)  {

////////////////////////////////////////////////////////////////////////////////////////////////

// Execute the report action

////////////////////////////////////////////////////////////////////////////////////////////////

const reportResult = five.executeAction('StaffExtensionTestV2', {StaffExtension:"2122"});

five.log("Test....:")

five.log(JSON.stringify(reportResult))

if (reportResult.isOk() === false)   {

    return five.createError(reportResult);

}

const files = \[\]

let base64PDF = context.Base64PDF;

//prepend (add to the beginning) a string using simple string concatenation.

base64PDF = \`data:application/pdf;base64,${base64PDF}\`;



files.push(base64PDF)

files.push(reportResult.report)



////////////////////////////////////////////////////////////////////////////////////////////////

// The reportResult contains a parameter called report, which is the report result in the format

// of a pdf (encoded as data url application/pdf mimetype)

////////////////////////////////////////////////////////////////////////////////////////////////    

const mailMergeContext = {SMTPToEmail: 'youremail@yourcompany.co', SMTPToName: "Staff", SMTPAttachments: \[...files\]};

const mailResult = five.executeAction('ExtensionReportEmail', mailMergeContext);

if (mailResult.isOk() === false) {

    return five.createError(mailResult);

}

return five.success('Report has been sent');

}

This is the default function that attaches the files before the email is sent; this function is on the event ‘Do Merge Record‘, in the Mail Merge action, tab events.

Observation: In this example, both files will have the same name, but you can change it.

function DoAddAttachments(five, context, result)  {
 
if (context.Attachments && context.Attachments.length > 0) {

    for (let i = 0; i < context.Attachments.length; i++) {

        const attachment = context.Attachments\[i\];

        five.addAttachment(five.toDataURL('application/pdf', attachment), 'StaffExtension');

    }    

}

return five.success(result);

}

If you use a five.variable for the Base64 (raw), make sure you set this variable in the back, and retrieve it using five.getVariable(‘variableName‘).

Please let me know if that helps you.
Regards,
Elton S

Hi Elton,

My problem was here:
context.SMTPAttachments = [
reportResult.report,
{
filename: “Documents_Fusionnes.pdf”,
data: five.getVariable('PdfPiecesGlobal'),
encoding: “base64”,
contentType: “application/pdf”

}
];

I simply needed to use the string of my PDF and not to wrap it in an object, and add the “data:…” prefix.
context.SMTPAttachments = [
reportResult.report,
“data:application/pdf;base64,” + five.getVariable('PdfPiecesGlobal')
}
];

Thank you so much! My problem is finally solved!