Print Word document to specific Printer and Printer Tray

Hi,

After further investigation, we came up with the following solution that you can try out.

The C# version:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Printing;
using System.Xml;
using GemBox.Document;

static class Program
{
    static Program()
    {
        ComponentInfo.SetLicense("FREE-LIMITED-KEY");
    }

    static void Main()
    {
        // Get the dictionary of print queues that the print server hosts.
        var printQueues = WpfPrinterUtilities.GetPrintQueues();
        // Select the print queue.
        var printQueue = printQueues["HP Laserjet"];
        // Retrieve the dictionary of input bins from the print queue configuration.
        var inputBins = WpfPrinterUtilities.GetInputBins(printQueue);
        // Get the default print ticket associated with the print queue.
        var printTicket = printQueue.DefaultPrintTicket;
        // Modify a print ticket input bin value.
        printTicket = WpfPrinterUtilities.ModifyPrintTicket(printTicket, "psk:JobInputBin", inputBins["Tray 1"]);

        var document = DocumentModel.Load("Sample.docx");
        var options = new PrintOptions(printTicket.GetXmlStream());
        var printerName = "HP Laserjet";
        document.Print(printerName, options);
    }
}

public static class WpfPrinterUtilities
{
    /// <summary>
    /// Gets a dictionary of print queues where key is the print queue name, and value is the print queue object.
    /// </summary>
    /// <param name="printQueueTypes">EnumeratedPrintQueueTypes params array of the types of print queues being requested.</param>
    /// <returns>Dictionary of print queues where key is the print queue name, and value is the print queue object.</returns>
    public static Dictionary<string, PrintQueue> GetPrintQueues(params EnumeratedPrintQueueTypes[] printQueueTypes)
    {
        var server = new PrintServer();
        return server.GetPrintQueues(printQueueTypes).ToDictionary(pq => !string.IsNullOrEmpty(pq.ShareName) ? pq.ShareName : pq.Name);
    }

    /// <summary>
    /// Gets a dictionary of print queues where key is the print queue name, and value is the print queue object.
    /// </summary>
    /// <returns>Dictionary of print queues where key is the print queue name, and value is the print queue object.</returns>
    public static Dictionary<string, PrintQueue> GetPrintQueues()
    {
        var server = new PrintServer();
        return server.GetPrintQueues().ToDictionary(pq => !string.IsNullOrEmpty(pq.ShareName) ? pq.ShareName : pq.Name);
    }

    /// <summary>
    /// Reads print queue configuration xml to retrieve the current list of input bins.
    /// </summary>
    /// <param name="printQueue">The print queue to query.</param>
    /// <returns></returns>
    public static Dictionary<string, string> GetInputBins(PrintQueue printQueue)
    {
        Dictionary<string, string> inputBins = new Dictionary<string, string>();

        // Get the print queue PrintCapabilities.
        XmlDocument xmlDoc = new XmlDocument();
        using (MemoryStream stream = printQueue.GetPrintCapabilitiesAsXml())
            xmlDoc.Load(stream);

        // Read the JobInputBins out of the PrintCapabilities.

        // Create NamespaceManager and add PrintSchemaFrameWork-Namespace (should be on DocumentElement of the PrintTicket).
        // Prefix: psf NameSpace: xmlDoc.DocumentElement.NamespaceURI = "http://schemas.microsoft.com/windows/2003/08/printing/printschemaframework"
        XmlNamespaceManager manager = new XmlNamespaceManager(xmlDoc.NameTable);
        manager.AddNamespace(xmlDoc.DocumentElement.Prefix, xmlDoc.DocumentElement.NamespaceURI);

        // Select all job input bins.
        XmlNodeList nodeList = xmlDoc.SelectNodes("//psf:Feature[@name='psk:JobInputBin']/psf:Option/psf:Property", manager);

        // Load the Dictionary with the bin values and names. The names will be used to modify the print ticket if necessary.
        foreach (XmlNode node in nodeList)
            inputBins.Add(node.LastChild.InnerText, node.ParentNode.Attributes[0].Value);

        return inputBins;
    }

