Отправить документ на принтер с C #

Мне дали требование, чтобы внутреннее веб-приложение могло прозрачно отправлять документы на принтер. Идея состоит в том, что пользователь может выбрать ближайший к нему принтер, а веб-приложение будет отправлять задания на печать на выбранный принтер.

Первым принтером, который нам нужно развернуть, является Canons, поэтому у меня такой вопрос: как мне отправить документ для печати по сети в конкретный Canon? Тип Cannon, о котором идет речь, - iR5570, а документы, о которых будет сказано, будут в основном в формате Word и PDF.

В настоящее время я прохожу свой путь через ужасный сайт разработчиков Canon, предназначенный только для IE, но я как бы надеюсь, что кто-то может указать мне правильное направление или указать на стороннюю сборку :)


person Chris Canal    schedule 20.10.2008    source источник


Ответы (7)


Ключевая фраза в этом вопросе - «веб-приложение».

В обычном веб-приложении, использующем только HTML + Javascript поверх HTTP, вы не можете просто отправить документ прямо на принтер. Это одна из причин, по которой существуют веб-браузеры, и без этой функции каждый принтер будет собирать такой же мусор, что и общедоступный факс.

Итак, вам нужен какой-то обходной путь. Один из вариантов - создать общий плагин, такой как flash, silverlight, java-апплет или даже что-то вроде greasemonkey. Другой - это настраиваемый плагин, такой как размещенный элемент управления winforms или настраиваемое расширение браузера.

Вам очень повезло, поскольку похоже, что вы полностью контролируете (или знаете) среду развертывания, и что эта среда достаточно однородна. Это означает, что у вас есть дополнительная опция, которую другие начали изучать. Если вы можете установить все принтеры в своей среде на веб-сервер, тогда будет довольно просто использовать встроенные классы принтеров .Net (в пространстве имен System.Drawing.Printing), чтобы перечислить эти принтеры, либо показать их пользователю, чтобы они могли выберите или сохраните какую-либо таблицу сопоставления IP-адреса принтера, а затем распечатайте его прямо на этот принтер из веб-приложения. Обратите внимание, что для этой схемы может потребоваться, чтобы ваше приложение работало на более высоком уровне доверия, чем требовалось бы в противном случае.

Теперь дело доходит до фактической печати ваших PDF-файлов и текстовых документов. Для acrobat проверьте эту ссылку:
http://web.archive.org/web/20090206162133/http://support.adobe.com/devsup/devsup.nsf/docs/52080.htm (Wayback machine)
Обратите внимание, что он немного устарел, но я считаю концепция все еще в силе. Вам придется поэкспериментировать с некоторыми из них, чтобы убедиться, что они работают должным образом.

Что касается Word, я обычно не являюсь поклонником взаимодействия / автоматизации Office в веб-приложении. Но в этом случае вы не редактируете никакие документы: просто загружаете их на время, необходимое для печати. И тот факт, что вы полагаетесь на другой дефицитный ресурс (принтеры), должен удерживать его от масштабирования сверх того, с чем может справиться ваш веб-сервер. Так что у вас может быть тот редкий случай, когда автоматизация Office в веб-проекте имеет смысл.

person Joel Coehoorn    schedule 20.10.2008
comment
У меня есть полный контроль над средой развертывания, это приложение для внутренней интрасети. Взаимодействие / автоматизация Office ужасны, и хотя документы генерируются C #, мы используем Aspose.Words. Я могу согласиться с использованием interop.automation для печати и попробую. - person Chris Canal; 20.10.2008
comment
Если у вас уже есть Aspose, вы можете проверить, поддерживает ли он печать. - person Joel Coehoorn; 20.10.2008
comment
Но определенно согласен, что в 99% случаев офисное взаимодействие ужасно. - person Joel Coehoorn; 20.10.2008
comment
Я идиот, надо было проверить документацию по Aspose! Спасибо - person Chris Canal; 20.10.2008
comment
Aspose.Words поддерживает печать документов Word (он отображает документы почти так же, как MS Word, но без MS Word), но эта функция могла быть недоступна в октябре 2008 года, когда вы задали этот вопрос. - person romeok; 06.03.2011
comment
Ссылка на PDF-документацию не работает. У кого-нибудь есть ссылка или резюме? - person k-den; 23.10.2013
comment
@ k-den, спасибо, что подняли голову. Ссылка на Wayback Machine находится здесь: http://web.archive.org/web/20090206162133/http://support.adobe.com/devsup/devsup.nsf/docs/52080.htm - person Joel Coehoorn; 23.10.2013

