Search code examples
apirestgoogle-apps-scriptconvertapi

Merge PDF in ConvertAPI using Google Apps Script


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)

Solution

  • 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: