C#(一)WPF 导出 PDF

需求

在 A.xaml 界面将 B.xaml 界面导出为 PDF,B.xaml 为 Window 窗体。


实现

调用 windows api 通过打印机导出 PDF。


后台代码实现

A.xaml 核心代码:反射调用 B 界面的方法,获得传入参数后的 B 界面。

private void Export_Click(object sender, RoutedEventArgs e)
{
    try
    {
        thread = new Thread(new ThreadStart(delegate
		{
			this.Dispatcher.Invoke(DispatcherPriority.Background, (ThreadStart)delegate
			{
				Assembly assembly = Assembly.GetExecutingAssembly();
				var type = assembly.GetType($"namespace");
				var assem = assembly.CreateInstance($"namespace", false, BindingFlags.CreateInstance, null, new object[] { "B 界面参数" }, null, null) as Window;
				MethodInfo method = assem.GetType().GetMethod("ExportPDF");
				Object[] paras = new Object[] { "ExportPDF 方法参数 1", "ExportPDF 方法参数 2" };
				method.Invoke(assem, paras);
			});
		}));
		thread.Start();
    }
    catch (Exception ex)
    {
        throw ex;
    }
    finally
    {
    }
}

B.xaml 核心代码:将要打印的控件内容重新布局,细节可以自调。可传入多个控件名字,这里取两个。

public void GetPDF(string 参数 1, string 参数 2)
{
    // obj1、obj2 分别为要打印的控件名。
    // 其它参数随意,这里取导出的 PDF 的名字 (name) 以及路径 (path)
    GetPDF(ojb1, obj2, name, path);
}

private void GetPDF(object object1, object object2, string name, string path)
{
    PrintDialog pd = new PrintDialog();

    var stack = new StackPanel() { };
    var stack1 = new StackPanel() { HorizontalAlignment = HorizontalAlignment.Center, Margin = new Thickness(0, 0, 0, 0) };

    string childXaml1 = XamlWriter.Save(object2);
    StringReader stringReader1 = new StringReader(childXaml1);
    XmlReader xmlReader1 = XmlReader.Create(stringReader1);
    UIElement clonedChile1 = (UIElement)XamlReader.Load(xmlReader1);
    stack.Children.Add(clonedChile);
    
    string childXaml = XamlWriter.Save(object1);
    StringReader stringReader = new StringReader(childXaml);
    XmlReader xmlReader = XmlReader.Create(stringReader);
    UIElement clonedChile = (UIElement)XamlReader.Load(xmlReader);
    stack1.Children.Add(clonedChile1);

    stack.Children.Add(stack1);

    FixedDocumentValuePdf(GetFixedDocumentHorizontal(stack, pd), name, path);
}

若是要打印的内容超过一页,则需要分页。

private FixedDocument GetFixedDocumentHorizontal(FrameworkElement toPrint, PrintDialog printDialog)
{
    double sizeWidths = 800;
    double sizeHeights = 1045;

    Size pageSize = new Size(printDialog.PrintableAreaWidth, printDialog.PrintableAreaHeight);
    FixedDocument fixedDoc = new FixedDocument();

    toPrint.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
    toPrint.Arrange(new Rect(new System.Windows.Point(0, 0), toPrint.DesiredSize));

    Size size = toPrint.DesiredSize;

    double yOffset = 0;
    while (yOffset < size.Height)
    {
        VisualBrush vb = new VisualBrush(toPrint)
        {
            Stretch = Stretch.None,
            AlignmentX = AlignmentX.Left,
            AlignmentY = AlignmentY.Top,
            ViewboxUnits = BrushMappingMode.Absolute,
            TileMode = TileMode.None,
            Viewbox = new Rect(0, yOffset, sizeWidths, sizeHeights)
        };

        PageContent pageContent = new PageContent();
        FixedPage page = new FixedPage();
        ((IAddChild)pageContent).AddChild(page);
        fixedDoc.Pages.Add(pageContent);
        page.Width = pageSize.Width;
        page.Height = pageSize.Height;

        var stack = new StackPanel()
        {
            Width = sizeWidths,
            Height = sizeHeights,
            HorizontalAlignment = HorizontalAlignment.Center,
            VerticalAlignment = VerticalAlignment.Center,
            Background = vb,
            Margin = new Thickness(0, 0, 0, 0)
        };
        page.Children.Add(stack);

        yOffset += sizeHeights;
    }
    return fixedDoc;
}
private void FixedDocumentValuePdf(FixedDocument fd, string name, string path)
{
    MemoryStream ms = new MemoryStream();
    Package package = Package.Open(ms, FileMode.Create);
    XpsDocument doc = new XpsDocument(package);
    var writer = XpsDocument.CreateXpsDocumentWriter(doc);
    writer.Write(fd.DocumentPaginator);
    doc.Close();
    package.Close();

    var bytes = ms.ToArray();
    ms.Dispose();

    string root = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
    string dir = root + "Data\\";
    if (!System.IO.Directory.Exists(dir))
    {
        System.IO.Directory.CreateDirectory(dir);
    }
    var outputFilePath = dir + "\\" + name + ".pdf";
    PrintXpsToPdf(bytes, outputFilePath, "pdf", false);
}