Многие принтеры и многофункциональные устройства сегодня поддерживают печать PDF-файлов напрямую, это может решить одну из ваших проблем. Просто отправьте PDF-файл на принтер. Фактически, некоторые даже поддерживают отправку URL-адреса, и затем принтер берет документ и распечатывает его. Lexmark точно так же поступает, и я думаю, что некоторые другие поставщики тоже. Это по-прежнему означает, что вам нужно иметь дело с документом Word. Word 2007 поддерживает PDF (с помощью add- в установленном от Microsoft), и я с большим успехом использовал эту функцию программно на C #.

Вот код для этого:

Microsoft.Office.Interop.Word.ApplicationClass msWord = new Microsoft.Office.Interop.Word.ApplicationClass();

object paramUnknown = Type.Missing;
object missing = Type.Missing;
object paramSaveChangesNo = Microsoft.Office.Interop.Word.WdSaveOptions.wdDoNotSaveChanges;
//object paramFonts = Microsoft.Office.Interop.Word.wde
object paramFormatPDF = Microsoft.Office.Interop.Word.WdSaveFormat.wdFormatPDF;
object paramTrue = true;
object paramReadOnly = true;  
object sourceDoc = @"c:\input.doc"                              
object target = @"c:\output.pdf";

msWord.Visible = false;

//open .doc
msWord.Documents.Open(ref sourceDoc, ref paramUnknown, ref paramReadOnly, ref paramUnknown, ref paramUnknown, ref paramUnknown, ref paramUnknown, ref paramUnknown, ref paramUnknown, ref paramUnknown, ref paramUnknown, ref paramUnknown, ref paramUnknown, ref paramUnknown, ref paramUnknown, ref paramUnknown);

//so it won't show on the taskbar
msWord.Application.Visible = false;
msWord.WindowState = Microsoft.Office.Interop.Word.WdWindowState.wdWindowStateMinimize;

//save .doc to new target name and format
msWord.ActiveDocument.SaveAs(ref targetDoc, ref paramFormatPDF, ref paramUnknown, ref paramUnknown, ref paramUnknown, ref paramUnknown, ref paramUnknown, ref paramTrue, ref paramUnknown, ref paramUnknown, ref paramUnknown, ref paramUnknown, ref paramUnknown, ref paramUnknown, ref paramUnknown, ref paramUnknown);

msWord.ActiveDocument.Close(ref missing, ref missing, ref missing);

msWord.Quit(ref paramSaveChangesNo, ref paramUnknown, ref paramUnknown);

Наконец, если ваше устройство не поддерживает печать PDF-файлов, вы можете использовать Ghostscript или другие инструменты для преобразования вашего PDF-файла в PS или даже PCL. Не лучший вариант, поскольку это означает запуск небольшого количества неуправляемого кода или, в худшем случае, выполнение оболочки и выполнение командной строки GS, при этом, как говорится, в настоящее время мы делаем это в одном из наших веб-приложений, и это работает хорошо. Кстати, мы делаем это не для печати, а, скорее, для объединения нескольких PDF-файлов вместе, но в конечном итоге это будет работать так же.

person Douglas Anderson    schedule 21.10.2008

Документация PrintDocument содержит подробный пример печати из C #. Имя принтера должно указывать на локальный принтер или общий принтер. См. программную печать на pdf-принтер для печати документов PDF. .

person gimel    schedule 20.10.2008
comment
В первом примере демонстрируется текстовый (строковый) документ, но класс PrintDocument использует для рисования объект Graphics. Это означает, что вы можете использовать всю мощь пространств имен System.Drawing (GDI +). - person gimel; 31.08.2012
comment
ах, понятно .. но я полагаю, что проприетарный формат (например, MS Word) не поддерживается этим классом? - person Louis Rhys; 31.08.2012
comment
@ louis-rhys Здесь мы говорим о НИЗКОМ УРОВНЕ (даже на уровне пикселей) - намного ниже форматов документов. - person gimel; 31.08.2012

