前言
想要对PDF文档进行预览和打印,实际上这个文档可以是存在的,也可以是一个PDF文件流(本地并没有PDF文件),找了一段时间发现有个特牛逼的开源组件PdfiumViewer
,满足我这一切的需求,还可以自定义缩放,翻页的按钮。
但是这个组件需要和pdfium.dll配合使用,放在同一个路径下,运行在X86平台下。
关于PdfiumViewer
GitHub链接:https://github.com/pvginkel/PdfiumViewer
PdfiumViewer
是一个基于PDFium项目的PDF查看器,它提供了许多用于处理PDF文件的组件。承载了一个PdfRenderer控件,并添加一个工具栏老保存或打印PDF文件。
PdfDocument
用于呈现PDF文档的基类
PdfRenderer
是一个winform控件,可以渲染PdfDocument
PdfiumViewer需要PDFium库的支持,在Nuget包中是没有包含这个PDFium库的, 需要自己下载。界面大致是长这个样子,有保存、打印、放大、缩小菜单。
配置PdfiumViewer环境
使用VS自带的Nuget包管理器下载安装,这里我选择的是第一个。
然后再下载PDFium.dll,使用Nuget包管理器搜索并下载安装,这里x86_V64.v8-xfa的安装包。
在工程的 packages\PdfiumViewer.Native.x86.v8-xfa.2018.4.8.256\Build\x86路径下可以看到pdfium.dll
, 就把pdfium.dll
和PdfiumViewer
放在同一个路径下就可以使用了。
PdfiumViewer 打开并预览本地的PDF文档
创建一个winform工程,直接在.cs文件中添加PdfiumViewer即可。也可以直接把PdfiumViewer控件拖拽到工具栏,从工具栏添加这个组件,再编程。
方式1:直接打开已经存在本地的PDF文件。PdfDocument.Load
的函数有四个重载,这是其中一个。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using PdfiumViewer;
namespace PdfiumViewerDemo
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// 加载PDF文档
LoadPdfByDocument();
}
/// <summary>
/// 加载本地PDF文档
/// </summary>
private void LoadPdfByDocument()
{
//创建一个PDFView控件
PdfViewer pdfViewer1 = new PdfViewer();
//设置位置和大小
pdfViewer1.Location = new Point(5, 5);
pdfViewer1.Size = new Size(200, 300);
pdfViewer1.Dock = DockStyle.Fill;
//将控件添加到界面上
this.Controls.Add(pdfViewer1);
//加载PDF文档
pdfViewer1.Document = PdfDocument.Load(@"D:Demo.pdf");
}
}
}
运行代码的时候如果出现找不到“pdfium.dll”的异常,那么就是pdfium.dll
没有和PdfViewer.dll
放在同一目录下。
如果出现System.BadImageFormatException
HResult=0x8007000B
Message=试图加载格式不正确的程序。 (异常来自 HRESULT:0x8007000B)
Source=PdfiumViewer的异常,
那么很可能就是运行环境不对,应该更改为X86的运行环境。
运行效果:
PdfiumViewer 预览PDF文件流
PdfSharpHelper类具体代码如下,更多详细的介绍请移步c# 数据保存为PDF(三) (PdfSharp篇)
https://blog.csdn.net/weixin_40314351/article/details/127343819?spm=1001.2014.3001.5501
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using PdfSharp;
using PdfSharp.Drawing;
using PdfSharp.Pdf;
namespace PdfiumViewerDemo
{
public class PdfSharpHelper
{
// const string facename = "Times New Roman";
static string fontName = "华文宋体"; //华文宋体 Arial
//左右边距
const int padding_leftRight = 30;
//上下边距
const int padding_topBottom = 30;
/// <summary>
/// 保存为文件流
/// </summary>
/// <param name="binary"></param>
/// <returns></returns>
public static MemoryStream SaveToStream()
{
PdfDocument doc = new PdfDocument();
CreatePdfSharpPDF( doc);
MemoryStream stream = new MemoryStream();
doc.Save(stream);
return stream;
}
/// <summary>
/// 绘制功能码表头
/// </summary>
/// <param name="document"></param>
/// <param name="page"></param>
/// <param name="startY"></param>
/// <returns></returns>
private static float DrawPDFCodeTitle(PdfPage page, XGraphics gfx, float startY)
{
float x = padding_leftRight;
float y = startY;
XFont Titlefont = new XFont(fontName, 9, XFontStyle.Bold);
XSize size = gfx.MeasureString("Param", Titlefont);
float endY = (float)size.Height * 2 + 5;
//绘制标题
gfx.DrawRectangle(XBrushes.LightCyan, new XRect(padding_leftRight, y,
page.Width - 2 * padding_leftRight, endY));
y += (float)size.Height * 2 + 1;
gfx.DrawString(" Parameter", Titlefont, XBrushes.Black, x, y);
x += (float)(page.Width * 0.32);
gfx.DrawString("Value", Titlefont, XBrushes.Black, x, y);
// x += (float)(page.Width * 0.098);
// gfx.DrawString("Infomation", Titlefont, XBrushes.Black, x, y);
x += (float)(page.Width * 0.31);
gfx.DrawString("Setting", Titlefont, XBrushes.Black, x, y);
gfx.DrawString("Default", Titlefont, XBrushes.Black, x, y - size.Height - 1);
return (endY + startY);
}
/// <summary>
/// 绘制页脚
/// </summary>
/// <param name="page">页面</param>
/// <param name="gfx">画布</param>
/// <param name="cur">当前页序号</param>
/// <param name="total">总的页码数</param>
private static void DrawPDFFooter(PdfPage page, XGraphics gfx, int cur, int total)
{
//计算高度
float endY = (float)page.Height - padding_topBottom;
XFont Timefont = new XFont(fontName, 10, XFontStyle.Bold);
//测量字符串大小
XSize size = gfx.MeasureString("Drive Type/", Timefont);
//绘制页脚
gfx.DrawLine(new XPen(XColors.Black, 0.2f),
padding_leftRight,
endY,
page.Width - padding_leftRight,
endY);
endY += 2 + (float)size.Height;
//绘制信息
String footerText = "Test for Windows(C) by 唠嗑一夏 Electric Corporation";
gfx.DrawString(footerText, Timefont, XBrushes.Black, padding_leftRight,
endY);
Timefont = new XFont(fontName, 10, XFontStyle.Regular);
//绘制页码数
footerText = cur.ToString() + "/" + total.ToString();
gfx.DrawString(footerText, Timefont, XBrushes.Black,
(page.Width * 0.9), endY);
}
/// <summary>
/// 数据表格
/// </summary>
/// <returns></returns>
private static DataTable CreateData()
{
DataTable dt = new DataTable();
DataColumn col1 = new DataColumn("Num", typeof(string));
DataColumn col2 = new DataColumn("Name", typeof(string));
DataColumn col3 = new DataColumn("Val", typeof(string));
DataColumn col4 = new DataColumn("Des", typeof(string));
DataColumn col5 = new DataColumn("Set", typeof(string));
dt.Columns.Add(col1);
dt.Columns.Add(col2);
dt.Columns.Add(col3);
dt.Columns.Add(col4);
dt.Columns.Add(col5);
Random random = new Random();
List<string> nameList = new List<string>
{
"A", "BB", "CCC", "D",
"E", "F", "G","H","II",
"JJ", "LL", "M"
};
List<string> tempList = new List<string>
{
"dsd", "sdfdgvre", "Hello", "Gilrs",
"Today", "YYYY", "dfgre","GSD","fdgfer",
"Wesd", "DLG", "fsdahfi;o"
};
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
DataRow dr = dt.NewRow();
dr[0] = "KK" + i.ToString("d2") + "." + j.ToString("d2");
dr[1] = nameList[j];
if (j % 3 == 0)
{
dr[2] = random.NextDouble().ToString("f3");
}
else
{
dr[2] = i * j - random.Next(0, 30);
}
dr[3] = tempList[j];
dr[4] = random.NextDouble().ToString("f2");
//添加新行
dt.Rows.Add(dr);
}
}
return dt;
}
/// <summary>
/// 创建PDF
/// </summary>
public static void CreatePdfSharpPDF(PdfDocument doc)
{
try
{
//获取测试数据
DataTable dataTable = CreateData();
//创建文档对象
// PdfDocument doc = new PdfDocument();
//创建空页
PdfPage page = doc.AddPage();
//设置纸张大小
page.Size = PageSize.A4;
List<XGraphics> gfxList = new List<XGraphics>();
//设置一个画布
XGraphics gfx = XGraphics.FromPdfPage(page);
gfxList.Add(gfx);
const string fontName = "华文宋体";
//设置字体
XFont Titlefont = new XFont(fontName, 14, XFontStyle.Bold);
XFont Timefont = new XFont(fontName, 12, XFontStyle.Regular);
//绘制标题
gfx.DrawString(" Parameter Settings Report(Program)", Titlefont, XBrushes.Black,
new XRect(padding_leftRight, 30, page.Width - padding_leftRight, 30),
XStringFormats.CenterLeft);
//日期
gfx.DrawString(DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"), Timefont, XBrushes.Black,
new XRect(page.Width * 0.7, 30, page.Width * 0.3 - padding_leftRight, 30),
XStringFormats.BottomRight);
XPen linePen = new XPen(XColor.FromKnownColor(XKnownColor.Black), 0.1);
//绘制线
gfx.DrawLine(linePen,
new XPoint(padding_leftRight, 60), new XPoint(page.Width - padding_leftRight, 60));
//设置字体
Titlefont = new XFont(fontName, 12, XFontStyle.Bold);
Timefont = new XFont(fontName, 10, XFontStyle.Regular);
//测量字体的大小
XSize size = gfx.MeasureString("Program", Titlefont);
XSize size2 = gfx.MeasureString("Drive Type/", Timefont);
float y = 62;
float endY = (float)size.Height + 4 + (float)size2.Height;
//绘制矩形框和字符串
gfx.DrawRectangle(XBrushes.LightCyan, new XRect(padding_leftRight, y, page.Width - 2 * padding_leftRight, endY));
gfx.DrawString(" Program(Drive Selected / Connected)", Titlefont,
XBrushes.Black, padding_leftRight, y, XStringFormat.TopLeft);
y += 4 + (float)size.Height;
gfx.DrawString(" Drive Type/Model: ", Timefont,
XBrushes.Black, padding_leftRight, y, XStringFormat.TopLeft);
y += 4 + (float)size2.Height;
gfx.DrawLine(linePen,
new XPoint(padding_leftRight, y), new XPoint(page.Width - padding_leftRight, y));
y += 4;
gfx.DrawString(" Project: ", Titlefont,
XBrushes.Black, padding_leftRight, y, XStringFormat.TopLeft);
y += 4 + (float)size.Height;
gfx.DrawLine(linePen,
new XPoint(padding_leftRight, y), new XPoint(page.Width - padding_leftRight, y));
y += 4;
Titlefont = new XFont(fontName, 11, XFontStyle.Bold);
gfx.DrawString(" Information ", Titlefont,
XBrushes.Black, padding_leftRight, y, XStringFormat.TopLeft);
y += 100;
//项目的标题
y = DrawPDFCodeTitle(page, gfx, y) + 10;
//绘制功能码的字体
XFont funFont = new XFont(fontName, 9);
XFont FunBoldFont = new XFont(fontName, 9, XFontStyle.Underline | XFontStyle.Bold);
//字体的高度
XSize funcSize = gfx.MeasureString("KK00.00", funFont);
XSize funcBoldSize = gfx.MeasureString("KK00.00", FunBoldFont);
int count = dataTable.Rows.Count;
string str = "";
string strBak = "";
int j = 0;
for (int i = 0; i < count; i++)
{//遍历功能码
if ((y + funcSize.Height + 3) > (page.Height - padding_topBottom))
{//换页
page = doc.Pages.Add();
page.Size = PageSize.A4;
gfx = XGraphics.FromPdfPage(page);
gfxList.Add(gfx);
//绘制功能码表头
y = DrawPDFCodeTitle(page, gfx, 10) + 10;
}
DataRow dataRow = dataTable.Rows[i];
strBak = dataRow[0].ToString().Substring(0, 4);
if (strBak != str)
{//绘制功能码组
str = strBak;
string converStr = strBak;
j++;
funcBoldSize = gfx.MeasureString(converStr, FunBoldFont);
y += (float)funcBoldSize.Height + 6;
gfx.DrawString(converStr, FunBoldFont, XBrushes.Black, padding_leftRight, y);
y += 2;
i--;
continue;
}
// else
{//绘制
string tempStr = dataRow[0].ToString() + " " + dataRow[1].ToString();
funcSize = gfx.MeasureString(tempStr, funFont);
y += (float)funcSize.Height + 3;
if ((y + funcSize.Height + 3) > (page.Height - padding_topBottom))
{//换页
page = doc.Pages.Add();
page.Size = PageSize.A4;
gfx = XGraphics.FromPdfPage(page);
gfxList.Add(gfx);
//绘制功能码表头
y = DrawPDFCodeTitle(page, gfx, 10) + 10;
}
//序号+描述
float widthX = padding_leftRight;
gfx.DrawString(tempStr, funFont, XBrushes.Black, widthX, y);
//当前值
widthX += (float)(page.Width * 0.32);
gfx.DrawString(dataRow[2].ToString(), funFont, XBrushes.Black, widthX, y);
//设置值
widthX += (float)(page.Width * 0.31);
gfx.DrawString(dataRow[4].ToString(), funFont, XBrushes.Black, widthX, y);
}
}
gfx = null;
int Total = doc.PageCount;
for (int i = 0; i < Total; i++)
{//绘制页脚
DrawPDFFooter(page, gfxList[i], i + 1, Total);
}
// string path = "PdfSharpDemo.pdf";
// doc.Save(path);
}
catch (Exception ex)
{
}
}
}
}
将使用PdfSharp生成的PDF文件流,在PdfViewer
中显示。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using PdfiumViewer;
namespace PdfiumViewerDemo
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// 加载PDF文档
// LoadPdfByDocument();
LoadPdfByStream();
}
/// <summary>
/// 加载本地PDF文档
/// </summary>
private void LoadPdfByDocument()
{
//创建一个PDFView控件
PdfViewer pdfViewer1 = new PdfViewer();
//设置位置和大小
pdfViewer1.Location = new Point(5, 5);
pdfViewer1.Size = new Size(200, 300);
pdfViewer1.Dock = DockStyle.Fill;
//将控件添加到界面上
this.Controls.Add(pdfViewer1);
//加载PDF文档
pdfViewer1.Document = PdfDocument.Load(@"D:Demo.pdf");
}
/// <summary>
/// 加载PDF文件流
/// </summary>
private void LoadPdfByStream()
{
//创建一个PDFView控件
PdfViewer pdfViewer1 = new PdfViewer();
//设置位置和大小
pdfViewer1.Location = new Point(5, 5);
pdfViewer1.Size = new Size(200, 300);
pdfViewer1.Dock = DockStyle.Fill;
//将控件添加到界面上
this.Controls.Add(pdfViewer1);
//加载PDF文件流
pdfViewer1.Document = PdfDocument.Load(PdfSharpHelper.SaveToStream());
}
}
}
小节
主要记录PdfiumViewer对PDF文档打印预览的两种方法,一种是直接打开加载PDF文档,另外一种是加载PDF文件流(与PdfSharp配合使用)。
PdfiumViewer组件主要是使用PdfDocument.Load
来加载PDF文档。
附录
PdfiumViewer GitHub链接:https://github.com/pvginkel/PdfiumViewer