C# Winform 从扫描仪获取图片 并导出为PDF
最近在做一个科研档案管理系统的项目,需要能够直接调用扫描仪进行扫描。扫描仪又分为是否有进稿器(批量扫描和一张一张扫描),进稿器模式使用twain协议就可以做,直接可以得到PDF文件,这个网上有很多案例我也没做什么创新,就不写了。另外一种方式因为扫描仪没有进稿器,一次只能扫描一张图像,所以可以是利用WIA获取图像,保存在临时文件夹下,然后最后再转为PDF导出。这篇文章说的就是这种方法。
利用WIA从扫描仪获取图像
1.添加引用
如图,为winform项目添加引用。网上看到有提问说找不到这个引用,据说是安装office2007以上版本可以解决,我没试过,因为大部分电脑都装了office,如果有解决这个问题经验的欢迎交流。
然后,在CS文件中添加USING,如下:
using WIA;
从扫描仪获取图像
1、先new 一个WIA.CommonDialogClass对象
CommonDialogClass cdc = new WIA.CommonDialogClass();
2、调用CommonDialogClass对象的ShowAcquireImage方法可以获取扫描仪图像
这个方法的说明可以看微软的官方文档,就不多做解释了
https://docs.microsoft.com/zh-cn/previous-versions/windows/desktop/wiaaut/-wiaaut-icommondialog-showacquireimage
我们只要知道返回的是一个ImageFile对象,这个对象有一个SaveFile方法可以把获取到的图像存为文件。
var imageFile = cdc.ShowAcquireImage(WIA.WiaDeviceType.ScannerDeviceType,WIA.WiaImageIntent.TextIntent,WIA.WiaImageBias.MaximizeQuality,"{00000000-0000-0000-0000-000000000000}",true,true,false);
这样就得到了imageFile对象了。
当然,这个方法不一定可靠,如果获取失败他会抛出一个异常,我们可以用 try catch捕获异常,我暂时捕获的是System.Runtime.InteropServices.COMException异常
接下来,用
imageFile.SaveFile("你设置的存储路径,以jpg,bmp,png结尾都行,我试过了可用");
在picturebox中显示扫描的图像
在界面上画一个picturebox控件。由于picturebox不能直接给路径,得给一个Image对象,Image提供了一个FromStream方法从图像文件流构造Image对象,因此,需要先读图像文件并获取文件流
这里当然是先using
using System.IO;
然后通过他的构找函数new一个FileStream 对象,因为涉及到释放资源的问题,建议使用using把它包裹起来。
using (FileStream stream = new FileStream("刚才存储的图片地址", FileMode.Open,
FileAccess.Read, FileShare.Read))
这样stream就是我们要的图像文件流了。
之后我们构造获取Image对象
var img=Image.FromStream(stream);;
然后就是
pictureBox1.Image = img;
这样picturebox1里面就会显示图像了。
图像转为PDF
一份文件可能由多页构成,一次上传好多图片可能并不利于阅读,因此我的设计是你只要按顺序进行扫描,之后就可以一键导出,将本次打开这个软件并扫描的多张图像转为一个pdf,挺方便的。
这里用到一个类库iTextSharp,可以在nuget上安装这个类库或者自己去下载一个iTextSharp.dll来添加引用。
经验:因为iTextSharp也存在一个Image类,和我们前面用的Image重名,这样就需要在写的时候标注具体指的是哪个image,所以我干脆就不using iTextSharp的命名空间。把代码写长点毕竟只用写那么几句,方便的很。
在这之前要先读取到已经扫描的文件,我的设计思路是读取本次的临时文件夹(每次打开软件第一次扫描会生成一个临时文件夹),列出该文件夹下所有的文件,这样万一扫描错了你直接在文件夹中删除文件就ok了,我的软件就不用负责管理。
private List<string> GetImageFileList()
{
List<string> res = new List<string>();
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
return res;
}
DirectoryInfo root = new DirectoryInfo(path);
FileInfo[] files=root.GetFiles();
foreach (var file in files)
{
if (file.FullName.Substring(file.FullName.LastIndexOf(".")+1).ToLower() == "pdf")
continue;
res.Add(file.FullName);
}
return res;
}
正式开始,先创建一个iTestSahrp.text.Document对象,也就相当于新建一个空文档,设置好它的纸张大小、页边距,代码入下:
iTextSharp.text.Document document = new iTextSharp.text.Document(iTextSharp.text.PageSize.A4, 25, 25, 25, 25);
然后,需要获取到一个pdfWriter对象,类库中这个类使用了单例模式,所以用GetInstance来创建对象。后面我发现由于它采用了单一实例,得到的对象甚至后面用不到,所以就不给他变量名了。
iTextSharp.text.pdf.PdfWriter.GetInstance(document, new FileStream("PDF存储路径,以.pdf结尾", FileMode.Create, FileAccess.ReadWrite));
打开这个空文档
document.Open();
然后就是把一张一张图片插入到文档里(插入之前用NewPage方法插入个新的页,然后用Add方法插如图片)
foreach (var imgfile in imglist)
{
if (string.IsNullOrEmpty(imgfile))
break;
var image = iTextSharp.text.Image.GetInstance(imgfile);
if (image.Height > iTextSharp.text.PageSize.A4.Height - 25)
{
image.ScaleToFit(iTextSharp.text.PageSize.A4.Width - 25, iTextSharp.text.PageSize.A4.Height - 25);
}
else if (image.Width > iTextSharp.text.PageSize.A4.Width - 25)
{
image.ScaleToFit(iTextSharp.text.PageSize.A4.Width - 25, iTextSharp.text.PageSize.A4.Height - 25);
}
image.Alignment = iTextSharp.text.Image.ALIGN_MIDDLE;
document.NewPage();//创建一个新的页
document.Add(image);//插入图片
}
最后都做完了记得关闭document,这几步我用的try cache finally来做的,这样可以保证出错也能关闭
document.Close();
提醒:
如果图片多,生成PDF会耗费时间,造成UI假死,所以要用多线程来做,创建个线程实现后台运行。
ThreadStart threadStart = new ThreadStart(ImagetoPDF);//ImagetoPDF为要执行的方法,无参数,返回类型VOID,这是个委托
Thread thread = new Thread(threadStart);
thread.IsBackground = true;
thread.Start();//启动新线程
Image2PDF.CheckForIllegalCrossThreadCalls=false;
详细代码:
扫描并在pictureBox上显示
private void 开始扫描ToolStripMenuItem_Click(object sender, EventArgs e)
{
ImageFile imageFile = null;
CommonDialogClass cdc = new WIA.CommonDialogClass();
try
{
imageFile = cdc.ShowAcquireImage(WIA.WiaDeviceType.ScannerDeviceType,
WIA.WiaImageIntent.TextIntent,
WIA.WiaImageBias.MaximizeQuality,
"{00000000-0000-0000-0000-000000000000}",
true,
true,
false);
}
catch (System.Runtime.InteropServices.COMException)
{
imageFile = null;
}
if (imageFile != null)
{
nowfile=path+"\\page_"+(++page)+".jpg";
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
imageFile.SaveFile(nowfile);
using (FileStream stream = new FileStream(nowfile, FileMode.Open,
FileAccess.Read, FileShare.Read))
{
var img=Image.FromStream(stream);;
pictureBox1.SizeMode = PictureBoxSizeMode.Zoom;
pictureBox1.Width = img.Width;
pictureBox1.Height = img.Height;
pictureBox1.Image = img;
}
}
}
导出PDF按钮被点击(我另外弹出一个窗体叫做Image2PDF,做了个等待的效果)
private void 转存pdfToolStripMenuItem_Click(object sender, EventArgs e)
{
try
{
string localFilePath;
SaveFileDialog sfd = new SaveFileDialog();
//设置文件类型
sfd.Filter = "PDF文档(*.pdf)|*.pdf";
//设置默认文件类型显示顺序
sfd.FilterIndex = 1;
//保存对话框是否记忆上次打开的目录
sfd.RestoreDirectory = true;
//点了保存按钮进入
if (sfd.ShowDialog() == DialogResult.OK)
{
localFilePath = sfd.FileName.ToString(); //获得文件路径
string fileNameExt = localFilePath.Substring(localFilePath.LastIndexOf("\\") + 1); //获取文件名,不带路径
var imgList = GetImageFileList();
if (imgList == null || imgList.Count == 0)
{
MessageBox.Show("本次未扫描任何文件");
return;
}
if (File.Exists(localFilePath))
{
DialogResult dr = MessageBox.Show("文件已存在,是否覆盖?", "警告", MessageBoxButtons.OKCancel);
if (dr == DialogResult.Cancel)
return;
File.Delete(localFilePath);
}
var form = new Image2PDF(imgList, localFilePath);
form.Show();
}
}
catch (Exception ex)
{
MessageBox.Show("导出失败");
}
}
public Image2PDF(List<string> imgList,string savepath)
{
ImgList = imgList;
SavePath = savepath;
InitializeComponent();
ThreadStart threadStart = new ThreadStart(ImagetoPDF);
Thread thread = new Thread(threadStart);
thread.IsBackground = true;
thread.Start();//启动新线程
Image2PDF.CheckForIllegalCrossThreadCalls=false;
}
private void ImagetoPDF()
{
try
{
var imglist = ImgList;
if (imglist == null || imglist.Count == 0)
{
MessageBox.Show("本次未扫描任何文件", "系统提示", MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1, MessageBoxOptions.ServiceNotification);
return;
}
string pdfpath = SavePath;
while (File.Exists(pdfpath))
{
pdfpath = SavePath;
}
iTextSharp.text.Document document = new iTextSharp.text.Document(iTextSharp.text.PageSize.A4, 25, 25, 25, 25);
try
{
iTextSharp.text.pdf.PdfWriter.GetInstance(document, new FileStream(pdfpath, FileMode.Create, FileAccess.ReadWrite));
document.Open();
iTextSharp.text.Image image;
foreach (var imgfile in imglist)
{
if (string.IsNullOrEmpty(imgfile))
break;
image = iTextSharp.text.Image.GetInstance(imgfile);
if (image.Height > iTextSharp.text.PageSize.A4.Height - 25)
{
image.ScaleToFit(iTextSharp.text.PageSize.A4.Width - 25, iTextSharp.text.PageSize.A4.Height - 25);
}
else if (image.Width > iTextSharp.text.PageSize.A4.Width - 25)
{
image.ScaleToFit(iTextSharp.text.PageSize.A4.Width - 25, iTextSharp.text.PageSize.A4.Height - 25);
}
image.Alignment = iTextSharp.text.Image.ALIGN_MIDDLE;
document.NewPage();
document.Add(image);
}
}
catch (Exception ex)
{
throw;
}
finally
{
document.Close();
}
MessageBox.Show("转换完成", "系统提示", MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1, MessageBoxOptions.ServiceNotification);
}
catch (Exception)
{
MessageBox.Show("转换失败", "系统提示", MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1, MessageBoxOptions.ServiceNotification);
}
finally
{
CloseForm();
}
}
public void CloseForm()
{
this.Close();
this.Dispose();
}
文章中部分代码来源于网络,我自己进行加工和组合,供大家参考,如侵权请告知,我将会及时删除。
上一篇: pdfh5 插件使用
下一篇: 如何将多张图片合并成一个PDF文件