Что-то должно переводить ваши документы Word и PDF во что-то, что понимает принтер. Обычно для первого используется сам Word, а для второго - какая-то программа для просмотра PDF-файлов. Затем этим программам необходимо указать, на какой принтер отправлять выходные данные.

Один из возможных способов - сохранить документы как файлы Postscript. Затем они могут быть отправлены на принтер с поддержкой Postscript непосредственно из приложения C #. Вероятно, это был бы самый простой способ сделать это.

person Jeffrey L Whitledge    schedule 20.10.2008

Возможно, стоит 5 минут, чтобы проверить возможности службы отчетов sql. Раньше я использовал его для рендеринга PDF прямо для печати.

http://blogs.msdn.com/bryanke/articles/71491.aspx

person MBoy    schedule 20.10.2008

Если типы документов, о которых идет речь, известны Windows (как DOC и PDF), можете ли вы использовать для этого глаголы Windows?

Пример PDF Codeproject: автоматическое преобразование PDF с помощью бесплатных принтеров PDF995 и FreePDF_XP MSDN: глаголы и ассоциации файлов

person Craig.Nicol    schedule 20.10.2008

Этот код отлично работает. Он использует сам Adobe Reader для печати.

Подсказки по использованию 1 - не забудьте указать свой собственный путь установки для Adobe Reader 2 - Получите имя принтера из свойств принтера, с которым вы хотите распечатать

используйте класс следующим образом:

PdfFilePrinter p = new PdfFilePrinter();

p.PdfFileName = @"c:\1.pdf";
p.Print();
p.PdfFileName = @"c:\2.pdf";
p.Print();

и класс:

