Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Google Cloud Print using C#

I try to use Google Cloud Print using C#. Internet has just one example, who wrote Josh Goebel. I will not publish the complete example, here is the only method that sends a file to print:


public CloudPrintJob PrintDocument(string printerId, string title, byte[] document)
    {
        try
        {
            string authCode;
            if (!Authorize(out authCode))
                return new CloudPrintJob() { success = false };

            HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://www.google.com/cloudprint/submit?output=json");
            request.Method = "POST";

            string queryString =
                "printerid=" + HttpUtility.UrlEncode(printerId) +
                "&capabilities=" + HttpUtility.UrlEncode("") +
                "&contentType=" + HttpUtility.UrlEncode("application/pdf") +
                "&title=" + HttpUtility.UrlEncode(title) +
                "&content=" + HttpUtility.UrlEncode(Convert.ToBase64String(document));

            byte[] data = new ASCIIEncoding().GetBytes(queryString);

            request.Headers.Add("X-CloudPrint-Proxy", Source);
            request.Headers.Add("Authorization", "GoogleLogin auth=" + authCode);

            request.ContentType = "application/x-www-form-urlencoded";
            request.ContentLength = data.Length;

            Stream stream = request.GetRequestStream();
            stream.Write(data, 0, data.Length);
            stream.Close();

            // Get response
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            string responseContent = new StreamReader(response.GetResponseStream()).ReadToEnd();

            DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(CloudPrintJob));
            MemoryStream ms = new MemoryStream(Encoding.Unicode.GetBytes(responseContent));
            CloudPrintJob printJob = serializer.ReadObject(ms) as CloudPrintJob;

            return printJob;
        }
        catch (Exception ex)
        {
            return new CloudPrintJob() { success = false, message = ex.Message };
        }
    }

I run this code, then there is an interface of my printer, but print is not happening. The interface of my printer says that the pages to print 0, and the file size does not coincide with the one I sent to the printer.

Google Cloud Print says that the task(job) is successfully added, but at the interface of Google Cloud Print next to the name of the document display "Error".

I thought that might have a problem with HttpUtility.UrlEncode or Convert.ToBase64String, but I tried the reverse transformation - everything works.

Does anyone have any ideas?

like image 865
t0rch Avatar asked Oct 21 '11 07:10

t0rch


People also ask

How do I print from Google cloud?

To print from Gmail and Docs in your mobile web browser, open the dropdown from any document or email and tap "Print" to open the Google Cloud Print Dialog.

Can you still use Google Cloud Print?

Google Cloud Print was deprecated in January of 2021 and is no longer supported. Devices across all operating systems will not be able to print using Google Cloud Print. We recommend that you find an alternative print solution and migrate your print services.

Why did Google discontinue Google Cloud Print?

However, the service never left the Beta stage. Furthermore, through the years, Google and Android came up with other printing solutions, and many printer manufacturers created their own print features and apps. That is why in November 2019, Google announced their decision to shut down the tool.

How do I connect my printer to Google Cloud?

Select the printers you want to connect, and then click Add printer(s). The printer is now associated with your Google Account and connected to Google Cloud Print. You can print to this printer whenever you're signed in with the same Google Account on your Chromebook.


1 Answers

I appreciate this question is a little old now, but I've recently had to look at this for something I'm doing at work, and whilst the sample code posted started me off in the right direction it did take me quite a while to get it working completely.

The "list printers" function worked OK as described, but I couldn't get the Submit working properly, it just went straight to Error as the OP describes.

After profiling an actual submit request in Chrome using Fiddler, and looking at the PHP sample code on google's website, I discovered that the submit needed to be a multipart MIME message or it wouldn't work properly.

This is an example of the HTTP POST message which finally got the Submit operation working. Note that the gcp Printer Id needs to be passed in the request URL, and the data (whether it be a PDF, JPG or PNG) needs to be Base64 encoded and the correct mime type set (application/pdf, image/jpeg ...) for the "content" block (the last one in the list below):

POST http://www.google.com/cloudprint/submit?printerid=<printerid>&output=json HTTP/1.1
Host: www.google.com
Content-Length: 44544
X-CloudPrint-Proxy: Google-JS
Content-Type: multipart/form-data; boundary=----CloudPrintFormBoundaryqeq6g6ncj5v7

------CloudPrintFormBoundaryqeq6g6ncj5v7
Content-Disposition: form-data; name="capabilities"

{"capabilities":[{}]}
------CloudPrintFormBoundaryqeq6g6ncj5v7
Content-Disposition: form-data; name="contentType"

dataUrl
------CloudPrintFormBoundaryqeq6g6ncj5v7
Content-Disposition: form-data; name="title"

zodiac-pig-pic.jpg
------CloudPrintFormBoundaryqeq6g6ncj5v7
Content-Disposition: form-data; name="content"

data:image/jpeg;base64,JVBERi0xLjQKJeHp69MKMiAwIG...2NgolJUVPRg==
------CloudPrintFormBoundaryqeq6g6ncj5v7--

