none
"Microsoft Print to PDF"をWPFで使った際の出力ファイル名設定

    質問

  • "Microsoft Print to PDF"を使ったPDFファイル作成時に
    プログラム内でファイル名を指定して人の操作(ダイアログ表示)無しで
    ファイル出力をしたいのですが、
    WPFを利用した際の方法がわかりません。

    以下に最小限の例を示します。

    WPFの場合は何かほかに印刷する方法があるのでしょうか。

    例)System.Drawing.Printing.PrintDocumentを利用する場合はわかる。
    正常に印刷できている。
    {
    System.Drawing.Printing.PrintDocument pdoc = new PrintDocument();
    pdoc.PrinterSettings.PrinterName = "Microsoft Print to PDF";
    pdoc.PrinterSettings.PrintFileName = @"C:\test\test.pdf";
    pdoc.PrinterSettings.PrintToFile = true;

    pdoc.PrintPage += (se, ea) =>
    {
    ea.Graphics.DrawString("test", new Font("", 100), System.Drawing.Brushes.Black, 100, 100);
    };

    pdoc.Print();
    }

    例)WPFを使用した例を試してみたが出力ファイル名を指定する方法がわからない。
    下記のコードは名前を付けて保存ダイアログが出るが正常に印刷できている。
    {
    System.Printing.LocalPrintServer localPrintServer = new LocalPrintServer();
    PrintQueue queue = localPrintServer.GetPrintQueue("Microsoft Print to PDF");

    XpsDocumentWriter writer = PrintQueue.CreateXpsDocumentWriter(queue);

    PrintTicket ticket = queue.DefaultPrintTicket;

    Canvas cvs = new Canvas();
    Label lbl = new Label();
    lbl.Content = "test";
    lbl.FontSize = 100;
    cvs.Children.Add(lbl);

    writer.Write(cvs, ticket);
    }

    よろしくお願いします。

    2019年2月15日 8:18