public class PdfFilePrinter
{
    /// <summary>
    /// Initializes a new instance of the <see cref="PdfFilePrinter"/> class.
    /// </summary>
    public PdfFilePrinter()
    {
        adobeReaderPath = @"C:\Program Files\Adobe\Reader 9.0\Reader\AcroRd32.exe";
        printerName = "HP LaserJet P2055 Series PCL6";
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="PdfFilePrinter"/> class.
    /// </summary>
    /// <param name="pdfFileName">Name of the PDF file.</param>
    public PdfFilePrinter(string pdfFileName)
    {
        this.PdfFileName = pdfFileName;
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="PdfFilePrinter"/> class.
    /// </summary>
    /// <param name="pdfFileName">Name of the PDF file.</param>
    /// <param name="printerName">Name of the printer.</param>
    public PdfFilePrinter(string pdfFileName, string printerName)
    {
        this.pdfFileName = pdfFileName;
        this.printerName = printerName;
    }

    /// <summary>
    /// Gets or sets the name of the PDF file to print.
    /// </summary>
    public string PdfFileName
    {
        get { return this.pdfFileName; }
        set { this.pdfFileName = value; }
    }
    string pdfFileName;

    /// <summary>
    /// Gets or sets the name of the printer. A typical name looks like '\\myserver\HP LaserJet PCL5'.
    /// </summary>
    /// <value>The name of the printer.</value>
    public string PrinterName
    {
        get { return this.printerName; }
        set { this.printerName = value; }
    }
    string printerName;

    /// <summary>
    /// Gets or sets the working directory.
    /// </summary>
    public string WorkingDirectory
    {
        get { return this.workingDirectory; }
        set { this.workingDirectory = value; }
    }
    string workingDirectory;

    /// <summary>
    /// Prints the PDF file.
    /// </summary>
    public void Print()
    {
        Print(-1);
    }

    /// <summary>
    /// Prints the PDF file.
    /// </summary>
    /// <param name="milliseconds">The number of milliseconds to wait for completing the print job.</param>
    public void Print(int milliseconds)
    {
        if (this.printerName == null || this.printerName.Length == 0)
            this.printerName = PdfFilePrinter.defaultPrinterName;

        if (PdfFilePrinter.adobeReaderPath == null || PdfFilePrinter.adobeReaderPath.Length == 0)
            throw new InvalidOperationException("No full qualified path to AcroRd32.exe or Acrobat.exe is set.");

        if (this.printerName == null || this.printerName.Length == 0)
            throw new InvalidOperationException("No printer name set.");

        // Check whether file exists.
        string fqName = String.Empty;
        if (this.workingDirectory != null && this.workingDirectory.Length != 0)
            fqName = Path.Combine(this.workingDirectory, this.pdfFileName);
        else
            fqName = Path.Combine(Directory.GetCurrentDirectory(), this.pdfFileName);
        if (!File.Exists(fqName))
            throw new InvalidOperationException(String.Format("The file {0} does not exist.", fqName));

        // TODO: Check whether printer exists.

        try
        {
            DoSomeVeryDirtyHacksToMakeItWork();

            //acrord32.exe /t          <- seems to work best
            //acrord32.exe /h /p       <- some swear by this version
            ProcessStartInfo startInfo = new ProcessStartInfo();
            startInfo.FileName = PdfFilePrinter.adobeReaderPath;
            string args = String.Format("/t \"{0}\" \"{1}\"", this.pdfFileName, this.printerName);
            //Debug.WriteLine(args);
            startInfo.Arguments = args;
            startInfo.CreateNoWindow = true;
            startInfo.ErrorDialog = false;
            startInfo.UseShellExecute = false;
            if (this.workingDirectory != null && this.workingDirectory.Length != 0)
                startInfo.WorkingDirectory = this.workingDirectory;

            Process process = Process.Start(startInfo);
            if (!process.WaitForExit(milliseconds))
            {
                // Kill Adobe Reader/Acrobat
                process.Kill();
            }
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }

    /// <summary>
    /// For reasons only Adobe knows the Reader seams to open and shows the document instead of printing it
    /// when it was not already running.
    /// If you use PDFsharp and have any suggestions to circumvent this function, please let us know.
    /// </summary>
    void DoSomeVeryDirtyHacksToMakeItWork()
    {
        if (PdfFilePrinter.runningAcro != null)
        {
            if (!PdfFilePrinter.runningAcro.HasExited)
                return;
            PdfFilePrinter.runningAcro.Dispose();
            PdfFilePrinter.runningAcro = null;
        }
        // Is any Adobe Reader/Acrobat running?
        Process[] processes = Process.GetProcesses();
        int count = processes.Length;
        for (int idx = 0; idx < count; idx++)
        {
            try
            {
                Process process = processes[idx];
                ProcessModule module = process.MainModule;

                if (String.Compare(Path.GetFileName(module.FileName), Path.GetFileName(PdfFilePrinter.adobeReaderPath), true) == 0)
                {
                    // Yes: Fine, we can print.
                    PdfFilePrinter.runningAcro = process;
                    break;
                }
            }
            catch { }
        }
        if (PdfFilePrinter.runningAcro == null)
        {
            // No: Start an Adobe Reader/Acrobat.
            // If you are within ASP.NET, good luck...
            PdfFilePrinter.runningAcro = Process.Start(PdfFilePrinter.adobeReaderPath);
            PdfFilePrinter.runningAcro.WaitForInputIdle();
        }
    }
    static Process runningAcro;

    /// <summary>
    /// Gets or sets the Adobe Reader or Adobe Acrobat path.
    /// A typical name looks like 'C:\Program Files\Adobe\Adobe Reader 7.0\AcroRd32.exe'.
    /// </summary>
    static public string AdobeReaderPath
    {
        get { return PdfFilePrinter.adobeReaderPath; }
        set { PdfFilePrinter.adobeReaderPath = value; }
    }
    static string adobeReaderPath;

    /// <summary>
    /// Gets or sets the name of the default printer. A typical name looks like '\\myserver\HP LaserJet PCL5'.
    /// </summary>
    static public string DefaultPrinterName
    {
        get { return PdfFilePrinter.defaultPrinterName; }
        set { PdfFilePrinter.defaultPrinterName = value; }
    }
    static string defaultPrinterName;
}
person masoud Cheragee    schedule 04.07.2012
comment
«Этот код работает отлично» - слегка переоптимистично. - person jwg; 21.12.2016
comment
Я почти уверен, что это работает, вы могли бы связаться с вами, чтобы помочь в этом вопросе, вместо -1 - person masoud Cheragee; 23.02.2017