    /// <summary>
    /// Modifes a print ticket xml after updating a feature value.
    /// 
    /// Sample usage:
    /// Get Dictionary with Inputbins by calling the other method
    /// and get "value" for the desired inputbin you'd like to use...
    /// ...
    /// desiredTray is then something like "NS0000:SurpriseOption7" for example.
    /// defaultPrintTicket is the (Default)PrintTicket you want to modify from the PrintQueue for example
    /// PrintTicket myPrintTicket = WpfPrinterUtils.ModifyPrintTicket(defaultPrintTicket, "psk:JobInputBin", desiredTray);
    /// </summary>
    /// <param name="ticket"></param>
    /// <param name="featureName"></param>
    /// <param name="newValue"></param>
    /// <returns></returns>
    public static PrintTicket ModifyPrintTicket(PrintTicket ticket, string featureName, string newValue)
    {
        if (ticket == null)
            throw new ArgumentNullException("ticket");

        // Read Xml of the PrintTicket xml.
        XmlDocument xmlDoc = new XmlDocument();
        xmlDoc.Load(ticket.GetXmlStream());

        // Create NamespaceManager and add PrintSchemaFrameWork-Namespace hinzufugen (should be on DocumentElement of the PrintTicket).
        // Prefix: psf NameSpace: xmlDoc.DocumentElement.NamespaceURI = "http://schemas.microsoft.com/windows/2003/08/printing/printschemaframework"
        XmlNamespaceManager manager = new XmlNamespaceManager(xmlDoc.NameTable);
        manager.AddNamespace(xmlDoc.DocumentElement.Prefix, xmlDoc.DocumentElement.NamespaceURI);

        // Search node with desired feature we're looking for and set newValue for it
        string xpath = string.Format("//psf:Feature[@name='{0}']/psf:Option", featureName);
        XmlNode node = xmlDoc.SelectSingleNode(xpath, manager);
        if (node != null)
        {
            if (node.Attributes["name"].Value != newValue)
                node.Attributes["name"].Value = newValue;
        }

        // Create a new PrintTicket out of the XML.
        PrintTicket modifiedPrintTicket = null;
        using (MemoryStream stream = new MemoryStream())
        {
            xmlDoc.Save(stream);
            stream.Position = 0;
            modifiedPrintTicket = new PrintTicket(stream);
        }

        return modifiedPrintTicket;
    }
}

The VB.NET version:

Imports System
Imports System.Collections.Generic
Imports System.IO
Imports System.Linq
Imports System.Printing
Imports System.Xml
Imports GemBox.Document

Module Module1

    Sub New()
        ComponentInfo.SetLicense("FREE-LIMITED-KEY")
    End Sub

    Sub Main()

        ' Get the dictionary of print queues that the print server hosts.
        Dim printQueues = WpfPrinterUtilities.GetPrintQueues()
        ' Select the print queue.
        Dim printQueue = printQueues("HP Laserjet")
        ' Retrieve the dictionary of input bins from the print queue configuration.
        Dim inputBins = WpfPrinterUtilities.GetInputBins(printQueue)
        ' Get the default print ticket associated with the print queue.
        Dim printTicket = printQueue.DefaultPrintTicket
        ' Modify a print ticket input bin value.
        printTicket = WpfPrinterUtilities.ModifyPrintTicket(printTicket, "psk:JobInputBin", inputBins("Tray 1"))

        ' Print document using modified print ticket.
        Dim document = DocumentModel.Load("Sample.docx")
        Dim options = New PrintOptions(printTicket.GetXmlStream())
        Dim printerName = "HP Laserjet"
        document.Print(printerName, options)

    End Sub

End Module

