google-apps-scriptgoogle-sheets-apigoogle-cloud-print

Print Google Spreadsheet Range using Cloud Print


I'm trying to sent a print job to one of my printers in Cloud Print.

The information I would like to print is the current range the user is selecting inside a Spreadsheet.

For this I have used this guide: https://www.labnol.org/code/20061-google-cloud-print-with-apps-script in order to set up the printer with GAS.

Step 1: Get the information you want to print

Source: How to Print sheet/range using .gs script in Google Sheets?

Explanation: carlesgg97's solution opens a modal window that will show our spreadsheet range exported as a PDF. From that point the user would need to manually print the job.

However, the information is sent to the Html via script.

Code adapted:

var PRINT_OPTIONS = {
  'size': 7,               // paper size. 0=letter, 1=tabloid, 2=Legal, 3=statement, 4=executive, 5=folio, 6=A3, 7=A4, 8=A5, 9=B4, 10=B
  'fzr': false,            // repeat row headers
  'portrait': false,        // false=landscape
  'fitw': true,            // fit window or actual size
  'gridlines': false,      // show gridlines
  'printtitle': false,
  'sheetnames': false,
  'pagenum': 'UNDEFINED',  // CENTER = show page numbers / UNDEFINED = do not show
  'attachment': false
}

var PDF_OPTS = objectToQueryString(PRINT_OPTIONS);

function printSelectedRange() {
  SpreadsheetApp.flush();
  var ss = SpreadsheetApp.getActiveSpreadsheet(); //Our Spreadsheet
  var sheet = ss.getActiveSheet(); //Our Active Sheet
  var range = sheet.getActiveRange(); // Our Active Range

  var gid = sheet.getSheetId();
  var printRange = objectToQueryString({
    'c1': range.getColumn() - 1,
    'r1': range.getRow() - 1,
    'c2': range.getColumn() + range.getWidth() - 1,
    'r2': range.getRow() + range.getHeight() - 1
  });
  //Here is variable that we will send to the htmlTemplate
  //We will build the HtmlTemplate using this url 
  var url = ss.getUrl().replace(/edit$/, '') + 'export?format=pdf' + 
                PDF_OPTS + printRange + "&gid=" + gid;
  var htmlTemplate = HtmlService.createTemplateFromFile('js');

  //Here I'm wrongly trying to get the content from the Html.
  //What I'll get is the content of js.html and not the info from the url
  var pdf_html = htmlTemplate.evaluate().getContent(); 

  //The blob needs to be build using an object. 
  //However I don't know how to access the pdf I'm exporting inside the url var
  var blob = Utilities.newBlob(pdf_html, MimeType.HTML, 'temp').getAs(MimeType.PDF);
  //random printer ID
  var printerID = 'b71f9cd2-3e3e-qweq-12asd-aogt5keoqoa2';

  printGoogleDocument(blob, printerID, 1);

}

function objectToQueryString(obj) {
  return Object.keys(obj).map(function(key) {
    return Utilities.formatString('&%s=%s', key, obj[key]);
  }).join('');
}

js.html:

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    <script>
      window.open('<?=url?>', '_blank', 'width=800, height=600');
      google.script.host.close();
    </script>
  </body>
</html>

Step 2: Print the Job

function printGoogleDocument(docBlob, printerID, docName) {
  var ticket = {
    version: "1.0",
    print: {
      color: {
        type: "STANDARD_COLOR",
        vendor_id: "Color"
      },
      duplex: {
        type: "NO_DUPLEX"
      }
    }
  };

  var payload = {
    "printerid" : printerID,
    "title"     : docName,
    "content"   : docBlob,
    "contentType": "application/pdf",
    "ticket"    : JSON.stringify(ticket)
  };

  var response = UrlFetchApp.fetch('https://www.google.com/cloudprint/submit', {
    method: "POST",
    payload: payload,
    headers: {
      Authorization: 'Bearer ' + getCloudPrintService().getAccessToken()
    },
    "muteHttpExceptions": true
  });

  response = JSON.parse(response);

  if (response.success) {
    Logger.log("%s", response.message);
  } else {
    Logger.log("Error Code: %s %s", response.errorCode, response.message);
  }
}

I believe that what I need to modify is how the HtmlTemplate is build so that I can create a correct BLOB and send it to the printer.

EDIT

Applying Tanaike recommendation it works just fine when I call the function directly from the Script Editor.

Although in the scenario where we want to call this function printSelectedRange() using another function I got the following error:

Exception: Request failed for https://docs.google.com returned code 500.
Truncated server response: <!DOCTYPE html><html lang="en"><head> 
<metaname="description" content="Web word processing, presentations and 
spreadsheets"><meta name="viewport" c... (use muteHttpExceptions option to` 
examine full response)`

The error points out to this line of code

var blob = UrlFetchApp.fetch(url, {headers: {authorization: "Bearer " + 
ScriptApp.getOAuthToken()}}).getBlob();

In order to avoid this I updated the parameters for the UrlFetchApp:

  var htmlTemplate = HtmlService.createTemplateFromFile('js');
  htmlTemplate.url = url;

  var params = {
    headers: {authorization: "Bearer " + ScriptApp.getOAuthToken()},
    muteHttpExceptions: true
  };

  var blob = UrlFetchApp.fetch(url, params).getBlob(); 
  var printerID = 'randomprinterID';

  printGoogleDocument(blob, printerID, 1);

Solution

  • How about this modification?

    In this modification, blob is directly retrieved from url as the PDF data.

    From:

    var url = ss.getUrl().replace(/edit$/, '') + 'export?format=pdf' + 
                PDF_OPTS + printRange + "&gid=" + gid;
    var htmlTemplate = HtmlService.createTemplateFromFile('js');
    
    //Here I'm wrongly trying to get the content from the Html.
    //What I'll get is the content of js.html and not the info from the url
    var pdf_html = htmlTemplate.evaluate().getContent(); 
    
    //The blob needs to be build using an object. 
    //However I don't know how to access the pdf I'm exporting inside the url var
    var blob = Utilities.newBlob(pdf_html, MimeType.HTML, 'temp').getAs(MimeType.PDF);
    //random printer ID
    var printerID = 'b71f9cd2-3e3e-qweq-12asd-aogt5keoqoa2';
    
    printGoogleDocument(blob, printerID, 1);
    

    To:

    var url = ss.getUrl().replace(/edit$/, '') + 'export?format=pdf' + PDF_OPTS + printRange + "&gid=" + gid;
    var blob = UrlFetchApp.fetch(url, {headers: {authorization: "Bearer " + ScriptApp.getOAuthToken()}}).getBlob();
    var printerID = 'b71f9cd2-3e3e-qweq-12asd-aogt5keoqoa2';
    printGoogleDocument(blob, printerID, 1);