printer++ Tutorial

Scenario:

www.weather.com provides a service where you can put in your zipcode and it will return the weather for the day. The temperature is in Fahrenheit and I’d like to get it in Celsius. I’d also like to have it emailed to anyone I want.
  1. Goto www.weather.com
    Printer++
  2. Enter New York in the search field and click on SEARCH button.
    Printer++
  3. Site will return the weather info in Fahrenheit.
    Printer++

Install printer++

  1. Install printer++ with printer name: "EmailWeatherInCelcius".
    Printer++

Development

  1. Open Microsoft Visual Studio
  2. Create a new Class Library Project and name it "EmailWeatherInCelcius".
    Printer++
  3. Add Reference to printer++ SDK. Right-click on References and select "Add Reference".
    Printer++
  4. Go to "Browse" tab and navigate to the printer++ installation folder. (C:\PrinterPlusPlus). Select "PrinterPlusPlusSDK.dll". Click "OK" button.
    Printer++
    Printer++
  5. Create a new Processor Class. Select "Project" menu. Select "Add Class".
    Printer++
  6. In the "Add New Item" modal dialog. Select Class. And put "Processor" in the Name field. Click "Add" button.
    Printer++
  7. Processor.cs is now added to the solution. This will be the main class that will handle all printer++ Processor code.
    Printer++
  8. Double click on Processor.cs to open the code window.
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace EmailWeatherInCelcius
    {
        class Processor
        {
        }
    }
                
  9. Modify the Processor class to inherit from "PrinterPlusPlusSDK.IProcessor" and implement the PrinterPlusPlusSDK.ProcessResult Process function.
    namespace EmailWeatherInCelcius
    {
        class Processor: PrinterPlusPlusSDK.IProcessor
        {
            public PrinterPlusPlusSDK.ProcessResult Process(string key, string psFilename)
            {
                throw new NotImplementedException();
            }
        }
    }
            

Building the Processor function

The Process function has two parameters: string key and string psFilename. Key is the name of the printer that initiated the process, while psFilename is the filename of the generated PostScript file.
From this method, we can do almost anything we want to the PostScript file. For this demo, we will be doing the following:
  • Convert the Postscript file to Text
  • Read the Text file to extract the values we want and convert the extracted values from Fahrenheit to Celcius.
  • Prompt user to email recipient.
  • Send email to recipients.
Click here to download the sample PostScript file generated by printer++.