Module WpfPrinterUtilities

    ''' <summary>
    ''' Gets a dictionary of print queues where key is the print queue name, and value is the print queue object.
    ''' </summary>
    ''' <param name="printQueueTypes">EnumeratedPrintQueueTypes params array of the types of print queues being requested.</param>
    ''' <returns>Dictionary of print queues where key is the print queue name, and value is the print queue object.</returns>
    Function GetPrintQueues(ParamArray printQueueTypes As EnumeratedPrintQueueTypes()) As Dictionary(Of String, PrintQueue)
        Dim server = New PrintServer()
        Return server.GetPrintQueues(printQueueTypes).ToDictionary(Function(pq) If(Not String.IsNullOrEmpty(pq.ShareName), pq.ShareName, pq.Name))
    End Function

    ''' <summary>
    ''' Gets a dictionary of print queues where key is the print queue name, and value is the print queue object.
    ''' </summary>
    ''' <returns>Dictionary of print queues where key is the print queue name, and value is the print queue object.</returns>
    Function GetPrintQueues() As Dictionary(Of String, PrintQueue)
        Dim server = New PrintServer()
        Return server.GetPrintQueues().ToDictionary(Function(pq) If(Not String.IsNullOrEmpty(pq.ShareName), pq.ShareName, pq.Name))
    End Function

    ''' <summary>
    ''' Reads print queue configuration xml to retrieve the current list of input bins.
    ''' </summary>
    ''' <param name="printQueue">The print queue to query.</param>
    ''' <returns></returns>
    Function GetInputBins(ByVal printQueue As PrintQueue) As Dictionary(Of String, String)
        Dim inputBins As Dictionary(Of String, String) = New Dictionary(Of String, String)()

        ' Get the print queue PrintCapabilities.
        Dim xmlDoc As XmlDocument = New XmlDocument()
        Using stream As MemoryStream = printQueue.GetPrintCapabilitiesAsXml()
            xmlDoc.Load(stream)
        End Using

        ' Read the JobInputBins out of the PrintCapabilities.

        ' Create NamespaceManager and add PrintSchemaFrameWork-Namespace (should be on DocumentElement of the PrintTicket).
        ' Prefix: psf NameSpace: xmlDoc.DocumentElement.NamespaceURI = "http://schemas.microsoft.com/windows/2003/08/printing/printschemaframework"
        Dim manager As XmlNamespaceManager = New XmlNamespaceManager(xmlDoc.NameTable)
        manager.AddNamespace(xmlDoc.DocumentElement.Prefix, xmlDoc.DocumentElement.NamespaceURI)

        ' Select all job input bins.
        Dim nodeList As XmlNodeList = xmlDoc.SelectNodes("//psf:Feature[@name='psk:JobInputBin']/psf:Option/psf:Property", manager)

        ' Load the Dictionary with the bin values and names. The names will be used to modify the print ticket if necessary.
        For Each node As XmlNode In nodeList
            inputBins.Add(node.LastChild.InnerText, node.ParentNode.Attributes(0).Value)
        Next

        Return inputBins
    End Function

    ''' <summary>
    ''' Modifes a print ticket xml after updating a feature value.
    ''' 
    ''' Sample usage:
    ''' Get Dictionary with Inputbins by calling the other method
    ''' and get "value" for the desired inputbin you'd like to use...
    ''' ...
    ''' desiredTray is then something like "NS0000:SurpriseOption7" for example.
    ''' defaultPrintTicket is the (Default)PrintTicket you want to modify from the PrintQueue for example
    ''' PrintTicket myPrintTicket = WpfPrinterUtils.ModifyPrintTicket(defaultPrintTicket, "psk:JobInputBin", desiredTray);
    ''' </summary>
    ''' <param name="ticket"></param>
    ''' <param name="featureName"></param>
    ''' <param name="newValue"></param>
    ''' <returns></returns>
    Function ModifyPrintTicket(ByVal ticket As PrintTicket, ByVal featureName As String, ByVal newValue As String) As PrintTicket
        If ticket Is Nothing Then Throw New ArgumentNullException("ticket")

        ' Read Xml of the PrintTicket xml.
        Dim xmlDoc As XmlDocument = New XmlDocument()
        xmlDoc.Load(ticket.GetXmlStream())

        ' Create NamespaceManager and add PrintSchemaFrameWork-Namespace hinzufugen (should be on DocumentElement of the PrintTicket).
        ' Prefix: psf NameSpace: xmlDoc.DocumentElement.NamespaceURI = "http://schemas.microsoft.com/windows/2003/08/printing/printschemaframework"
        Dim manager As XmlNamespaceManager = New XmlNamespaceManager(xmlDoc.NameTable)
        manager.AddNamespace(xmlDoc.DocumentElement.Prefix, xmlDoc.DocumentElement.NamespaceURI)

        ' Search node with desired feature we're looking for and set newValue for it
        Dim xpath As String = String.Format("//psf:Feature[@name='{0}']/psf:Option", featureName)
        Dim node As XmlNode = xmlDoc.SelectSingleNode(xpath, manager)
        If node IsNot Nothing Then
            If node.Attributes("name").Value <> newValue Then node.Attributes("name").Value = newValue
        End If

        ' Create a new PrintTicket out of the XML.
        Dim modifiedPrintTicket As PrintTicket = Nothing

        Using stream As MemoryStream = New MemoryStream()
            xmlDoc.Save(stream)
            stream.Position = 0
            modifiedPrintTicket = New PrintTicket(stream)
        End Using

        Return modifiedPrintTicket
    End Function

End Module

I hope this helps.

Regards,
Mario