3

I want to send a PDF file to be printed using the Google Cloud Print API. The code bellow will give me a positive message telling me that one page was generate. When I go and check what came out, I gate an empty page.

The same result happens if I save the print on Google Drive.

The code

unirest.post('https://www.google.com/cloudprint/submit')
.header('Authorization', 'Bearer ' + token)
.header("Accept-Charset", "utf-8")
.field('xsrf', xsrf_token)
.field('printerid', printerId)
.field('ticket', '{"version": "1.0", "print": {}}')
.field('title', 'Test from Simpe.li')
.field('contentType', 'application/pdf')
.attach('content', buffer)
.end(function (res) {

    console.log(res);

});

I know that what I'm sending is a PDF, because when I change the

.field('contentType', 'application/pdf')

to

.field('contentType', 'text/plain')

I will get 53 pages of text which is the raw content of the PDF file.

enter image description here

Question

What I'm doing wrong?

Tech spec

  • NodeJS v4.1.1
  • Unirest v0.4.2
David Gatti
  • 3,576
  • 3
  • 33
  • 64

1 Answers1

6

It turns out that the Google documentation left some key information out. To send a binary type data, like a PDF, you need to convert the file to base64. In addition to that you need to tell Google that you are going to send them a base64 blob with the add field contentTransferEncoding and set the value to base64.

Another important thing. There is a bug in Unirest (for NodeJS at least), where sending a base64 file won't set the Content-Size header. Nor even setting your own will fix the problem. To circumvent this issue I had to switch to Request. The following code shows a post to Google Cloud Print that works:

let buffer64 = buffer.toString('base64');

let formData = {
    xsrf: xsrf_token,
    printerid: printerId,
    ticket: '{"version": "1.0"}',
    title: 'Test Print',
    contentTransferEncoding: 'base64',
    contentType: 'application/pdf',
    content: buffer64
};

let headersData = {
    'Authorization': 'Bearer ' + token
};

request.post({
    url: 'https://www.google.com/cloudprint/submit',
    headers: headersData,
    formData: formData
}, function (err, httpResponse, body) {

  if (err) {

    return console.error('upload failed:', err);

  }

  console.log('Upload successful!  Server responded with:', body);

});

I hope this will help others :)

David Gatti
  • 3,576
  • 3
  • 33
  • 64
  • Where did you get your token in headersData? Is that something the user has to authenticate for? – thailey01 Mar 22 '17 at 22:40
  • If you are talking about OAuth 2.0, then you should read this https://developers.google.com/identity/protocols/OAuth2 to find out how it works. – David Gatti Mar 23 '17 at 07:47
  • Ok, I was hoping there was a way to use google cloud printing without authenticating users. Like, if there was a way to print to one designated printer silently and without user involvement. – thailey01 Mar 23 '17 at 15:24
  • no man, no body would want their printer to randomly start printing :D – David Gatti Mar 23 '17 at 18:20
  • Lol, not what I meant though. I'm working with a restaurant and I'd like to print to their printer using google cloud print when users order food from my app. – thailey01 Mar 23 '17 at 19:23
  • Then just add supper for Auth2.0, and let them authenticate from your app with Google, so they can give you access to their printers :) no problemo :) – David Gatti Mar 23 '17 at 20:42
  • Would it be ok for me to pm you? – thailey01 Mar 23 '17 at 20:43
  • Yes, no problem – David Gatti Mar 25 '17 at 08:00
  • Ok, I can't seem to export this conversation to chat so I'll just try to explain here. The restaurant I'm working with has a printer in their brick and mortar building. So when a user makes a purchase on the app, we're using square as a payment processor, I'd like to use google cloud print to send a job to that printer in the restaurant itself. Using google cloud print though you seem to need the user using the app to sign in to google first and that wouldn't make any sense in the flow of the app. – thailey01 Mar 25 '17 at 14:35
  • Since this is a separate questions, create a question on stack, and send the link to https://twitter.com/dawidgatti, and I'll give you a nice response :) I can't do it in this section, not enough space. – David Gatti Mar 26 '17 at 11:17
  • Ok, I've sent it :) – thailey01 Mar 26 '17 at 14:41
  • Or you can use an headless browser to authenticate to google and retrieve the access token. – mathieug May 12 '17 at 13:28