打印 PDF:

private void PrintXpsToPdf(byte[] bytes, string outputFilePath, string documentTitle, bool flag= true)
{
    var pdfPrintQueue = GetMicrosoftPdfPrintQueue();
    var ptrUnmanagedBytes = Marshal.AllocCoTaskMem(bytes.Length);
    Marshal.Copy(bytes, 0, ptrUnmanagedBytes, bytes.Length);

    var di = new DOCINFOA
    {
        pDocName = documentTitle,
        pOutputFile = outputFilePath,
        pDataType = "RAW"
    };

    var errorCode = SendBytesToPrinter(pdfPrintQueue.Name, ptrUnmanagedBytes, bytes.Length, di, out var jobId, flag);

    Marshal.FreeCoTaskMem(ptrUnmanagedBytes);
}
private int SendBytesToPrinter(string szPrinterName, IntPtr pBytes, int dwCount, DOCINFOA documentInfo, out int jobId, bool flag= true)
{
    jobId = 0;
    var dwWritten = 0;
    var success = false;

    if (OpenPrinter(szPrinterName.Normalize(), out var hPrinter, IntPtr.Zero))
    {
        jobId = StartDocPrinter(hPrinter, 1, documentInfo);
        if (jobId > 0)
        {
            if (StartPagePrinter(hPrinter))
            {
                success = WritePrinter(hPrinter, pBytes, dwCount, out dwWritten);
            }
        }
        ClosePrinter(hPrinter);
    }

    if (success == false)
    {
        return Marshal.GetLastWin32Error();
    }
    return 0;
}
[DllImport("winspool.drv", EntryPoint = "OpenPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
private static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string szPrinter, out IntPtr hPrinter, IntPtr pd);

[DllImport("winspool.drv", EntryPoint = "ClosePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
private static extern bool ClosePrinter(IntPtr hPrinter);

[DllImport("winspool.drv", EntryPoint = "StartDocPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
private static extern int StartDocPrinter(IntPtr hPrinter, int level, [In, MarshalAs(UnmanagedType.LPStruct)] DOCINFOA di);

[DllImport("winspool.drv", EntryPoint = "StartPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
private static extern bool StartPagePrinter(IntPtr hPrinter);

[DllImport("winspool.drv", EntryPoint = "WritePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
private static extern bool WritePrinter(IntPtr hPrinter, IntPtr pBytes, int dwCount, out int dwWritten);

C#  WPF 
更新时间:2021-04-29 14:44:37

本文由 caroly 创作,如果您觉得本文不错,请随意赞赏
采用 知识共享署名4.0 国际许可协议进行许可
本站文章除注明转载 / 出处外,均为本站原创或翻译,转载前请务必署名
原文链接:https://caroly.fun/archives/wpf导出pdf
最后更新:2021-04-29 14:44:37

评论

Your browser is out of date!

Update your browser to view this website correctly. Update my browser now

×