Now, let's start building the Processor function.
  1. Convert the Postscript file to Text
    For this conversion, we will be using Postscript to Text Converter from verydoc.
    Download and unzip the converter to "C:\ps2txt"

    Note that you can also use other conversion tools like Ghostscript and Xpdf.

    We can now add a new method that will call the converter to convert the ps file:
    public static string ConvertPsToTxt(string psFilename, string txtFilename)
    {
        var retVal = string.Empty;
        var errorMessage = string.Empty;
        var command = "C:\\ps2txt\\ps2txt.exe";
        var args = string.Format("-nolayout \"{0}\" \"{1}\"", psFilename, txtFilename);
        retVal = Shell.ExecuteShellCommand(command, args, ref errorMessage);
        return retVal;
    }
                
    Then, we'll call the new function in our Process method:
    //Convert PS to Text
    var txtFilename = System.IO.Path.GetTempFileName();
    ConvertPsToTxt(psFilename, txtFilename);
                
    Click here to download the converted Text file.


  2. Read the Text file to extract the values we want and convert the extracted values from Fahrenheit to Celcius
    Now let's analyze the converted text file, based from this conversion, we can create a logic on how to extract the values that we need.
    New York Weather
    Right Now
    Updated: Aug 29, 2012, 3:25am EDT
    
    73°F
    Clear
            
    We can use the line 47 with "Farming" value as a marker before we begin extracting the values.
    To extract the following, let's create a new function:
    public ExtractedValue ProcessTextFile(string txtFilename)
    {
        var values = new ExtractedValue();      //Create the extracted values placeholders
        var reachedMarker = false;
        //Read the text file
        using (System.IO.StreamReader sr = System.IO.File.OpenText(txtFilename))
        {
            while (sr.Peek() > -1)
            {
                var currentLine = sr.ReadLine().Trim();
    
                //Skip whitespaces
                if (string.IsNullOrWhiteSpace(currentLine))
                    continue;
    
                //Checked if we've reached the marker to begin extraction
                if (reachedMarker == true)
                {
                    //Get Title value
                    if (string.IsNullOrWhiteSpace(values.Title))
                    {
                        values.Title = currentLine;
                        continue;
                    }
                    //Skip Right Now value
                    if (currentLine.ToLower() == "right now")
                        continue;
                    //Get UpdatedDateTime value
                    if (string.IsNullOrWhiteSpace(values.UpdatedDateTime))
                    {
                        values.UpdatedDateTime = currentLine;
                        continue;
                    }
                    //Get TemperatureFahrenheit and convert value to Celcius
                    if (values.TemperatureFahrenheit == 0)
                    {
                        values.TemperatureFahrenheit = Convert.ToDouble(currentLine.Substring(0, currentLine.Length - 2));
                        values.TemperatureCelcius = ((values.TemperatureFahrenheit - 32) / 1.8);
                        continue;
                    }
                    //Get Forecast value
                    if (string.IsNullOrWhiteSpace(values.Forecast))
                    {
                        values.Forecast = currentLine;
                        break;
                    }
                }
                //Mark farming so we can begin extracting data
                if (currentLine.ToLower() == "farming")
                    reachedMarker = true;
            }
        }
        return values;      //Return the extracted values
    }
    
    public class ExtractedValue
    {
        public string Title { get; set; }
        public string UpdatedDateTime { get; set; }
        public double TemperatureFahrenheit { get; set; }
        public double TemperatureCelcius { get; set; }
        public string Forecast { get; set; }
    }
            
    Then we'll call the new function in our Process method:
    //Process the converted Text File
    var extractedValue = ProcessTextFile(txtFilename);
            
  3. Prompt user to email recipient
    Next is we need to ask the user to enter the email address of the recipient. We will need to add Reference to "Microsoft.VisualBasic" since we will be using the inputbox just for this demo. We can add the code below in our Process method:
    //Ask user for recipeint's email
    var recipients = Microsoft.VisualBasic.Interaction.InputBox("Enter email address of recipient.");
                
  4. Send email to recipients After all the conversion and processing, we will now send an email to the email address that the user entered. You can see more details on sending email in .net from this MSDN site
    For our demo, we can create a new function based from the msdn sample code:
    public void SendEmail(ExtractedValue values, string recipient)
    {
        try
        {
            var smtpHost = "smtp.mail.com";     //Change this to correct SMTP server
            var sender = "info@printerplusplus.com";    //Change this to the email address of the sender
            SmtpClient client = new SmtpClient(smtpHost);
            
            client.UseDefaultCredentials = false;
            client.Credentials = new System.Net.NetworkCredential("username", "password");  //Change this to the username and password for your SMTP server
    
            MailAddress from = new MailAddress(sender);
            MailAddress to = new MailAddress(recipient);
            MailMessage message = new MailMessage(from, to);
            message.Body = values.Title;
            message.Body += Environment.NewLine;
            message.Body += values.UpdatedDateTime;
            message.Body += Environment.NewLine;
            message.Body += values.TemperatureCelcius;
            message.Body += Environment.NewLine;
            message.Body += values.Forecast;
            message.BodyEncoding = System.Text.Encoding.UTF8;
            message.Subject = "Printer++ EmailWeatherInCelcius";
            message.SubjectEncoding = System.Text.Encoding.UTF8;
            client.Send(message);
            message.Dispose();
        }
        catch (Exception ex)
        {
            //Error occured while sending email. Add code to handle error.
        }
    }
                
    Then we'll call the new function in our Process method:
    //Send email if user entered an email address
    if (!string.IsNullOrWhiteSpace(recipients))
    {
        SendEmail(extractedValue, recipients);
    }
                
  5. Returning results to printer++. After all custom code, the Process method needs to return a ProcessResult object. This object encapsulates messages that you want to send back to printer++ like success or error messages.
    return new ProcessResult();
            
That's it. We are have just completed creating a processor for printer++.
Below is the whole code of the Processor.cs
using System;
using System.Net.Mail;
using PrinterPlusPlusSDK;

namespace EmailWeatherInCelcius
{
    public class Processor : PrinterPlusPlusSDK.IProcessor
    {
        public PrinterPlusPlusSDK.ProcessResult Process(string key, string psFilename)
        {
            //Convert PS to Text
            var txtFilename = System.IO.Path.GetTempFileName();
            ConvertPsToTxt(psFilename, txtFilename);

            //Process the converted Text File
            var extractedValue = ProcessTextFile(txtFilename);

            //Ask user for recipeint's email
            var recipients = Microsoft.VisualBasic.Interaction.InputBox("Enter email address of recipient.");

            //Send email if user entered an email address
            if (!string.IsNullOrWhiteSpace(recipients))
            {
                SendEmail(extractedValue, recipients);
            }

            return new ProcessResult();
        }

        public static string ConvertPsToTxt(string psFilename, string txtFilename)
        {
            var retVal = string.Empty;
            var errorMessage = string.Empty;
            var command = "C:\\ps2txt\\ps2txt.exe";
            var args = string.Format("-nolayout \"{0}\" \"{1}\"", psFilename, txtFilename);
            retVal = Shell.ExecuteShellCommand(command, args, ref errorMessage);
            return retVal;
        }