Things which tripped me up were that the Boundary value specified in the header has 2 less hyphens (--) than the actual usage of them (not obvious when you're staring at something wondering why it's not working!), the very last boundary instance has two additional hyphens at the end, and that I needed to get rid of the C# Expect100Continue header (by using request.ServicePoint.Expect100Continue = false).

UPDATE: Here is the complete code I wrote at the time, in case it helps anyone out.

using System;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Runtime.Serialization.Json;
using System.Text;
using GoogleCloudPrintServices.DTO;

namespace GoogleCloudPrintServices.Support
{
    public class GoogleCloudPrint
    {
        public string UserName { get; set; }
        public string Password { get; set; }
        public string Source { get; set; }

        private const int ServiceTimeout = 10000;

        public GoogleCloudPrint (String source)
        {
            Source = source;
        }

        public CloudPrintJob PrintDocument (string printerId, string title, byte[] document, String mimeType)
        {
            try
            {
                string authCode;
                if (!Authorize (out authCode))
                    return new CloudPrintJob { success = false };

                var b64 = Convert.ToBase64String (document);

                var request = (HttpWebRequest)WebRequest.Create ("http://www.google.com/cloudprint/submit?output=json&printerid=" + printerId);
                request.Method = "POST";

                // Setup the web request
                SetupWebRequest (request);

                // Add the headers
                request.Headers.Add ("X-CloudPrint-Proxy", Source);
                request.Headers.Add ("Authorization", "GoogleLogin auth=" + authCode);

                var p = new PostData ();

                p.Params.Add (new PostDataParam { Name = "printerid", Value = printerId, Type = PostDataParamType.Field });
                p.Params.Add (new PostDataParam { Name = "capabilities", Value = "{\"capabilities\":[{}]}", Type = PostDataParamType.Field });
                p.Params.Add (new PostDataParam { Name = "contentType", Value = "dataUrl", Type = PostDataParamType.Field });
                p.Params.Add (new PostDataParam { Name = "title", Value = title, Type = PostDataParamType.Field });

                p.Params.Add (new PostDataParam
                {
                    Name = "content",
                    Type = PostDataParamType.Field,
                    Value = "data:" + mimeType + ";base64," + b64
                });

                var postData = p.GetPostData ();
                Trace.WriteLine (postData);

                byte[] data = Encoding.UTF8.GetBytes (postData);

                request.ContentType = "multipart/form-data; boundary=" + p.Boundary;

                Stream stream = request.GetRequestStream ();
                stream.Write (data, 0, data.Length);
                stream.Close ();

                // Get response
                var response = (HttpWebResponse)request.GetResponse ();
                var responseContent = new StreamReader (response.GetResponseStream ()).ReadToEnd ();

                var serializer = new DataContractJsonSerializer (typeof (CloudPrintJob));
                var ms = new MemoryStream (Encoding.Unicode.GetBytes (responseContent));
                var printJob = serializer.ReadObject (ms) as CloudPrintJob;

                return printJob;
            }
            catch (Exception ex)
            {
                return new CloudPrintJob { success = false, message = ex.Message };
            }
        }

        public CloudPrinters Printers
        {
            get
            {
                var printers = new CloudPrinters ();

                string authCode;
                if (!Authorize (out authCode))
                    return new CloudPrinters { success = false };

                try
                {
                    var request = (HttpWebRequest)WebRequest.Create ("http://www.google.com/cloudprint/search?output=json");
                    request.Method = "POST";

                    // Setup the web request
                    SetupWebRequest (request);

                    // Add the headers
                    request.Headers.Add ("X-CloudPrint-Proxy", Source);
                    request.Headers.Add ("Authorization", "GoogleLogin auth=" + authCode);

                    request.ContentType = "application/x-www-form-urlencoded";
                    request.ContentLength = 0;

                    var response = (HttpWebResponse)request.GetResponse ();
                    var responseContent = new StreamReader (response.GetResponseStream ()).ReadToEnd ();

                    var serializer = new DataContractJsonSerializer (typeof (CloudPrinters));
                    var ms = new MemoryStream (Encoding.Unicode.GetBytes (responseContent));
                    printers = serializer.ReadObject (ms) as CloudPrinters;

                    return printers;
                }
                catch (Exception)
                {
                    return printers;
                }
            }
        }

        private bool Authorize (out string authCode)
        {
            var result = false;
            authCode = "";

            var queryString = String.Format ("https://www.google.com/accounts/ClientLogin?accountType=HOSTED_OR_GOOGLE&Email={0}&Passwd={1}&service=cloudprint&source={2}",
                UserName, Password, Source);
            var request = (HttpWebRequest)WebRequest.Create (queryString);

            // Setup the web request
            SetupWebRequest (request);

            var response = (HttpWebResponse)request.GetResponse ();
            var responseContent = new StreamReader (response.GetResponseStream ()).ReadToEnd ();

            var split = responseContent.Split ('\n');
            foreach (var s in split)
            {
                var nvsplit = s.Split ('=');
                if (nvsplit.Length == 2)
                {
                    if (nvsplit[0] == "Auth")
                    {
                        authCode = nvsplit[1];
                        result = true;
                    }
                }
            }

            return result;
        }

