//补充一些对于pdf组件的调研结果和心路历程
心得:
原生的能实现的就尽量用原生的,调研任务是针对于达到目的,而不是针对于如何使用。
首先不得不承认spire.pdf的强大,但是其收费限制也是挺恶心的。pdf页面转图片功能也是效率特别低的。恰好pdfiumViewer补足了这部分短板。spir.pdf打印也只能打印前三页。pdfiumViewer对于打印支持的内容太少了。恰好原生的pdfDocument补足了这块短板。其它也有调研,但大都发现需要代码做到开源。具体功能也都有部分限制。
一:spire.pdf组件的使用
这里使用spir.pdf处理了一些各类格式的文件转为pdf亦或是pdf合并等小功能点的实现。正因为如此。利用递归繁琐的处理也是可以跳过十页,三页的限制的。但是打印时会有断层。
二:pdfiumViewer组件的使用
注意pdfiumViewer是将pdf页面转为图片,值得注意的是:【转换后的图片有很大程度的失真效果】。
1、Method下面是通过调整分辨率,解决的失真方式。
public static List<string> PdfToJpg(string filePath, string saveDir, float dpiWidth = 1600, float dpiHeight = 900, bool isMax = true)
{
if (string.IsNullOrWhiteSpace(filePath) || !File.Exists(filePath))
{
throw new ArgumentException("文件有误或不存在,请检查文件是否正确。");
}
List<string> imageLst = new List<string>();
using (var document = PdfDocument.Load(filePath))
{
for (int i = 0; i < document.PageCount; i++)
{
var page = document.PageSizes[i];
SizeF standardSize = new SizeF(dpiWidth, dpiHeight);
float height = page.Height;
//等比处理分辨率
float scale = isMax ? Math.Max(standardSize.Width / page.Width, standardSize.Height / height) : Math.Min(standardSize.Width / page.Width, standardSize.Height / height);
SizeF scaledSize = new SizeF(page.Width * scale, page.Height * scale);
using (Image img = document.Render(i, (int)scaledSize.Width, (int)scaledSize.Height, (int)scaledSize.Width, (int)scaledSize.Height, true))
{
string jpgPath = Path.Combine(saveDir, $"{i + 1}.jpg");
img.Save(jpgPath, ImageFormat.Jpeg);
imageLst.Add(jpgPath);
}
}
}
return imageLst;
}
2、Method下面是一个主要的方法,可以实现对于pdf文件页面进行等分,转换为照片。
public List<string> cutPdfByPdfiumView(string fileStream, int copies, string outPdfPath)
{
List<string> files = new List<string>();
// 删除文件夹及其所有内容
if (Directory.Exists(outPdfPath))
{
Directory.Delete(outPdfPath, true);
}
// 创建空文件夹
Directory.CreateDirectory(outPdfPath);
using (var document = PdfDocument.Load(fileStream))
{
for (int i = 0; i < document.PageCount; i++)
{
var page = document.PageSizes[i];
SizeF standardSize = new SizeF(1600, 900);
float top = 0, height = page.Height / copies;
float scale = Math.Max(standardSize.Width / page.Width, standardSize.Height / height);
SizeF scaledSize = new SizeF(page.Width * scale, page.Height * scale);
using (var pageImage = document.Render(i, (int)scaledSize.Width, (int)scaledSize.Height, (int)scaledSize.Width, (int)scaledSize.Height, true))
{
height = scaledSize.Height / copies;
for (int n = 0; n < copies; n++)
{
using (Bitmap img = new Bitmap(pageImage.Width, (int)height))
{
using (Graphics graphics = Graphics.FromImage(img))
{
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.DrawImage(pageImage
, new RectangleF(0, 0, img.Width, img.Height)
, new RectangleF(0, top, img.Width, img.Height), GraphicsUnit.Pixel);
top += img.Height;
if (IsImageBlank(img))
{
continue;
}
string filePath = Path.Combine(outPdfPath, $"{i + 1}-{n + 1}.png");
img.Save(filePath, ImageFormat.Png);
files.Add(filePath);
}
}
}
}
}
}
return files;
}
下面是一个拓展的例子,对于页面进行随意切割
new BankReceiptCutDto("民生银行",false,0, new List<float>{ 715,60,715,60,715},@"\s交易流水号:\s*\d+[A-Z0-9]+\s*",new List<string>{ "交易流水号",":",":"},new char[]{':',':'}),
//流水号匹配数据
private readonly string regularTradeno = @"\s交易流水号:\s*\d+[A-Z0-9]+\s*";
private readonly List<string> strTradeno = new List<string>() { "交易流水号", ":", ":" };
private readonly char[] chrTradeno = new char[] { ':', ':' };
public List<BankBillFile> CutBankReceipt(string bankType, string fileStream)
{
try
{
//先获取银行类型内置的处理逻辑
BankReceiptCutDto match = ReceiptCutHelper.GetReceiptCutDto(bankType);
if (match == null)
{
throw new ArgumentException("未获取到该银行类型的处理逻辑,必要时您可以联系客服寻求帮助。");
}
//处理后的回单文件
List<BankBillFile> billFiles = new List<BankBillFile>();
using (var document = PdfDocument.Load(fileStream))
{
for (int i = 0; i < document.PageCount; i++)
{
var page = document.PageSizes[i];
SizeF standardSize = new SizeF(1600, 900);
float top = 0, height = page.Height;
//获取是等分还是按照长度分割
int copies = match.isBisection ? match.copies : match.cutDistance.Count;
height = page.Height / copies;
//等比处理分辨率
float scale = Math.Min(standardSize.Width / page.Width, standardSize.Height / height);
SizeF scaledSize = new SizeF(page.Width * scale, page.Height * scale);
#region "处理获取匹配信息"
string txt = document.GetPdfText(i);
//处理从里面获取需要的文字信息
List<string> matchTradeNos = new List<string>();
MatchCollection tradenoLst = Regex.Matches(txt, regularTradeno);
if (tradenoLst == null || tradenoLst.Count <= 0)
{
throw new ArgumentException("未获取到匹配数据,请确认导入文件是否正确,必要时您可以联系客服寻求帮助。");
}
for (int no = 0; no < tradenoLst.Count; no++)
{
string TradenoStr = PdfiumViewerHelper.DealMatchText(tradenoLst[no].ToString(), strTradeno, chrTradeno);
matchTradeNos.Add(TradenoStr.Substring(0, 23));
}
#endregion
using (var pageImage = document.Render(i, (int)scaledSize.Width, (int)scaledSize.Height, (int)scaledSize.Width, (int)scaledSize.Height, true))
{
int noName = 0;
for (int n = 0; n < copies; n++)
{
float imgHeight = match.isBisection ? scaledSize.Height / copies : match.cutDistance[n];
using (Bitmap img = new Bitmap(pageImage.Width, (int)imgHeight))
{
using (Graphics graphics = Graphics.FromImage(img))
{
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.DrawImage(pageImage
, new RectangleF(0, 0, img.Width, img.Height)
, new RectangleF(0, top, img.Width, img.Height), GraphicsUnit.Pixel);
top += imgHeight;
//去掉非回单页 虚线行等
if (!match.isBisection && img.Height != (int)match.cutDistance.Max())
{
continue;
}
//去掉空白页
if (PdfiumViewerHelper.IsImageBlank(img))
{
continue;
}
//以防万一 文件命名没名字
string pngName = $"{ i + 1 }-{ n + 1}";
if (matchTradeNos != null && matchTradeNos.Count >= noName + 1)
{
//需要做一个受限 不然textLst越界
pngName = matchTradeNos[noName];
noName++;
}
byte[] ImgBt = PdfiumViewerHelper.ImageToByteArray(img, ImageFormat.Png);
BankBillFile bankBill = new BankBillFile()
{
AccountId = GlobalParameter.AccountId,
TradeNo = pngName,
FileContent = ImgBt,
};
billFiles.Add(bankBill);
}
}
}
}
}
}
return billFiles;
}
catch (ArgumentException aex)
{
throw aex;
}
catch (Exception ex)
{
Logger.Error(ex.Message);
throw new ArgumentException("民生银行回单处理有误请重试,必要时您可以联系客服寻求帮助。");
}
}
三、对于图片或者pdf进行打印操作。
具体想打印什么,都可以通用,比较繁琐罢了,避过去了任何收费和开源限制。任何文件都可以递归都可以利用spire.pdf转为pdf,利用pdfiumViewer转换为照片,这些处理操作过程中的失真效果也可以处理,最后利用不会导致失真的pdfDocument进行缩放打印处理。下面具体的处理代码,可以稍作调整,直接使用。
private void printFile()
{
try
{
if (string.IsNullOrEmpty(filePath) || !File.Exists(filePath))
{
throw new ArgumentException("请检查打印文件是否存在");
}
//string fileName = Path.GetFileName(filePath).ToLower();
string fileExtension = Path.GetExtension(filePath).ToLower();
if (fileExtension == ".jpg"
|| fileExtension == ".jpeg"
|| fileExtension == ".png"
|| fileExtension == ".bmp")
{
Image image = new Bitmap(filePath);
fileList.Add(image);
}
else if (fileExtension == ".pdf")
{
var imgLst = PdfiumViewerHelper.PdfToImage(filePath);
if (imgLst != null && imgLst.Count > 0)
{
imgLst.ForEach(s => fileList.Add(s));
}
}
PrintDocument printDocument = new PrintDocument();
printDocument.PrintPage += printdocument_PrintPage;
//设置打印机
printDocument.PrinterSettings.PrinterName = this.Report.printer;
//设置打印纸
printDocument.DefaultPageSettings.PaperSize = this.Report.paperSize;
//设置页边距
printDocument.DefaultPageSettings.Margins = new Margins((int)(this.Report.papermargintop * 100 / 2.54m), (int)(this.Report.papermarginbottom * 100 / 2.54m), (int)(this.Report.papermarginleft * 100 / 2.54m), (int)(this.Report.papermarginright * 100 / 2.54m));
//设置页方向
printDocument.DefaultPageSettings.Landscape = this.Report.direction == Paperdirection.horizontal;
printDocument.PrintController = new StandardPrintController();
//printDocument.
//ConfigHelper.PREVIEW_BEFROE_PRINT
string chkPreview = ConfigHelper.GetValue(ConfigHelper.PREVIEW_BEFROE_PRINT);
if (chkPreview =="True")
{
PrintPreviewDialog previewDialog = new PrintPreviewDialog();
previewDialog.Document = printDocument;
previewDialog.ShowIcon = false;
previewDialog.WindowState = FormWindowState.Maximized;
previewDialog.ShowDialog(this);
}
else
{
printDocument.Print();
}
}
catch (ArgumentException ex)
{
throw ex;
}
catch (Exception ex)
{
Logger.Error(ex.Message);
throw new ArgumentException("打印失败,请稍后重试或联系工程师查阅打印日志");
}
}
正在使用的代码是按照图片居左自适应处理,注释掉的代码是对于打印内容进行按照打印内容进行调整,具体看个人要求来处理。
private void printdocument_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
//图片抗锯齿
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
//Stream fs = new FileStream(fileList[printNum].ToString().Trim(), FileMode.Open, FileAccess.Read);
System.Drawing.Image image = fileList[printNum];
int x = e.MarginBounds.X;
int y = e.MarginBounds.Y;
int width = image.Width;
int height = image.Height;
if (((double)width / e.MarginBounds.Width) > ((double)height / e.MarginBounds.Height))
{
width = e.MarginBounds.Width;
height = image.Height * e.MarginBounds.Width / image.Width;
}
else
{
height = e.MarginBounds.Height;
width = image.Width * e.MarginBounds.Height / image.Height;
}
//DrawImage参数根据打印机和图片大小自行调整
System.Drawing.Rectangle destRect = new System.Drawing.Rectangle(x, y, width, height);
if (image.Height < e.MarginBounds.Height)
{
//e.Graphics.DrawImage(image, 0, 30, image.Width + 20, image.Height);
e.Graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, System.Drawing.GraphicsUnit.Pixel);
}
else
{
//e.Graphics.DrawImage(image, 0, 0, image.Width + 20, image.Height);
e.Graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, System.Drawing.GraphicsUnit.Pixel);
}
//if (image.Height < e.MarginBounds.Height)
//{
// if (image.Width < e.MarginBounds.Width)
// {
// e.Graphics.DrawImage(image, (int)this.Report.papermargintop, (int)this.Report.papermarginleft, image.Width - widthMargin, image.Height - heightMargin);
// }
// else
// {
// e.Graphics.DrawImage(image, (int)this.Report.papermargintop, (int)this.Report.papermarginleft, e.MarginBounds.Width - widthMargin, image.Height - heightMargin);
// }
//}
//else
//{
// if (image.Width < e.MarginBounds.Width)
// {
// e.Graphics.DrawImage(image, (int)this.Report.papermargintop, (int)this.Report.papermarginleft, image.Width - widthMargin, e.MarginBounds.Height - heightMargin);
// }
// else
// {
// e.Graphics.DrawImage(image, (int)this.Report.papermargintop, (int)this.Report.papermarginleft, e.MarginBounds.Width - widthMargin, e.MarginBounds.Height - heightMargin);
// }
//}
if (printNum < fileList.Count - 1)
{
printNum++;
e.HasMorePages = true;
return;
}
e.HasMorePages = false;
}
需要注意的点:
1、 if ((width / e.MarginBounds.Width) > (height / e.MarginBounds.Height))
这句代码进行调整,因为前后求出结果会取整。
调整后内容:
if (((double)width / e.MarginBounds.Width) > ((double)height / e.MarginBounds.Height))
行文至此,后续再补充,莫愁莫愁,个人调研路,前路漫漫