If you’ve worked with Google Cloud Printing then you know that much of the sample code out there that shows how to submit print jobs using Java or C# don’t work … frustrating to say the least.

After more pain then I’d care to admit, I finally got the code working so wanted to share it.

Below is the code to submit a PDF file for Printing (or storage on a Google Drive) using Google Cloud Printing from an Android device.

For a complete Android Studio project that shows how to interrogate the available printers, get the list of print jobs, and submit a print job download this project zip file.

public class GoogleCloudPrint {
 private static final String SERVICE_SOURCE_NAME = "your-apps-name-for-logging-purposes";
 public static final String GCP_BASE_URL_STRING =
  "https://www.google.com/cloudprint";
 private static final String SUBMIT_URL_STRING =
  GCP_BASE_URL_STRING + "/submit";
 private static final int DEFAULT_STREAMING_CHUNK_SIZE = 0;
 String mUsername;
 String mPassword;
 public GoogleCloudPrint(String username, String password) {
  mUsername = username;
  mPassword = password;
 }
 public void print(String printerId, String title, File documentFile, String mimeType) {
  try {
   byte[] documentBytes = getBytes(documentFile);
   String urlString = SUBMIT_URL_STRING +
    "?output=json" +
    "&printerid=" + printerId +
    "&contentType=" + mimeType +
    "&title=" + title ;
   // Open connection and configure for POST with streaming writes
   HttpsURLConnection connection = createConnection(urlString);
   connection.setDoOutput(true);
   connection.setChunkedStreamingMode(DEFAULT_STREAMING_CHUNK_SIZE);
   // Create the multi-part form data prefix and suffix
   // include filename value in prefix
   String queryPrefix = String.format(
    "------CloudPrintFormBoundaryqeq6g6ncj5v7\r\n" +
    "Content-Disposition: form-data; name=\"content\"; filename=\"%s\"\r\n" +
    "\r\n", title);
   String querySuffix = "\r\n------CloudPrintFormBoundaryqeq6g6ncj5v7--";
   // Encoide prefix and suffix and determine total content length
   byte[] queryPrefixBytes = EncodingUtils.getAsciiBytes(queryPrefix);
   byte[] querySuffixBytes = EncodingUtils.getAsciiBytes(querySuffix);
   int contentLength = queryPrefixBytes.length + documentBytes.length + querySuffixBytes.length;
   // Add headers for multipart form data and length
   connection.addRequestProperty("Content-Type", 
    "multipart/form-data; boundary=----CloudPrintFormBoundaryqeq6g6ncj5v7");
   connection.addRequestProperty("Content-Length", Integer.toString(contentLength));
   // Write the document to server wrapped as multipart form data
   writePrintBody(connection, queryPrefixBytes, documentBytes, querySuffixBytes);
   // Write results to log
   InputStream inStream = connection.getInputStream();
  } catch (Exception e) {
   e.printStackTrace();
  }
 }
 private HttpsURLConnection createConnection(String urlString) throws IOException {
  String auth = retrieveAuthToken(mUsername, mPassword);
  URL url = new URL(urlString);
  HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
  connection.addRequestProperty("X-CloudPrint-Proxy", SERVICE_SOURCE_NAME);
  connection.addRequestProperty("Authorization", "GoogleLogin auth=" + auth);
  return connection;
 }
 private void writePrintBody(HttpsURLConnection connection, byte[] prefixBytes,
  byte[] documentByes, byte[] suffixBytes) throws IOException {
  OutputStream outStream = new BufferedOutputStream(connection.getOutputStream());
  outStream.write(prefixBytes, 0, prefixBytes.length);
  outStream.write(documentByes, 0, documentByes.length);
  outStream.write(suffixBytes, 0, suffixBytes.length);
  outStream.flush();
  outStream.close();
 }
private String retrieveAuthToken(String userId, String password) {
  final String AUTHORIZE_URL_FORMAT = "https://www.google.com/accounts/ClientLogin" +
   "?accountType=HOSTED_OR_GOOGLE" +
   "&Email=%s" +
   "&Passwd=%s" +
   "&service=cloudprint" +
   "&source=" + SERVICE_SOURCE_NAME;
  String authorizeURL = String.format(AUTHORIZE_URL_FORMAT, userId, password);
  String auth = "NOT_SET";
  InputStream inStream = null;
  BufferedReader reader = null;
  try {
   URL url = new URL(authorizeURL);
   HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
   inStream = connection.getInputStream();
   reader = new BufferedReader(new InputStreamReader(inStream));
   String buffer;
   StringBuilder stringBuilder = new StringBuilder();
   while ((buffer = reader.readLine()) != null) {
    String[] parts = buffer.split("=");
    if ("Auth".equalsIgnoreCase(parts[0])) {
      auth = parts[1];
      break;
    }
   }
  } catch (Exception e) {
   e.printStackTrace();
  } finally {
   try {
    if (reader != null)
     reader.close();
    if (inStream != null)
     inStream.close();
   } catch (Exception e) {
    e.printStackTrace();
   }
  }
  return auth;
 }
 private byte[] getBytes(File documentFile) {
  byte[] bytes = null;
  try {
   FileInputStream inputStream = new FileInputStream(documentFile);
   ByteArrayOutputStream baos = new ByteArrayOutputStream();
   byte[] buffer = new byte[4096];
   int n = inputStream.read(buffer);
   while (n >= 0) {
    baos.write(buffer, 0, n);
    n = inputStream.read(buffer);
   }
   inputStream.close();
   baos.flush();
   bytes = baos.toByteArray();
  } catch (Exception e) {
   e.printStackTrace();
  }
  return bytes;
 }
}

Posted by hedgehogjim

30+ years as a professional software dev, 17+ years developing mobile solutions, Pluralsight Author, Founder JWHH, LLC

2 Comments

  1. Thank you for your sharing!:)

    Reply

  2. Is there any method that we can send orientation potrait/landscape to the printer or a dimension?

    Reply

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s