using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Printing; using System.IO; using System.Net; using System.Threading; using System.Web; using System.Windows.Forms; using iTextSharp.text.pdf; using Lskj.Util; using O2S.Components.PDFView4NET; using O2S.Components.PDFView4NET.Printing; namespace Lskj.Control.Model { public class SignaturePrinting { private const string DefaultSignatureImageFileName = "signature.png"; private const float DefaultSignatureWidth = 160f; private const float DefaultSignatureOpacity = 1f; public static string PreviewByUrl(string pdfUrl) { return PreviewByUrls(new string[] { pdfUrl }); } public static string PreviewByUrls(string pdfUrls) { return PreviewByUrls(SplitUrls(pdfUrls)); } public static string PreviewByUrls(IEnumerable pdfUrls) { return PreviewByUrls(pdfUrls, null); } public static string PreviewByUrls(IEnumerable pdfUrls, string signatureImagePath) { try { string previewFile = CreateSignedPreviewFile(pdfUrls, signatureImagePath); ShowPreviewForm(previewFile); return previewFile; } catch (Exception ex) { MessageUtil.Show("签章打印预览失败", ex.Message); return string.Empty; } } public static string CreateSignedPreviewFile(IEnumerable pdfUrls, string signatureImagePath) { List urls = NormalizeUrls(pdfUrls); if (urls.Count == 0) { throw new ArgumentException("未传入需要打印的PDF文件地址。"); } string signatureImage = ResolveSignatureImagePath(signatureImagePath); string tempPath = PubUtil.SignatureFilePath; PubUtil.ClearFilesInDirectory(tempPath); List signedFiles = new List(); for (int i = 0; i < urls.Count; i++) { string downloadedFile = DownloadPdf(urls[i], tempPath, i + 1); string signedFile = Path.Combine(tempPath, "signed_" + (i + 1).ToString("000") + "_" + Path.GetFileName(downloadedFile)); AddCenteredSignature(downloadedFile, signedFile, signatureImage, DefaultSignatureWidth, DefaultSignatureOpacity); signedFiles.Add(signedFile); } if (signedFiles.Count == 1) { return signedFiles[0]; } string mergedFile = Path.Combine(tempPath, "signature_preview_" + DateTime.Now.ToString("yyyyMMddHHmmssfff") + ".pdf"); MergePdfFiles(signedFiles, mergedFile); return mergedFile; } private static IEnumerable SplitUrls(string pdfUrls) { if (string.IsNullOrWhiteSpace(pdfUrls)) { return new string[0]; } return pdfUrls.Split(new string[] { "\r\n", "\n", ";", "|" }, StringSplitOptions.RemoveEmptyEntries); } private static List NormalizeUrls(IEnumerable pdfUrls) { List urls = new List(); if (pdfUrls == null) { return urls; } foreach (string item in pdfUrls) { if (!string.IsNullOrWhiteSpace(item)) { urls.Add(item.Trim()); } } return urls; } private static string ResolveSignatureImagePath(string signatureImagePath) { string imagePath = signatureImagePath; if (string.IsNullOrWhiteSpace(imagePath)) { imagePath = Path.Combine(PubUtil.PdfSignImagePath, DefaultSignatureImageFileName); } else if (!Path.IsPathRooted(imagePath)) { imagePath = Path.Combine(PubUtil.AbsolutelyPath, imagePath); } if (File.Exists(imagePath)) { return imagePath; } string[] extensions = new string[] { "*.png", "*.jpg", "*.jpeg", "*.bmp", "*.gif" }; foreach (string extension in extensions) { string[] files = Directory.GetFiles(PubUtil.PdfSignImagePath, extension); if (files.Length > 0) { return files[0]; } } throw new FileNotFoundException("未找到签章图片,请将签章图片放到:" + imagePath); } private static string DownloadPdf(string sourceUrl, string tempPath, int index) { string fileName = GetSafePdfFileName(sourceUrl, index); string localFile = Path.Combine(tempPath, "download_" + index.ToString("000") + "_" + fileName); if (File.Exists(sourceUrl)) { File.Copy(sourceUrl, localFile, true); return localFile; } Uri uri; if (!Uri.TryCreate(sourceUrl, UriKind.Absolute, out uri)) { throw new ArgumentException("PDF文件地址无效:" + sourceUrl); } TryEnableTls12(); using (TimeoutWebClient client = new TimeoutWebClient()) { client.Headers.Add(HttpRequestHeader.UserAgent, "Mozilla/5.0"); client.DownloadFile(uri, localFile); } return localFile; } private static string GetSafePdfFileName(string sourceUrl, int index) { string fileName = string.Empty; Uri uri; if (Uri.TryCreate(sourceUrl, UriKind.Absolute, out uri)) { fileName = HttpUtility.UrlDecode(Path.GetFileName(uri.LocalPath)); } else { fileName = Path.GetFileName(sourceUrl); } if (string.IsNullOrWhiteSpace(fileName)) { fileName = "file_" + index.ToString("000") + ".pdf"; } foreach (char invalidChar in Path.GetInvalidFileNameChars()) { fileName = fileName.Replace(invalidChar, '_'); } if (!Path.GetExtension(fileName).Equals(".pdf", StringComparison.OrdinalIgnoreCase)) { fileName = Path.GetFileNameWithoutExtension(fileName) + ".pdf"; } return fileName; } private static void AddCenteredSignature(string inputPdfPath, string outputPdfPath, string signatureImagePath, float signatureWidth, float opacity) { using (PdfReader reader = new PdfReader(inputPdfPath)) { using (FileStream outputStream = new FileStream(outputPdfPath, FileMode.Create, FileAccess.Write, FileShare.None)) { PdfStamper stamper = null; try { stamper = new PdfStamper(reader, outputStream); for (int pageIndex = 1; pageIndex <= reader.NumberOfPages; pageIndex++) { iTextSharp.text.Rectangle pageSize = reader.GetPageSizeWithRotation(pageIndex); iTextSharp.text.Image signature = iTextSharp.text.Image.GetInstance(signatureImagePath); float width = Math.Min(signatureWidth, pageSize.Width * 0.6f); float height = width * signature.Height / signature.Width; float left = pageSize.Left + (pageSize.Width - width) / 2f; float bottom = pageSize.Bottom + (pageSize.Height - height) / 2f; signature.ScaleAbsolute(width, height); signature.SetAbsolutePosition(left, bottom); PdfContentByte content = stamper.GetOverContent(pageIndex); PdfGState state = new PdfGState(); state.FillOpacity = opacity; content.SaveState(); content.SetGState(state); content.AddImage(signature); content.RestoreState(); } } finally { if (stamper != null) { stamper.Close(); } } } } } private static void MergePdfFiles(List pdfFiles, string outputPdfPath) { iTextSharp.text.Document document = null; PdfCopy copy = null; FileStream outputStream = null; try { outputStream = new FileStream(outputPdfPath, FileMode.Create, FileAccess.Write, FileShare.None); for (int fileIndex = 0; fileIndex < pdfFiles.Count; fileIndex++) { using (PdfReader reader = new PdfReader(pdfFiles[fileIndex])) { if (document == null) { document = new iTextSharp.text.Document(reader.GetPageSizeWithRotation(1)); copy = new PdfCopy(document, outputStream); document.Open(); } for (int pageIndex = 1; pageIndex <= reader.NumberOfPages; pageIndex++) { copy.AddPage(copy.GetImportedPage(reader, pageIndex)); } } } } finally { if (document != null) { document.Close(); } else if (outputStream != null) { outputStream.Close(); } } } private static void ShowPreviewForm(string pdfFile) { if (Thread.CurrentThread.GetApartmentState() != ApartmentState.STA) { throw new InvalidOperationException("签章打印预览需要在UI线程调用。"); } using (SignaturePreviewForm previewForm = new SignaturePreviewForm(pdfFile)) { Form owner = Form.ActiveForm; if (owner != null && !owner.IsDisposed) { previewForm.ShowDialog(owner); } else { previewForm.ShowDialog(); } } } private static void TryEnableTls12() { try { ServicePointManager.SecurityProtocol = ServicePointManager.SecurityProtocol | (SecurityProtocolType)3072; } catch { } } private class TimeoutWebClient : WebClient { public int Timeout { get; set; } public TimeoutWebClient() { Timeout = 60000; } protected override WebRequest GetWebRequest(Uri address) { WebRequest request = base.GetWebRequest(address); if (request != null) { request.Timeout = Timeout; } return request; } } private class SignaturePreviewForm : Form { private readonly string _pdfFile; private readonly PDFDocument _document; private readonly PDFPageView _pageView; private readonly ToolStripLabel _pageLabel; public SignaturePreviewForm(string pdfFile) { _pdfFile = pdfFile; _document = new PDFDocument(); _pageView = new PDFPageView(); _pageLabel = new ToolStripLabel(); InitializeComponent(); Load += OnFormLoad; FormClosed += OnFormClosed; } private void InitializeComponent() { Text = "签章打印预览"; StartPosition = FormStartPosition.CenterScreen; Width = 1000; Height = 760; ToolStrip toolStrip = new ToolStrip(); toolStrip.GripStyle = ToolStripGripStyle.Hidden; toolStrip.Dock = DockStyle.Top; ToolStripButton printButton = new ToolStripButton("打印"); printButton.Click += OnPrintClick; ToolStripButton firstButton = new ToolStripButton("首页"); firstButton.Click += OnFirstClick; ToolStripButton prevButton = new ToolStripButton("上一页"); prevButton.Click += OnPrevClick; ToolStripButton nextButton = new ToolStripButton("下一页"); nextButton.Click += OnNextClick; ToolStripButton lastButton = new ToolStripButton("末页"); lastButton.Click += OnLastClick; ToolStripButton zoomOutButton = new ToolStripButton("缩小"); zoomOutButton.Click += OnZoomOutClick; ToolStripButton zoomInButton = new ToolStripButton("放大"); zoomInButton.Click += OnZoomInClick; toolStrip.Items.Add(printButton); toolStrip.Items.Add(new ToolStripSeparator()); toolStrip.Items.Add(firstButton); toolStrip.Items.Add(prevButton); toolStrip.Items.Add(nextButton); toolStrip.Items.Add(lastButton); toolStrip.Items.Add(new ToolStripSeparator()); toolStrip.Items.Add(zoomOutButton); toolStrip.Items.Add(zoomInButton); toolStrip.Items.Add(new ToolStripSeparator()); toolStrip.Items.Add(_pageLabel); _document.PageLayout = PDFPageLayout.SinglePage; _document.PageMode = PDFPageMode.UseNone; _pageView.Dock = DockStyle.Fill; _pageView.Document = _document; _pageView.PageDisplayLayout = PDFPageDisplayLayout.OneColumn; _pageView.ZoomMode = PDFZoomMode.FitWidth; _pageView.WorkMode = UserInteractiveWorkMode.None; _pageView.BackColor = Color.White; Controls.Add(_pageView); Controls.Add(toolStrip); } private void OnFormLoad(object sender, EventArgs e) { _document.Load(_pdfFile); _pageView.PageNumber = 0; UpdatePageLabel(); } private void OnFormClosed(object sender, FormClosedEventArgs e) { try { _document.Close(); } catch { } } private void OnPrintClick(object sender, EventArgs e) { try { using (PrintDialog printDialog = new PrintDialog()) { printDialog.AllowSomePages = true; printDialog.UseEXDialog = true; printDialog.PrinterSettings = new PrinterSettings(); if (printDialog.ShowDialog(this) != DialogResult.OK) { return; } PDFPrintSettings settings = new PDFPrintSettings(printDialog.PrinterSettings); settings.DocumentName = Path.GetFileName(_pdfFile); settings.AutoRotate = true; settings.PageScaling = PageScaling.FitToPrinterMarginsProportional; PDFPrintContent content = PDFPrintContent.Page | PDFPrintContent.Annotations | PDFPrintContent.FormFields; _document.Print(settings, content); } } catch (Exception ex) { MessageUtil.Show("签章PDF打印失败", ex.Message); } } private void OnFirstClick(object sender, EventArgs e) { _pageView.GoToFirstPage(); UpdatePageLabel(); } private void OnPrevClick(object sender, EventArgs e) { _pageView.GoToPrevPage(); UpdatePageLabel(); } private void OnNextClick(object sender, EventArgs e) { _pageView.GoToNextPage(); UpdatePageLabel(); } private void OnLastClick(object sender, EventArgs e) { _pageView.GoToLastPage(); UpdatePageLabel(); } private void OnZoomOutClick(object sender, EventArgs e) { _pageView.ZoomMode = PDFZoomMode.Custom; _pageView.Zoom = Math.Max(0.25d, _pageView.Zoom - 0.1d); } private void OnZoomInClick(object sender, EventArgs e) { _pageView.ZoomMode = PDFZoomMode.Custom; _pageView.Zoom = Math.Min(5d, _pageView.Zoom + 0.1d); } private void UpdatePageLabel() { int pageCount = _document.PageCount; int pageNumber = pageCount == 0 ? 0 : _pageView.PageNumber + 1; _pageLabel.Text = "第 " + pageNumber + " / " + pageCount + " 页"; } } } }