        private static void SetupWebRequest (HttpWebRequest webRequest)
        {
            // Get the details
            var appSettings = ConfigurationManager.AppSettings;

            // Create some credentials
            if (!String.IsNullOrWhiteSpace (appSettings["ProxyUsername"]))
            {
                var cred = new NetworkCredential (appSettings["ProxyUsername"], appSettings["ProxyPassword"],
                                                 appSettings["ProxyDomain"]);

                // Set the credentials
                webRequest.Credentials = cred;
                webRequest.Proxy = WebRequest.DefaultWebProxy;
                webRequest.Proxy.Credentials = cred;
            }

            // Set the timeout
            webRequest.Timeout = ServiceTimeout;
            webRequest.ServicePoint.ConnectionLeaseTimeout = ServiceTimeout;
            webRequest.ServicePoint.MaxIdleTime = ServiceTimeout;

            // Turn off the 100's
            webRequest.ServicePoint.Expect100Continue = false;
        }
    }
}



using System.Runtime.Serialization;

namespace GoogleCloudPrintServices.DTO
{
    [DataContract]
    public class CloudPrinter
    {
        [DataMember (Order = 0)]
        public string id { get; set; }

        [DataMember (Order = 1)]
        public string name { get; set; }

        [DataMember (Order = 2)]
        public string description { get; set; }

        [DataMember (Order = 3)]
        public string proxy { get; set; }

        [DataMember (Order = 4)]
        public string status { get; set; }

        [DataMember (Order = 5)]
        public string capsHash { get; set; }

        [DataMember (Order = 6)]
        public string createTime { get; set; }

        [DataMember (Order = 7)]
        public string updateTime { get; set; }

        [DataMember (Order = 8)]
        public string accessTime { get; set; }

        [DataMember (Order = 9)]
        public bool confirmed { get; set; }

        [DataMember (Order = 10)]
        public int numberOfDocuments { get; set; }

        [DataMember (Order = 11)]
        public int numberOfPages { get; set; }
    }
}



using System.Collections.Generic;
using System.Runtime.Serialization;

namespace GoogleCloudPrintServices.DTO
{
    [DataContract]
    public class CloudPrinters
    {
        [DataMember (Order = 0)]
        public bool success { get; set; }

        [DataMember (Order = 1)]
        public List<CloudPrinter> printers { get; set; }
    }
}



using System.Runtime.Serialization;

namespace GoogleCloudPrintServices.DTO
{
    [DataContract]
    public class CloudPrintJob
    {
        [DataMember (Order = 0)]
        public bool success { get; set; }

        [DataMember (Order = 1)]
        public string message { get; set; }
    }
}

using System;
using System.Collections.Generic;
using System.Text;

namespace GoogleCloudPrintServices.Support
{
    internal class PostData
    {
        private const String CRLF = "\r\n";

        public string Boundary { get; set; }
        private List<PostDataParam> _mParams;

        public List<PostDataParam> Params
        {
            get { return _mParams; }
            set { _mParams = value; }
        }

        public PostData ()
        {
            // Get boundary, default is --AaB03x
            Boundary = "----CloudPrintFormBoundary" + DateTime.UtcNow;

            // The set of parameters
            _mParams = new List<PostDataParam> ();
        }

        public string GetPostData ()
        {
            var sb = new StringBuilder ();
            foreach (var p in _mParams)
            {
                sb.Append ("--" + Boundary).Append (CRLF);

                if (p.Type == PostDataParamType.File)
                {
                    sb.Append (string.Format ("Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"", p.Name, p.FileName)).Append (CRLF);
                    sb.Append ("Content-Type: ").Append (p.FileMimeType).Append (CRLF);
                    sb.Append ("Content-Transfer-Encoding: base64").Append (CRLF);
                    sb.Append ("").Append (CRLF);
                    sb.Append (p.Value).Append (CRLF);
                }
                else
                {
                    sb.Append (string.Format ("Content-Disposition: form-data; name=\"{0}\"", p.Name)).Append (CRLF);
                    sb.Append ("").Append (CRLF);
                    sb.Append (p.Value).Append (CRLF);
                }
            }

            sb.Append ("--" + Boundary + "--").Append (CRLF);

            return sb.ToString ();
        }
    }

    public enum PostDataParamType
    {
        Field,
        File
    }

    public class PostDataParam
    {
        public string Name { get; set; }
        public string FileName { get; set; }
        public string FileMimeType { get; set; }
        public string Value { get; set; }
        public PostDataParamType Type { get; set; }

        public PostDataParam ()
        {
            FileMimeType = "text/plain";
        }
    }
}
like image 62
deevodavis Avatar answered Oct 04 '22 17:10

deevodavis