2

I'm attempting to merge two PDF files from Google Drive via ConvertAPI with the following code. Both pdf1 and pdf2 are file objects.

function mergePDFs(pdf1,pdf2){
  const formData = ""

  formData['Files[0]'] = pdf1.getBlob();
  formData['Files[1]'] = pdf2.getBlob();

  Logger.log(formData['Files[0]']);
  Logger.log(formData);

  endpoint = "https://v2.convertapi.com/convert/pdf/to/merge?Secret=XXXXXXXXXXXXX&StoreFile=true"

  var options = {
    'method' : 'post',
    'payload' : formData,
    'muteHttpExceptions': true
  };

  Logger.log(UrlFetchApp.fetch(endpoint,options));
}

To which I receive the following error code:

{"Code":4000,"Message":"Parameter validation error.","InvalidParameters":{"Files":["Files array item count must be greater than 0."]}}

It should be noted both Logger.log() statement come up empty. It appears to me that assignments of pdf1 and pdf2 to formData['Files[index]'] are not

I have modified the following lines considerably as I'm not familiar with this syntax, but it was referenced on another Stack Overflow post.

  formData['Files[0]'] = pdf1.getBlob();
  formData['Files[1]'] = pdf2.getBlob();

I'm also finding that the declaration of formData is inconsequential regardless of whether it's declared as an array or as a string.

Lastly, the pertinent documentation from ConvertAPI says:

Files to be converted. Value can be URL or file content. If used in >query or multipart content parameter must be suffixed with index >e.g. Files[0], Files1, Files[2]...

The following lines from the main function are the pertinent ones to the function in question:

  //lodgingRecieptId is set to a url
  const receiptFile = DriveApp.getFileById(lodgingReceiptId);

  ...

  const copyID = frontPage.makeCopy().getId();
  const copyDoc = DocumentApp.openById(copyID)
  copyDoc.setName("MPD | " + sheet.getLastRow());
  const body = copyDoc.getBody();

  //find and replace in template copy
  body.replaceText("{{AMOUNT}}",amount)
  body.replaceText("{{CURRENT_DATE}}",current_date)
  body.replaceText("{{LOCATION}}",location)
  body.replaceText("{{PURPOSE}}",purpose);
  body.replaceText("{{DEPART_DATE}}",formatLeaveDate);
  body.replaceText("{{RETURN_DATE}}",formatReturnDate);
  body.replaceText("{RSB}   ",rsb);
  body.replaceText("{{DAYS}}",days);
  body.replaceText("{{MPD_AMOUNT}}",mpdAmount);

  const docBlob = copyDoc.getAs('application/pdf');
  docBlob.setName(copyDoc.getName() + ".pdf");
  const copyPdf = DriveApp.createFile(docBlob);

  //merge lodging receipt and template copy ——> Save id
  mergePDFs(copyPdf,receiptFile)

1 Answers1

3

From your showing script and your showing official document, how about the following modification?

Modified script:

Please replace <YOUR SECRET HERE> with your secret.

function mergePDFs(pdf1, pdf2) {
  var fileValues = [pdf1.getBlob(), pdf2.getBlob()].map((e, i) => ({
    "Name": e.getName() || `sample${i + 1}`,
    "Data": Utilities.base64Encode(e.getBytes())
  }));
  var data = { "Parameters": [{ "Name": "Files", "FileValues": fileValues }, { "Name": "StoreFile", "Value": true }] };
  var endpoint = "https://v2.convertapi.com/convert/pdf/to/merge?Secret=<YOUR SECRET HERE>";
  var options = {
    'method': 'post',
    'payload': JSON.stringify(data),
    'contentType': "application/json",
    'muteHttpExceptions': true
  };
  var res = UrlFetchApp.fetch(endpoint, options);
  var outputPDFURL = JSON.parse(res.getContentText()).Files[0].Url;
  var outputFile = UrlFetchApp.fetch(outputPDFURL).getBlob().setName("sampleOutput.pdf");
  DriveApp.createFile(outputFile);
}

Note:

  • In this modification, it supposes that your secret is valid for using this API and pdf1 and pdf2 are the file object or the HTTPResponse object of the PDF data. Please be careful about this.

References:

Tanaike
  • 181,128
  • 11
  • 97
  • 165
  • @Jacob Bauer Sorry. I modified my sample script. Could you please confirm it? When this script is run, as a test, the merged PDF file is created as a file in the root folder. – Tanaike Mar 30 '22 at 03:38
  • Thank you! It looks like I'm still getting an error message: TypeError: Cannot read property '0' of undefined. This is on the line that outputPDFURL is declared. – Jacob Bauer Mar 30 '22 at 13:25
  • @Jacob Bauer Thank you for replying. I apologize for my poor English skill. Unfortunately, I cannot understand the line of your error of `TypeError: Cannot read property '0' of undefined.` from your reply. But, when I tested my proposed script, 2 PDF files are merged to 1 PDF file and created it as a file on the root folder. So, I cannot replicate your current issue. This is due to my poor skill. I deeply apologize for this. But I would like to support you. So can you provide the detailed flow for correctly replicating your issue? By this, I would like to confirm it. – Tanaike Mar 30 '22 at 13:30
  • @Jacob Bauer For example, can you provide your whole script? Because, although I suppose that `pdf1` and `pdf2` are the file object or the HTTPResponse object of the PDF data, I cannot understand how you call `mergePDFs(pdf1, pdf2)` in your question. This is due to my poor skill. I deeply apologize for this again. I would be grateful if you can forgive my poor skill. – Tanaike Mar 30 '22 at 13:33
  • @Jacob Bauer Now, when I tested my proposed script, when the values of `pdf1, pdf2` are not the file object or the HTTPResponse object of the PDF data, I confirmed that your error of `TypeError: Cannot read property '0' of undefined` occurred. But, in your showing script, `pdf1.getBlob()` and `pdf2.getBlob()` are used. So I had believed that those values might be the file object or the HTTPResponse object of the PDF data. From this situation, I guess that your current issue is due to your values of `pdf1` and `pdf2`. So, can you provide your whole script for calling `mergePDFs(pdf1, pdf2)`? – Tanaike Mar 30 '22 at 13:42
  • Just added in the main post above. Thank you! – Jacob Bauer Mar 30 '22 at 13:47
  • @Jacob Bauer Thank you for replying. When I saw your added script, `copyPdf` and `receiptFile` of `mergePDFs(copyPdf,receiptFile)` are `const copyPdf = DriveApp.createFile(docBlob)` and `const receiptFile = DriveApp.getFileById(lodgingReceiptId)`, respectively. From your script, I can confirm that `copyPdf` is the file object of PDF file. But I cannot know about `receiptFile`. Can I ask you about the detail of it? – Tanaike Mar 30 '22 at 13:50
  • Yes, receiptFile is also a PDF in this case. – Jacob Bauer Mar 30 '22 at 13:51
  • Actually, upon further review, receiptFile was a .jpg, and all is working well now. Many, many thanks for your help on this! – Jacob Bauer Mar 30 '22 at 13:56