        public ExtractedValue ProcessTextFile(string txtFilename)
        {
            var values = new ExtractedValue();      //Create the extracted values placeholders
            var reachedMarker = false;
            //Read the text file
            using (System.IO.StreamReader sr = System.IO.File.OpenText(txtFilename))
            {
                while (sr.Peek() > -1)
                {
                    var currentLine = sr.ReadLine().Trim();

                    //Skip whitespaces
                    if (string.IsNullOrWhiteSpace(currentLine))
                        continue;

                    //Checked if we've reached the marker to begin extraction
                    if (reachedMarker == true)
                    {
                        //Get Title value
                        if (string.IsNullOrWhiteSpace(values.Title))
                        {
                            values.Title = currentLine;
                            continue;
                        }
                        //Skip Right Now value
                        if (currentLine.ToLower() == "right now")
                            continue;
                        //Get UpdatedDateTime value
                        if (string.IsNullOrWhiteSpace(values.UpdatedDateTime))
                        {
                            values.UpdatedDateTime = currentLine;
                            continue;
                        }
                        //Get TemperatureFahrenheit and convert value to Celcius
                        if (values.TemperatureFahrenheit == 0)
                        {
                            values.TemperatureFahrenheit = Convert.ToDouble(currentLine.Substring(0, currentLine.Length - 2));
                            values.TemperatureCelcius = ((values.TemperatureFahrenheit - 32) / 1.8);
                            continue;
                        }
                        //Get Forecast value
                        if (string.IsNullOrWhiteSpace(values.Forecast))
                        {
                            values.Forecast = currentLine;
                            break;
                        }
                    }
                    //Mark farming so we can begin extracting data
                    if (currentLine.ToLower() == "farming")
                        reachedMarker = true;
                }
            }
            return values;      //Return the extracted values
        }

        public class ExtractedValue
        {
            public string Title { get; set; }
            public string UpdatedDateTime { get; set; }
            public double TemperatureFahrenheit { get; set; }
            public double TemperatureCelcius { get; set; }
            public string Forecast { get; set; }
        }

        public void SendEmail(ExtractedValue values, string recipient)
        {
            try
            {
                var smtpHost = "smtp.mail.com";     //Change this to correct SMTP server
                var sender = "info@printerplusplus.com";    //Change this to the email address of the sender
                SmtpClient client = new SmtpClient(smtpHost);
        
                client.UseDefaultCredentials = false;
                client.Credentials = new System.Net.NetworkCredential("username", "password");  //Change this to the username and password for your SMTP server

                MailAddress from = new MailAddress(sender);
                MailAddress to = new MailAddress(recipient);
                MailMessage message = new MailMessage(from, to);
                message.Body = values.Title;
                message.Body += Environment.NewLine;
                message.Body += values.UpdatedDateTime;
                message.Body += Environment.NewLine;
                message.Body += values.TemperatureCelcius;
                message.Body += Environment.NewLine;
                message.Body += values.Forecast;
                message.BodyEncoding = System.Text.Encoding.UTF8;
                message.Subject = "Printer++ EmailWeatherInCelcius";
                message.SubjectEncoding = System.Text.Encoding.UTF8;
                client.Send(message);
                message.Dispose();
            }
            catch (Exception ex)
            {
                //Error occured while sending email. Add code to handle error.
            }
        }
    }
}
    
Click here to download the tutorial project.

Deploy and register Processor to printer++

After compiling the processor dll, we will now need to deploy it to printer++ and register it as a processor.
  1. Copy the compiled "EmailWeatherInCelcius.dll" to the printer++ installation folder (C:\PrinterPlusPlus).
  2. Open "PrinterPlusPlus.exe.config" in a text editor.
  3. Add an entry in the Processors section:
    <add key="EmailWeatherInCelcius" value="EmailWeatherInCelcius.Processor, EmailWeatherInCelcius, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
            
    The key is the identifier for the processor while the value is the AssemblyQualifiedName of the processor dll.
    You can find more details about the AssemblyQualifiedName in this MSDN site
Your PrinterPlusPlus.exe.config file should now look like this:
<?xml version="1.0"?>
<configuration>
  <configSections>
    <section name="processors" type="System.Configuration.NameValueSectionHandler"/>
  </configSections>
  <processors>
    <add key="EmailWeatherInCelcius" value="EmailWeatherInCelcius.Processor, EmailWeatherInCelcius, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
  </processors>
  <appSettings>
    <add key="DefaultProcessor" value=""/>
    <add key="DebugMode" value="1"/>
  </appSettings>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
  </startup>
</configuration>
    

Testing the new processor