回答

  • こんな

    namespace WpfApp1
    {
        using System.Linq;
        using System.Windows;
        using System.Windows.Controls;
        using System.Windows.Documents;
        using System.Windows.Media;
        using System.IO.Packaging;//ReachFramework
        using System.Printing;//System.Printing
    
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
                Button btn = new Button();
                this.Content = btn;
                btn.Click += this.Button_Click;
            }
    
            private void Button_Click(object sender, RoutedEventArgs e)
            {
                string filePath = @"C:\Test\Test.pdf";
    
                var svr = new System.Printing.LocalPrintServer();
                var queue = svr.GetPrintQueues().FirstOrDefault(_ => _.Name == "Microsoft Print to PDF");
                if (queue == null)
                {
                    return;
                }
    
                var ticket = queue.DefaultPrintTicket;
                var fp = CreateTestPage();
    
                System.IO.MemoryStream streamXPS = new System.IO.MemoryStream();
                using (Package pack = Package.Open(streamXPS, System.IO.FileMode.CreateNew))
                {
                    using (var doc = new System.Windows.Xps.Packaging.XpsDocument(pack, CompressionOption.SuperFast))
                    {
                        var writer = System.Windows.Xps.Packaging.XpsDocument.CreateXpsDocumentWriter(doc);
                        writer.Write(fp, ticket);
                    }
                }
                streamXPS.Position = 0;
    
                Aspose.Plugins.AsposeVSOpenXML.XpsPrintHelper.Print(streamXPS, queue.Name, "jobname", false, filePath);
            }
    
    
            private FixedPage CreateTestPage()
            {
                FixedPage fp = new FixedPage();
    
                Canvas cv = new Canvas();
                TextBlock tb = new TextBlock();
                tb.Text = "test";
                tb.FontFamily = this.FontFamily;
                tb.FontSize = 100;
                tb.Foreground = Brushes.Black;
                Canvas.SetLeft(tb, 100);
                Canvas.SetTop(tb, 100);
                cv.Children.Add(tb);
    
                fp.Children.Add(cv);
                return fp;
            }
        }
    }
    
    //以下https://code.msdn.microsoft.com/office/Missing-Features-in-6a2c882b/sourcecode?fileId=153127&pathId=1941566436
    //をちょっと改造してファイル名を受け付けるように
    namespace Aspose.Plugins.AsposeVSOpenXML
    {
        using System;
        using System.ComponentModel;
        using System.IO;
        using System.Runtime.InteropServices;
    
        public class XpsPrintHelper
        {
            public static void Print(Stream stream, string printerName, string jobName, bool isWait
                , string outputFileName)//ファイル名
            {
                if (stream == null)
                    throw new ArgumentNullException("stream");
                if (printerName == null)
                    throw new ArgumentNullException("printerName");
    
                IntPtr completionEvent = CreateEvent(IntPtr.Zero, true, false, null);
                if (completionEvent == IntPtr.Zero)
                    throw new Win32Exception();
    
                try
                {
                    IXpsPrintJob job;
                    IXpsPrintJobStream jobStream;
                    StartJob(printerName, jobName, completionEvent, out job, out jobStream, outputFileName);//ファイル名
    
                    CopyJob(stream, job, jobStream);
    
                    if (isWait)
                    {
                        WaitForJob(completionEvent);
                        CheckJobStatus(job);
                    }
                }
                finally
                {
                    if (completionEvent != IntPtr.Zero)
                        CloseHandle(completionEvent);
                }
            }
    
            private static void StartJob(string printerName, string jobName, IntPtr completionEvent, out IXpsPrintJob job, out IXpsPrintJobStream jobStream
                , string outputFileName)//ファイル名
            {
                int result = StartXpsPrintJob
                    (printerName, jobName, outputFileName //ファイル名
                    , IntPtr.Zero, completionEvent
                    , null, 0, out job, out jobStream, IntPtr.Zero);
                if (result != 0)
                    throw new Win32Exception(result);
            }
    
            private static void CopyJob(Stream stream, IXpsPrintJob job, IXpsPrintJobStream jobStream)
            {
                try
                {
                    byte[] buff = new byte[4096];
                    while (true)
                    {
                        uint read = (uint)stream.Read(buff, 0, buff.Length);
                        if (read == 0)
                            break;
    
                        uint written;
                        jobStream.Write(buff, read, out written);
    
                        if (read != written)
                            throw new Exception("Failed to copy data to the print job stream.");
                    }
    
                    // Indicate that the entire document has been copied. 
                    jobStream.Close();
                }
                catch (Exception)
                {
                    // Cancel the job if we had any trouble submitting it. 
                    job.Cancel();
                    throw;
                }
            }
    
            private static void WaitForJob(IntPtr completionEvent)
            {
                const int INFINITE = -1;
                switch (WaitForSingleObject(completionEvent, INFINITE))
                {
                case WAIT_RESULT.WAIT_OBJECT_0:
                    // Expected result, do nothing. 
                    break;
                case WAIT_RESULT.WAIT_FAILED:
                    throw new Win32Exception();
                default:
                    throw new Exception("Unexpected result when waiting for the print job.");
                }
            }
    
            private static void CheckJobStatus(IXpsPrintJob job)
            {
                XPS_JOB_STATUS jobStatus;
                job.GetJobStatus(out jobStatus);
                switch (jobStatus.completion)
                {
                case XPS_JOB_COMPLETION.XPS_JOB_COMPLETED:
                    // Expected result, do nothing. 
                    break;
                case XPS_JOB_COMPLETION.XPS_JOB_FAILED:
                    throw new Win32Exception(jobStatus.jobStatus);
                default:
                    throw new Exception("Unexpected print job status.");
                }
            }
    
            [DllImport("XpsPrint.dll", EntryPoint = "StartXpsPrintJob")]
            private static extern int StartXpsPrintJob(
                [MarshalAs(UnmanagedType.LPWStr)] String printerName,
                [MarshalAs(UnmanagedType.LPWStr)] String jobName,
                [MarshalAs(UnmanagedType.LPWStr)] String outputFileName, //こいつ
                IntPtr progressEvent,   // HANDLE 
                IntPtr completionEvent, // HANDLE 
                [MarshalAs(UnmanagedType.LPArray)] byte[] printablePagesOn,
                UInt32 printablePagesOnCount,
                out IXpsPrintJob xpsPrintJob,
                out IXpsPrintJobStream documentStream,
                IntPtr printTicketStream);  // This is actually "out IXpsPrintJobStream", but we don't use it and just want to pass null, hence IntPtr. 
    
            [DllImport("Kernel32.dll", SetLastError = true)]
            private static extern IntPtr CreateEvent(IntPtr lpEventAttributes, bool bManualReset, bool bInitialState, string lpName);
    
            [DllImport("Kernel32.dll", SetLastError = true, ExactSpelling = true)]
            private static extern WAIT_RESULT WaitForSingleObject(IntPtr handle, Int32 milliseconds);
    
            [DllImport("Kernel32.dll", SetLastError = true)]
            [return: MarshalAs(UnmanagedType.Bool)]
            private static extern bool CloseHandle(IntPtr hObject);
        }
    
        [Guid("0C733A30-2A1C-11CE-ADE5-00AA0044773D")]  // This is IID of ISequenatialSteam. 
        [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        interface IXpsPrintJobStream
        {
            // ISequentualStream methods. 
            void Read([MarshalAs(UnmanagedType.LPArray)] byte[] pv, uint cb, out uint pcbRead);
            void Write([MarshalAs(UnmanagedType.LPArray)] byte[] pv, uint cb, out uint pcbWritten);
            // IXpsPrintJobStream methods. 
            void Close();
        }
    
        [Guid("5ab89b06-8194-425f-ab3b-d7a96e350161")]
        [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        interface IXpsPrintJob
        {
            void Cancel();
            void GetJobStatus(out XPS_JOB_STATUS jobStatus);
        }
    
        [StructLayout(LayoutKind.Sequential)]
        struct XPS_JOB_STATUS
        {
            public UInt32 jobId;
            public Int32 currentDocument;
            public Int32 currentPage;
            public Int32 currentPageTotal;
            public XPS_JOB_COMPLETION completion;
            public Int32 jobStatus; // UInt32 
        };
    
        enum XPS_JOB_COMPLETION
        {
            XPS_JOB_IN_PROGRESS = 0,
            XPS_JOB_COMPLETED = 1,
            XPS_JOB_CANCELLED = 2,
            XPS_JOB_FAILED = 3
        }
    
        enum WAIT_RESULT
        {
            WAIT_OBJECT_0 = 0,
            WAIT_ABANDONED = 0x80,
            WAIT_TIMEOUT = 0x102,
            WAIT_FAILED = -1 // 0xFFFFFFFF 
        }
    }

    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 回答としてマーク kazudev 2019年2月16日 3:07
    2019年2月15日 11:38

すべての返信

  • こんな

    namespace WpfApp1
    {
        using System.Linq;
        using System.Windows;
        using System.Windows.Controls;
        using System.Windows.Documents;
        using System.Windows.Media;
        using System.IO.Packaging;//ReachFramework
        using System.Printing;//System.Printing
    
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
                Button btn = new Button();
                this.Content = btn;
                btn.Click += this.Button_Click;
            }
    
            private void Button_Click(object sender, RoutedEventArgs e)
            {
                string filePath = @"C:\Test\Test.pdf";
    
                var svr = new System.Printing.LocalPrintServer();
                var queue = svr.GetPrintQueues().FirstOrDefault(_ => _.Name == "Microsoft Print to PDF");
                if (queue == null)
                {
                    return;
                }
    
                var ticket = queue.DefaultPrintTicket;
                var fp = CreateTestPage();
    
                System.IO.MemoryStream streamXPS = new System.IO.MemoryStream();
                using (Package pack = Package.Open(streamXPS, System.IO.FileMode.CreateNew))
                {
                    using (var doc = new System.Windows.Xps.Packaging.XpsDocument(pack, CompressionOption.SuperFast))
                    {
                        var writer = System.Windows.Xps.Packaging.XpsDocument.CreateXpsDocumentWriter(doc);
                        writer.Write(fp, ticket);
                    }
                }
                streamXPS.Position = 0;
    
                Aspose.Plugins.AsposeVSOpenXML.XpsPrintHelper.Print(streamXPS, queue.Name, "jobname", false, filePath);
            }
    
    
            private FixedPage CreateTestPage()
            {
                FixedPage fp = new FixedPage();
    
                Canvas cv = new Canvas();
                TextBlock tb = new TextBlock();
                tb.Text = "test";
                tb.FontFamily = this.FontFamily;
                tb.FontSize = 100;
                tb.Foreground = Brushes.Black;
                Canvas.SetLeft(tb, 100);
                Canvas.SetTop(tb, 100);
                cv.Children.Add(tb);
    
                fp.Children.Add(cv);
                return fp;
            }
        }
    }
    
    //以下https://code.msdn.microsoft.com/office/Missing-Features-in-6a2c882b/sourcecode?fileId=153127&pathId=1941566436
    //をちょっと改造してファイル名を受け付けるように
    namespace Aspose.Plugins.AsposeVSOpenXML
    {
        using System;
        using System.ComponentModel;
        using System.IO;
        using System.Runtime.InteropServices;
    
        public class XpsPrintHelper
        {
            public static void Print(Stream stream, string printerName, string jobName, bool isWait
                , string outputFileName)//ファイル名
            {
                if (stream == null)
                    throw new ArgumentNullException("stream");
                if (printerName == null)
                    throw new ArgumentNullException("printerName");
    
                IntPtr completionEvent = CreateEvent(IntPtr.Zero, true, false, null);
                if (completionEvent == IntPtr.Zero)
                    throw new Win32Exception();
    
                try
                {
                    IXpsPrintJob job;
                    IXpsPrintJobStream jobStream;
                    StartJob(printerName, jobName, completionEvent, out job, out jobStream, outputFileName);//ファイル名
    
                    CopyJob(stream, job, jobStream);
    
                    if (isWait)
                    {
                        WaitForJob(completionEvent);
                        CheckJobStatus(job);
                    }
                }
                finally
                {
                    if (completionEvent != IntPtr.Zero)
                        CloseHandle(completionEvent);
                }
            }
    
            private static void StartJob(string printerName, string jobName, IntPtr completionEvent, out IXpsPrintJob job, out IXpsPrintJobStream jobStream
                , string outputFileName)//ファイル名
            {
                int result = StartXpsPrintJob
                    (printerName, jobName, outputFileName //ファイル名
                    , IntPtr.Zero, completionEvent
                    , null, 0, out job, out jobStream, IntPtr.Zero);
                if (result != 0)
                    throw new Win32Exception(result);
            }
    
            private static void CopyJob(Stream stream, IXpsPrintJob job, IXpsPrintJobStream jobStream)
            {
                try
                {
                    byte[] buff = new byte[4096];
                    while (true)
                    {
                        uint read = (uint)stream.Read(buff, 0, buff.Length);
                        if (read == 0)
                            break;
    
                        uint written;
                        jobStream.Write(buff, read, out written);
    
                        if (read != written)
                            throw new Exception("Failed to copy data to the print job stream.");
                    }
    
                    // Indicate that the entire document has been copied. 
                    jobStream.Close();
                }
                catch (Exception)
                {
                    // Cancel the job if we had any trouble submitting it. 
                    job.Cancel();
                    throw;
                }
            }
    
            private static void WaitForJob(IntPtr completionEvent)
            {
                const int INFINITE = -1;
                switch (WaitForSingleObject(completionEvent, INFINITE))
                {
                case WAIT_RESULT.WAIT_OBJECT_0:
                    // Expected result, do nothing. 
                    break;
                case WAIT_RESULT.WAIT_FAILED:
                    throw new Win32Exception();
                default:
                    throw new Exception("Unexpected result when waiting for the print job.");
                }
            }
    
            private static void CheckJobStatus(IXpsPrintJob job)
            {
                XPS_JOB_STATUS jobStatus;
                job.GetJobStatus(out jobStatus);
                switch (jobStatus.completion)
                {
                case XPS_JOB_COMPLETION.XPS_JOB_COMPLETED:
                    // Expected result, do nothing. 
                    break;
                case XPS_JOB_COMPLETION.XPS_JOB_FAILED:
                    throw new Win32Exception(jobStatus.jobStatus);
                default:
                    throw new Exception("Unexpected print job status.");
                }
            }
    
            [DllImport("XpsPrint.dll", EntryPoint = "StartXpsPrintJob")]
            private static extern int StartXpsPrintJob(
                [MarshalAs(UnmanagedType.LPWStr)] String printerName,
                [MarshalAs(UnmanagedType.LPWStr)] String jobName,
                [MarshalAs(UnmanagedType.LPWStr)] String outputFileName, //こいつ
                IntPtr progressEvent,   // HANDLE 
                IntPtr completionEvent, // HANDLE 
                [MarshalAs(UnmanagedType.LPArray)] byte[] printablePagesOn,
                UInt32 printablePagesOnCount,
                out IXpsPrintJob xpsPrintJob,
                out IXpsPrintJobStream documentStream,
                IntPtr printTicketStream);  // This is actually "out IXpsPrintJobStream", but we don't use it and just want to pass null, hence IntPtr. 
    
            [DllImport("Kernel32.dll", SetLastError = true)]
            private static extern IntPtr CreateEvent(IntPtr lpEventAttributes, bool bManualReset, bool bInitialState, string lpName);
    
            [DllImport("Kernel32.dll", SetLastError = true, ExactSpelling = true)]
            private static extern WAIT_RESULT WaitForSingleObject(IntPtr handle, Int32 milliseconds);
    
            [DllImport("Kernel32.dll", SetLastError = true)]
            [return: MarshalAs(UnmanagedType.Bool)]
            private static extern bool CloseHandle(IntPtr hObject);
        }
    
        [Guid("0C733A30-2A1C-11CE-ADE5-00AA0044773D")]  // This is IID of ISequenatialSteam. 
        [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        interface IXpsPrintJobStream
        {
            // ISequentualStream methods. 
            void Read([MarshalAs(UnmanagedType.LPArray)] byte[] pv, uint cb, out uint pcbRead);
            void Write([MarshalAs(UnmanagedType.LPArray)] byte[] pv, uint cb, out uint pcbWritten);
            // IXpsPrintJobStream methods. 
            void Close();
        }
    
        [Guid("5ab89b06-8194-425f-ab3b-d7a96e350161")]
        [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        interface IXpsPrintJob
        {
            void Cancel();
            void GetJobStatus(out XPS_JOB_STATUS jobStatus);
        }
    
        [StructLayout(LayoutKind.Sequential)]
        struct XPS_JOB_STATUS
        {
            public UInt32 jobId;
            public Int32 currentDocument;
            public Int32 currentPage;
            public Int32 currentPageTotal;
            public XPS_JOB_COMPLETION completion;
            public Int32 jobStatus; // UInt32 
        };
    
        enum XPS_JOB_COMPLETION
        {
            XPS_JOB_IN_PROGRESS = 0,
            XPS_JOB_COMPLETED = 1,
            XPS_JOB_CANCELLED = 2,
            XPS_JOB_FAILED = 3
        }
    
        enum WAIT_RESULT
        {
            WAIT_OBJECT_0 = 0,
            WAIT_ABANDONED = 0x80,
            WAIT_TIMEOUT = 0x102,
            WAIT_FAILED = -1 // 0xFFFFFFFF 
        }
    }

    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 回答としてマーク kazudev 2019年2月16日 3:07
    2019年2月15日 11:38
  • gekka さま

    拝見&動作確認させて頂きました。
    詳細な返信頂きましたおかげで理解する事が出来ました。
    //こいつ までたどり着くことが出来るのですね!
    大変参考になります。
    これから検討いたします。
    どうもありがとうございました。

    2019年2月16日 3:04