Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GetPrintCapabilities will not return all page sizes

Tags:

c#

xml

printing

wpf

PrintCapabilities printCapabilites = SelectedPrinter.GetPrintCapabilities(); IEnumerable pagesizeList = printCapabilites.PageMediaSizeCapability;

The above code does not list all the page sizes that the printer driver supports and this is my problem.

As an example if you use the Microsoft XPS printer driver you will find that pagesizeList (above) will be missing some page sizes. "Letter Small" is one of the missing page sizes (MS Word will successfully list this page size though).

As a quick check I dumped the Printer capabilities to xml as below:

long gpCLen = _selectedPrinter.GetPrintCapabilitiesAsXml().Length;
FileStream fs = File.OpenWrite(@"c:\test.txt");
MemoryStream ms = _selectedPrinter.GetPrintCapabilitiesAsXml();
byte[] b = new byte[gpCLen];
ms.Read(b, 0, (int)gpCLen);
fs.Write(b, 0, (int)gpCLen);
fs.Close();

The PageMediaSize node in the xml file produced does indeed have all the page sizes, AND the missing ones.

Displayed pages seem to have their name start psk:

<psf:Option name="psk:ISOA4" constrained="psk:None">

but undisplayed pages seem to have:

<psf:Option name="ns0000:LETTERSMALL" constrained="psk:None">

Undisplayed Epson print driver pages are similar:

<psf:Option name="epns200:IndexCard5x8" constrained="psk:None">

Basically a page whos name starts 'psk' PageMediaSizeCapability displays, but if it's manufacture custom ie epns200, ns0000, it doesn't list. Any ideas why and how to fix this please? The nodes/Pages are there but PageMediaSizeCapability doesn't like it!

Thanks in advance

EDIT:

As MS state 'A PrintCapabilities object is an easy-to-work-with representation of a certain type of XML document called a PrintCapabilities document.' But the document has more info than the object Full description

like image 657
Anthony Avatar asked Oct 15 '22 04:10

Anthony


2 Answers

you have to save the paper name that you read from PrintCapabilities xml, and the use it to create a PrintTicket xml (PrintTicket has a constructor that accepts a xml stream), and then use the PrintTicket. Here is an example of PrintTicket XML (ns0000:User0000000257 is hte name of a custom paper size that i created):

    <psf:PrintTicket xmlns:psf="http://schemas.microsoft.com/windows/2003/08/printing/printschemaframework"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
           xmlns:psk="http://schemas.microsoft.com/windows/2003/08/printing/printschemakeywords"
           xmlns:ns0000="http://schemas.microsoft.com/windows/printing/oemdriverpt/Samsung_CLP-310 Series/5.1.2600.2180/" version="1">
  <psf:Feature name="psk:PageMediaSize">
    <psf:Option name="ns0000:User0000000257"></psf:Option>
  </psf:Feature>
</psf:PrintTicket>
like image 117
MariusC Avatar answered Oct 31 '22 12:10

MariusC


Even though this was an older thread, it was helpful in pointing us in the right direction when we needed to read and update custom properties.

The code below was adapted from this thread: XPS Printing, Tray selection and InputBinCapability (InputBin) = Problem: http://www.windows-tech.info/14/29c7cf575646cb39.php. Jo0815's answer at the bottom had most of what you see below, especially the XPath expressions that pointed us in the right direction.

Also, update the PrintQueue.UserPrintTicket with your changes, NOT the DefaultPrintTicket.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Printing;
using System.Text;
using System.Windows;
using System.Xml;

// Adapted
// From: XPS Printing, Tray selection and InputBinCapability (InputBin) = Problem
// Link: http://www.windows-tech.info/14/29c7cf575646cb39.php - last answer at bottom by Jo0815

namespace WpfApplication1
{
    public static class WpfPrinterUtilities
    {
        #region GetPrintQueues

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

        #endregion

        #region GetInputBins

        /// <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 = null;
            using (MemoryStream stream = printQueue.GetPrintCapabilitiesAsXml())
            {
                // Read the JobInputBins out of the PrintCapabilities.
                xmlDoc = new XmlDocument();
                xmlDoc.Load(stream);
            }

            // 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;
        }

        #endregion

        #region ModifyPrintTicket

        /// <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>
        /// <param name="printQueueName">Optional - If provided, a file is created with the print ticket xml. Useful for debugging.</param>
        /// <param name="folder">Optional - If provided, the path for a file is created with the print ticket xml. Defaults to c:\. Useful for debugging.</param>
        /// <param name="displayMessage">Optional - True to display a dialog with changes. Defaults to false. Useful for debugging.</param>
        /// <returns></returns>
        public static PrintTicket ModifyPrintTicket(PrintTicket ticket, string featureName, string newValue, string printQueueName = null, string folder = null, bool displayMessage = false)
        {
            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)
                {
                    if (displayMessage)
                    {
                        System.Windows.MessageBox.Show(string.Format("OldValue: {0}, NewValue: {1}", node.Attributes["name"].Value, newValue), "Input Bin");
                    }
                    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);
            }

            // For testing purpose save the print ticket to a file.
            if (!string.IsNullOrWhiteSpace(printQueueName))
            {
                if (string.IsNullOrWhiteSpace(folder))
                {
                    folder = "c:\\";
                }
                // Colons are not valid in a file name.
                newValue = newValue.Replace(':', ';');
                printQueueName = string.Format("{0} PrintTicket {1}.xml", Path.Combine(folder, printQueueName), newValue);
                if (File.Exists(printQueueName))
                {
                    File.Delete(printQueueName);
                }
                if (!Directory.Exists(Path.GetDirectoryName(printQueueName)))
                {
                    Directory.CreateDirectory(Path.GetDirectoryName(printQueueName));
                }
                using (FileStream stream = new FileStream(printQueueName, FileMode.CreateNew, FileAccess.ReadWrite))
                {
                    modifiedPrintTicket.GetXmlStream().WriteTo(stream);
                }
            }

            return modifiedPrintTicket;
        }

        #endregion
    }
}
like image 42
j2associates Avatar answered Oct 31 '22 11:10

j2associates