using System; using System.Collections.Generic; using System.IO; using System.Windows.Forms; using System.Xml; using iTextSharp.text; using iTextSharp.text.pdf; using MuPdfSharp; using PDFPatcher.Common; using PDFPatcher.Model; using Ext = PDFPatcher.Constants.FileExtensions; namespace PDFPatcher.Processor { internal static class Worker { const string OperationCanceled = "已经取消操作。"; private static PdfReader OpenPdf(string sourceFile, bool loadPartial, bool removeUnusedObjects) { try { Tracker.TraceMessage(Tracker.Category.ImportantMessage, String.Concat("打开 PDF 文件:<<", sourceFile, ">>。")); return PdfHelper.OpenPdfFile(sourceFile, loadPartial, removeUnusedObjects); } catch (FileNotFoundException) { FormHelper.ErrorBox(String.Concat("找不到文件:“", sourceFile, "”。")); return null; } catch (iTextSharp.text.exceptions.BadPasswordException) { Tracker.TraceMessage(Tracker.Category.Error, Messages.PasswordInvalid); FormHelper.ErrorBox(Messages.PasswordInvalid); return null; } catch (Exception ex) { FormHelper.ErrorBox("在打开 PDF 文件时遇到错误:\n" + ex.Message); #if DEBUG Tracker.TraceMessage(ex); #endif return null; } } internal static void ExtractImages(string sourceFile, ImageExtracterOptions options) { const int loadDocProgressWeight = 10; Tracker.TraceMessage(Tracker.Category.InputFile, sourceFile); var pdf = OpenPdf(sourceFile, true, false); // 由于导出图片一般不需要往复访问文档,可用此选项降低内存占用及提高打开速度 if (pdf == null) { return; } var targetPath = options.OutputPath; var ranges = PageRangeCollection.Parse(options.PageRange, 1, pdf.NumberOfPages, true); var loadCount = loadDocProgressWeight + ranges.TotalPages; Tracker.SetProgressGoal(loadCount); var op = targetPath; var om = options.FileMask; try { if (Directory.Exists(targetPath) == false) { Directory.CreateDirectory(targetPath); } Tracker.TraceMessage("正在导出图片。"); Tracker.TrackProgress(loadDocProgressWeight); if (FileHelper.HasFileNameMacro(op)) { options.OutputPath = ReplaceTargetFileNameMacros(sourceFile, op, pdf); } if (FileHelper.HasFileNameMacro(om)) { options.FileMask = ReplaceTargetFileNameMacros(sourceFile, om, pdf); } var exp = new ImageExtractor(options, pdf); foreach (var range in ranges) { foreach (var i in range) { exp.ExtractPageImages(pdf, i); if (exp.InfoList.Count > 0) { Tracker.TraceMessage(Tracker.Category.OutputFile, exp.InfoList[exp.InfoList.Count - 1].FileName); } Tracker.IncrementProgress(1); } } Tracker.TrackProgress(loadCount); Tracker.TraceMessage(Tracker.Category.Alert, "成功提取图片文件,存放目录为:<<" + targetPath + ">>。"); } catch (OperationCanceledException) { Tracker.TraceMessage(Tracker.Category.ImportantMessage, OperationCanceled); } catch (Exception ex) { Tracker.TraceMessage(ex); FormHelper.ErrorBox("在导出图片时遇到错误:\n" + ex.Message); } finally { options.OutputPath = op; options.FileMask = om; pdf?.Close(); } } internal static void RenderPages(string sourceFile, ImageRendererOptions options) { const int loadDocProgressWeight = 10; Tracker.TraceMessage(Tracker.Category.InputFile, sourceFile); options.TintColor = System.Drawing.Color.Transparent; if (Directory.Exists(options.ExtractImagePath) == false) { Directory.CreateDirectory(options.ExtractImagePath); } MuDocument mupdf = null; try { mupdf = PdfHelper.OpenMuDocument(sourceFile); var ranges = PageRangeCollection.Parse(options.ExtractPageRange, 1, mupdf.PageCount, true); int loadCount = loadDocProgressWeight + ranges.TotalPages; Tracker.SetProgressGoal(loadCount); Tracker.TraceMessage("正在转换图片。"); Tracker.TrackProgress(loadDocProgressWeight); foreach (var range in ranges) { foreach (var i in range) { var fn = FileHelper.CombinePath(options.ExtractImagePath, i.ToString(options.FileMask) + options.FileFormatExtension); using (var p = mupdf.LoadPage(i)) using (var bmp = p.RenderBitmapPage(options.ImageWidth, 0, options)) { if (bmp == null) { Tracker.TraceMessage(Tracker.Category.Error, "页面" + i + "的尺寸为空。"); } else if (options.FileFormat == ImageFormat.Tiff) { using (var b = Imaging.BitmapHelper.ToBitonal(bmp)) { Imaging.BitmapHelper.SaveAs(b, fn); } } else { var uc = Imaging.BitmapHelper.GetPalette(bmp); if (uc.Length > 256 && options.Quantize) { using (var b = Imaging.WuQuantizer.QuantizeImage(bmp)) { Imaging.BitmapHelper.SaveAs(b, fn); } } else if (uc.Length <= 256 && Imaging.BitmapHelper.IsIndexed(bmp) == false) { using (var b = Imaging.BitmapHelper.ToIndexImage(bmp, uc)) { Imaging.BitmapHelper.SaveAs(b, fn); } } else if (options.FileFormat == ImageFormat.Jpeg) { Imaging.JpgHelper.Save(bmp, fn, options.JpegQuality); } else { Imaging.BitmapHelper.SaveAs(bmp, fn); } } } Tracker.TraceMessage(Tracker.Category.OutputFile, fn); Tracker.IncrementProgress(1); } } Tracker.TrackProgress(loadCount); Tracker.TraceMessage(Tracker.Category.Alert, "成功转换图片文件,存放目录为:<<" + options.ExtractImagePath + ">>。"); } catch (OperationCanceledException) { Tracker.TraceMessage(Tracker.Category.ImportantMessage, OperationCanceled); } catch (Exception ex) { Tracker.TraceMessage(ex); FormHelper.ErrorBox("在转换图片时遇到错误:\n" + ex.Message); } finally { mupdf?.Dispose(); } } internal static void RenderPagesToPdf(string sourceFile, ImageRendererOptions options, Document doc) { const int loadDocProgressWeight = 10; Tracker.TraceMessage(Tracker.Category.InputFile, sourceFile); Tracker.TraceMessage(Tracker.Category.OutputFile, options.ExtractImagePath); options.TintColor = System.Drawing.Color.Transparent; ((FilePath)options.ExtractImagePath).CreateContainingDirectory(); MuDocument mupdf = null; try { mupdf = PdfHelper.OpenMuDocument(sourceFile); var ranges = PageRangeCollection.Parse(options.ExtractPageRange, 1, mupdf.PageCount, true); int loadCount = loadDocProgressWeight + ranges.TotalPages; Tracker.SetProgressGoal(loadCount); Tracker.TraceMessage("正在转换图片。"); Tracker.TrackProgress(loadDocProgressWeight); MemoryStream ms = new MemoryStream(); var fn = options.FileFormatExtension; foreach (var range in ranges) { foreach (var i in range) { ms.Position = 0; using (var p = mupdf.LoadPage(i)) using (var bmp = p.RenderBitmapPage(options.ImageWidth, 0, options)) { if (bmp == null) { Tracker.TraceMessage(Tracker.Category.Error, "页面" + i + "的尺寸为空。"); } else if (options.FileFormat == ImageFormat.Tiff) { using (var b = Imaging.BitmapHelper.ToBitonal(bmp)) { Imaging.BitmapHelper.SaveAs(b, fn, ms); } } else { var uc = Imaging.BitmapHelper.GetPalette(bmp); if (uc.Length > 256 && options.Quantize) { using (var b = Imaging.WuQuantizer.QuantizeImage(bmp)) { Imaging.BitmapHelper.SaveAs(b, fn, ms); } } else if (uc.Length <= 256 && Imaging.BitmapHelper.IsIndexed(bmp) == false) { using (var b = Imaging.BitmapHelper.ToIndexImage(bmp, uc)) { Imaging.BitmapHelper.SaveAs(b, fn, ms); } } else if (options.FileFormat == ImageFormat.Jpeg) { Imaging.JpgHelper.Save(bmp, ms, options.JpegQuality); } else { Imaging.BitmapHelper.SaveAs(bmp, fn, ms); } } doc.SetPageSize((options.Rotation % 180) == 0 ? new iTextSharp.text.Rectangle(p.Bound.Width, p.Bound.Height) : new iTextSharp.text.Rectangle(p.Bound.Height, p.Bound.Width)); doc.NewPage(); } var image = Image.GetInstance(ms.ToArray(), true); image.ScalePercent(72f / image.DpiX.SubstituteDefault(72) * 100f, 72f / image.DpiY.SubstituteDefault(72) * 100f); doc.Add(image); Tracker.IncrementProgress(1); } } Tracker.TrackProgress(loadCount); Tracker.TraceMessage(Tracker.Category.Alert, "成功转换图片文件,存放位置为:<<" + options.ExtractImagePath + ">>。"); } catch (OperationCanceledException) { Tracker.TraceMessage(Tracker.Category.ImportantMessage, OperationCanceled); } catch (Exception ex) { Tracker.TraceMessage(ex); FormHelper.ErrorBox("在转换图片时遇到错误:\n" + ex.Message); } finally { mupdf?.Dispose(); } } internal static void ExportInfo(FilePath sourceFile, FilePath targetFile) { Tracker.TraceMessage(Tracker.Category.InputFile, sourceFile); Tracker.TraceMessage(Tracker.Category.OutputFile, targetFile); var r = OpenPdf(sourceFile, AppContext.LoadPartialPdfFile, false); if (r == null) { return; } if (targetFile.IsValidPath == false || targetFile.FileName.Length == 0) { Tracker.TraceMessage(Tracker.Category.Error, Messages.InfoFileNameInvalid); FormHelper.ErrorBox(Messages.InfoFileNameInvalid); return; } targetFile = FileHelper.MakePathRootedAndWithExtension(targetFile, sourceFile, Ext.Xml, false); targetFile.CreateContainingDirectory(); var export = new DocInfoExporter(r, AppContext.Exporter); if (AppContext.Exporter.ExtractImages) { AppContext.Exporter.Images.OutputPath = FileHelper.CombinePath(targetFile.Directory, targetFile.FileNameWithoutExtension + "图片文件\\"); } try { Tracker.TraceMessage("正在导出信息文件。"); if (targetFile.HasExtension(Ext.Txt)) { Tracker.SetProgressGoal(50); using (TextWriter w = new StreamWriter(targetFile, false, AppContext.Exporter.GetEncoding())) { DocInfoExporter.WriteDocumentInfoAttributes(w, sourceFile, r.NumberOfPages); export.ExportDocument(w); w.WriteLine(); Tracker.SetProgressGoal(10); r.ConsolidateNamedDestinations(); export.ExportBookmarks(OutlineManager.GetBookmark(r, new UnitConverter()), w, 0, false); } } else { var workload = export.EstimateWorkload(); Tracker.SetProgressGoal(workload); using (XmlWriter w = XmlWriter.Create(targetFile, DocInfoExporter.GetWriterSettings())) { w.WriteStartDocument(); w.WriteStartElement(Constants.PdfInfo); DocInfoExporter.WriteDocumentInfoAttributes(w, sourceFile, r.NumberOfPages); export.ExportDocument(w); w.WriteEndElement(); } } Tracker.TraceMessage(Tracker.Category.Alert, "成功导出信息文件到 <<" + targetFile + ">>。"); //if (this._BookmarkBox.Text.Length == 0) { // this._BookmarkBox.Text = targetFile; //} //if (Common.Form.YesNoBox ("已完成导出信息文件到 " + targetFile + ",是否使用内置的编辑器编辑文件?") == DialogResult.Yes) { // ShowInfoFileEditorForm (targetFile); //} } catch (OperationCanceledException) { Tracker.TraceMessage(Tracker.Category.ImportantMessage, OperationCanceled); } catch (System.Text.EncoderFallbackException ex) { Tracker.TraceMessage(ex); FormHelper.ErrorBox("在导出信息文件时遇到错误:\n" + ex.Message + "\n\n请选择在导出信息选项中选择其它编码方式。"); } catch (Exception ex) { Tracker.TraceMessage(ex); FormHelper.ErrorBox("在导出信息文件时遇到错误:\n" + ex.Message); } finally { r?.Close(); } } internal static void ConvertBookmark(string sourceFile, string targetFile) { Tracker.TraceMessage(Tracker.Category.InputFile, sourceFile); Tracker.TraceMessage(Tracker.Category.OutputFile, targetFile); try { var infoDoc = new PdfInfoXmlDocument(); OutlineManager.ImportSimpleBookmarks(sourceFile, infoDoc); using (var w = XmlWriter.Create(targetFile, DocInfoExporter.GetWriterSettings())) { infoDoc.Save(w); } Tracker.TraceMessage(Tracker.Category.Alert, "成功转换信息文件到 <<" + targetFile + ">>。"); } catch (OperationCanceledException) { Tracker.TraceMessage(Tracker.Category.ImportantMessage, OperationCanceled); } catch (Exception ex) { Tracker.TraceMessage(ex); FormHelper.ErrorBox("在转换书签文件时发生错误:\n" + ex.Message); } } internal static void PatchDocument(SourceItem.Pdf sourceFile, FilePath targetFile, object infoDoc, ImporterOptions options, PatcherOptions pdfSettings) { var sourcePath = sourceFile.FilePath.ToString(); if (sourceFile.FilePath.IsEmpty) { Tracker.TraceMessage(Tracker.Category.Error, "输入文件名为空。"); return; } if (String.IsNullOrEmpty(targetFile) || targetFile.IsEmpty) { Tracker.TraceMessage(Tracker.Category.Error, "输出文件名为空。"); return; } Tracker.TraceMessage(Tracker.Category.InputFile, sourcePath); Tracker.TraceMessage(Tracker.Category.OutputFile, targetFile); bool overwriteSource = false; var pdf = OpenPdf(sourcePath, AppContext.LoadPartialPdfFile, true); if (pdf == null) { return; } try { DocInfoImporter import; var xInfoDoc = infoDoc as PdfInfoXmlDocument; var docPath = infoDoc as string; if (xInfoDoc != null) { import = new DocInfoImporter(options, pdf, pdfSettings, xInfoDoc); } else if (String.IsNullOrEmpty(docPath)) { Tracker.TraceMessage("没有指定信息文件,将按程序界面的设置执行补丁。"); import = new DocInfoImporter(options, pdf, pdfSettings, null); Tracker.TraceMessage("加载源 PDF 文件信息完毕,准备执行补丁操作。"); } else { Tracker.TraceMessage(Tracker.Category.ImportantMessage, String.Concat("加载信息文件:<<", docPath, ">>。")); import = new DocInfoImporter(options, docPath); if (import.InfoDoc != null && VerifyInfoDocument(import.InfoDoc) == false) return; } var pdfEngine = new PdfProcessingEngine(pdf); pdfEngine.CreateProcessors(pdfSettings); int workload = 110; workload += pdfEngine.EstimateWorkload(); Tracker.SetProgressGoal(workload); Tracker.TrackProgress(10); if (pdf.ConfirmUnethicalMode() == false) { return; } Tracker.TraceMessage("导入文档属性。"); //var pdfInfo = DocInfoExporter.RewriteDocInfoWithEncoding (pdf.Trailer.GetAsDict (PdfName.INFO), AppContext.Encodings.DocInfoEncoding); var info = pdfSettings.MetaData.SpecifyMetaData ? pdfSettings.MetaData //: sourceFile.DocInfo; // 不再使用书签文件的文件属性 : import.ImportDocumentInformation() ?? sourceFile.DocInfo; DocInfoImporter.ImportDocumentInformation(info, pdf, sourcePath); Tracker.TrackProgress(20); if (FileHelper.HasFileNameMacro(targetFile)) { targetFile = ReplaceTargetFileNameMacros(sourcePath, targetFile, pdf); } targetFile = FileHelper.MakePathRootedAndWithExtension(targetFile, sourcePath, Ext.Pdf, true); targetFile = targetFile.Normalize(); Tracker.TraceMessage(Tracker.Category.OutputFile, targetFile); if (FileHelper.ComparePath(sourcePath, targetFile)) { if (FormHelper.YesNoBox("是否覆盖原始 PDF 文件?\n如处理过程出现错误,覆盖操作将导致原始数据无法恢复!") == DialogResult.No) { Tracker.TraceMessage(Tracker.Category.Error, Messages.SourceFileEqualsTargetFile); return; } overwriteSource = true; targetFile = sourceFile.FilePath.ChangeExtension(Ext.Tmp).ToString(); } else if (FileHelper.CheckOverwrite(targetFile) == false) { return; } targetFile.CreateContainingDirectory(); using (Stream s = new FileStream(targetFile, FileMode.Create)) { var st = new PdfStamper(pdf, s); pdfEngine.ProcessDocument(st.Writer); #region 处理信息文件 var processors = new List(); if (pdfSettings.ViewerPreferences.RemoveZoomRate) { processors.Add(new RemoveZoomRateProcessor()); } if (pdfEngine.ExtraData.ContainsKey(DocProcessorContext.CoordinateTransition) && pdfSettings.UnifiedPageSettings.ScaleContent) { processors.Add(new GotoDestinationProcessor() { TransitionMapper = pdfEngine.ExtraData[DocProcessorContext.CoordinateTransition] as CoordinateTranslationSettings[] }); } if (pdfSettings.ViewerPreferences.ForceInternalLink) { processors.Add(new ForceInternalDestinationProcessor()); } //var cts = new CoordinateTranslationSettings[pdf.NumberOfPages + 1]; // 页面的位置偏移量 //var sc = false; //if (pdfSettings.PageSettings.Count > 0) { // Tracker.TraceMessage ("重设页面尺寸。"); // pdf.ResetReleasePage (); // foreach (var item in pdfSettings.PageSettings) { // var ranges = PageRangeCollection.Parse (item.PageRanges, 1, pdf.NumberOfPages, true); // foreach (var range in ranges) { // foreach (var i in range) { // var s = PageDimensionProcessor.ResizePage (pdf.GetPageN (i), item.PaperSize, item.HorizontalAlign, item.VerticalAlign, -1, item.ScaleContent); // if (item.ScaleContent && s.XScale != 1 && s.YScale != 1) { // PageDimensionProcessor.ScaleContent (pdf, i, s); // cts[i] = s; // TODO: 需要解决重复指定相同页面的问题 // sc = true; // } // } // } // } // pdf.ResetReleasePage (); //} if (pdfSettings.UnifiedPageSettings.PaperSize.PaperName == PaperSize.AsPageSize) { import.ImportPageSettings(pdf); } //if (sc == false) { // cts = null; //} #endregion if (pdfSettings.FullCompression) { st.SetFullCompression(); } //st.Writer.CompressionLevel = ContextData.CreateDocumentOptions.CompressionLevel; var labels = DocInfoImporter.ImportPageLabels(pdfSettings.PageLabels) ?? import.ImportPageLabels(); if (labels != null) { st.Writer.PageLabels = labels; } if (options.ImportPageLinks || pdfSettings.UnifiedPageSettings.PaperSize.PaperName != PaperSize.AsPageSize/* sc*/) { import.ImportPageLinks(pdf, st); } if (import.InfoDoc != null) { PdfDocumentCreator.ProcessInfoItem(import.InfoDoc.DocumentElement.SelectSingleNode(Constants.PageLink) as XmlElement, processors); PdfDocumentCreator.ProcessInfoItem(import.InfoDoc.DocumentElement.SelectSingleNode(Constants.NamedDestination) as XmlElement, processors); } XmlElement bookmarks = null; if (options.ImportBookmarks && pdfSettings.RemoveBookmarks == false || xInfoDoc != null) { Tracker.TraceMessage("导入书签。"); bookmarks = import.GetBookmarks() ?? OutlineManager.GetBookmark(pdf, new UnitConverter() { Unit = Constants.Units.Point }); } if (bookmarks != null) { // 预处理书签 processors.Add(new CollapseBookmarkProcessor() { BookmarkStatus = pdfSettings.ViewerPreferences.CollapseBookmark }); PdfDocumentCreator.ProcessInfoItem(bookmarks, processors); if (bookmarks.ChildNodes.Count > 0 || xInfoDoc != null) { import.ImportNamedDestinations(pdf, st.Writer); OutlineManager.KillOutline(pdf); var bm = OutlineManager.WriteOutline(st.Writer, bookmarks, pdf.NumberOfPages); if (bm != null) { pdf.Catalog.Put(PdfName.OUTLINES, bm); } if (pdf.Catalog.Contains(PdfName.PAGEMODE) == false) { pdf.Catalog.Put(PdfName.PAGEMODE, PdfName.USEOUTLINES); } } else if (String.IsNullOrEmpty(docPath) == false) { OutlineManager.KillOutline(pdf); } } Tracker.IncrementProgress(10); Tracker.TraceMessage("导入文档设置。"); import.ImportViewerPreferences(pdf); DocInfoImporter.OverrideViewerPreferences(pdfSettings.ViewerPreferences, pdf, st.Writer); //import.OverrideDocumentSettings (pdf); Tracker.IncrementProgress(5); Tracker.TraceMessage("清理输出文件。"); pdf.RemoveUnusedObjects(); if (pdf.AcroForm == null) { pdf.Trailer.Locate(PdfName.ROOT).Remove(PdfName.ACROFORM); } Tracker.IncrementProgress(10); Tracker.TraceMessage("保存文件:" + targetFile); st.Close(); Tracker.TrackProgress(workload); } } catch (OperationCanceledException) { Tracker.TraceMessage(Tracker.Category.ImportantMessage, OperationCanceled); return; } catch (Exception ex) { Tracker.TraceMessage(ex); FormHelper.ErrorBox("在导入信息时发生错误:\n" + ex.Message); return; } finally { if (pdf != null) { try { pdf.Close(); } catch (Exception ex) { // ignore exception Tracker.TraceMessage(ex); } } } if (overwriteSource) { try { Tracker.TraceMessage("覆盖输入文件。"); File.Delete(sourcePath); File.Move(targetFile, sourcePath); targetFile = sourcePath; } catch (Exception) { FormHelper.ErrorBox("无法覆盖输入文件。\n请手工将“" + Ext.Tmp + "”后缀的临时文件更名为输入文件。"); } } Tracker.TraceMessage(Tracker.Category.Alert, "成功导入信息到 <<" + targetFile + ">>。"); } internal static string ReplaceTargetFileNameMacros(string sourceFile, string targetFile, PdfReader pdf) { try { return ReplaceTargetFileNameMacros(sourceFile, targetFile, pdf.Trailer.GetAsDict(PdfName.INFO), pdf.NumberOfPages); } catch (IOException) { return ReplaceTargetFileNameMacros(sourceFile, targetFile, null as PdfDictionary, 0); } } /// /// 替换目标文件名的替代符。 /// /// 用于替换的源文件名。 /// 包含替代符的目标文件名。 /// 源文件的元数据属性。 /// 源文件的页数。 /// 替换目标文件名后的文件名。 internal static string ReplaceTargetFileNameMacros(string sourceFile, string targetFile, PdfDictionary info, int pageCount) { string p = null; // 文档属性 if (info != null) { if (info.Contains(PdfName.TITLE)) { p = FileHelper.GetValidFileName(info.GetAsString(PdfName.TITLE).ToUnicodeString()); } targetFile = targetFile.Replace(Constants.FileNameMacros.TitleProperty, p ?? Path.GetFileNameWithoutExtension(sourceFile)); p = String.Empty; if (info.Contains(PdfName.SUBJECT)) { p = FileHelper.GetValidFileName(info.GetAsString(PdfName.SUBJECT).ToUnicodeString()); } targetFile = targetFile.Replace(Constants.FileNameMacros.SubjectProperty, p); p = String.Empty; if (info.Contains(PdfName.AUTHOR)) { p = FileHelper.GetValidFileName(info.GetAsString(PdfName.AUTHOR).ToUnicodeString()); } targetFile = targetFile.Replace(Constants.FileNameMacros.AuthorProperty, p); p = String.Empty; if (info.Contains(PdfName.KEYWORDS)) { p = FileHelper.GetValidFileName(info.GetAsString(PdfName.KEYWORDS).ToUnicodeString()); } targetFile = targetFile.Replace(Constants.FileNameMacros.KeywordsProperty, p); } return targetFile .Replace(Constants.FileNameMacros.FileName, Path.GetFileNameWithoutExtension(sourceFile)) .Replace(Constants.FileNameMacros.FolderName, Path.GetFileName(Path.GetDirectoryName(sourceFile))) .Replace(Constants.FileNameMacros.PathName, FileHelper.CombinePath(sourceFile.Substring(0, sourceFile.LastIndexOf(Path.DirectorySeparatorChar)), String.Empty)) .Replace(Constants.FileNameMacros.PageCount, pageCount.ToText()); } private static bool VerifyInfoDocument(XmlDocument infoDoc) { var root = infoDoc.DocumentElement; switch (root.Name) { case Constants.PdfInfo: // 使用中文的书签 var v = root.GetAttribute(Constants.Info.ProductVersion); if (v != Constants.InfoDocVersion && FormHelper.YesNoBox(String.Concat("信息文件不是用这个版本的程序生成的,可能会导入不成功,是否继续?\n当前程序的版本是:", Application.ProductVersion, "\n信息文件的导出程序版本是:", v)) == DialogResult.No) { return false; } break; default: FormHelper.ErrorBox("信息文件格式有误,根元素不是“" + Constants.PdfInfo + "”。"); return false; } return true; } internal static void ExtractPages(ExtractPageOptions options, string sourceFile, string targetFile) { Tracker.TraceMessage(Tracker.Category.InputFile, sourceFile); Tracker.TraceMessage(Tracker.Category.OutputFile, targetFile); var r = OpenPdf(sourceFile, AppContext.LoadPartialPdfFile, false); if (r == null) { return; } try { if (r.ConfirmUnethicalMode() == false) { return; } PdfPageExtractor.ExtractPages(options, sourceFile, targetFile, r); } catch (OperationCanceledException) { Tracker.TraceMessage(Tracker.Category.ImportantMessage, OperationCanceled); } catch (Exception ex) { Tracker.TraceMessage(ex); FormHelper.ErrorBox("在导出页面内容时出错:\n" + ex.Message); } finally { if (r != null) { try { r.Close(); } catch (Exception ex) { // ignore exception Tracker.TraceMessage(ex); } } } } internal static void MergeDocuments(ICollection sources, FilePath targetFile, string infoFile) { targetFile = targetFile.EnsureExtension(Ext.Pdf); Tracker.TraceMessage(Tracker.Category.OutputFile, targetFile.ToString()); var option = AppContext.Merger; var impOptions = AppContext.Importer; Document doc = null; var sink = new DocumentSink(sources, true); if (sink.Workload == 0) { Tracker.TraceMessage(Tracker.Category.ImportantMessage, "合并文件列表不包含图片或 PDF 文件。"); return; } Tracker.SetProgressGoal(10 + sink.Workload); Tracker.TrackProgress(1); try { GeneralInfo info = null; PdfPageLabels labels = null; BookmarkContainer bookmarks = null; if (String.IsNullOrEmpty(infoFile) == false) { Tracker.TraceMessage(Tracker.Category.ImportantMessage, String.Concat("加载信息文件:<<", infoFile, ">>。")); var import = new DocInfoImporter(impOptions, infoFile); info = import.ImportDocumentInformation(); labels = import.ImportPageLabels(); bookmarks = import.GetBookmarks(); } if (labels == null && option.PageLabels.Count > 0) { labels = DocInfoImporter.ImportPageLabels(option.PageLabels); } var f = targetFile.EnsureExtension(Ext.Pdf); Tracker.TraceMessage(Tracker.Category.OutputFile, f.ToString()); Tracker.TraceMessage(Tracker.Category.ImportantMessage, String.Concat("输出到文件:", f, "。")); if (f.IsValidPath == false) { Tracker.TraceMessage(Tracker.Category.Error, "输出文件路径无效。"); return; } f.CreateContainingDirectory(); using (Stream s = new FileStream(f, FileMode.Create)) { var ps = option.PageSettings; doc = new Document( new iTextSharp.text.Rectangle(ps.PaperSize.Width, ps.PaperSize.Height), ps.Margins.Left, ps.Margins.Right, ps.Margins.Top, ps.Margins.Bottom ); PdfCopy w = option.Deduplicate ? new PdfSmartCopy(doc, s) : new PdfCopy(doc, s); if (option.FullCompression) { w.SetFullCompression(); } //w.CompressionLevel = ContextData.CreateDocumentOptions.CompressionLevel; doc.Open(); doc.AddCreator(Application.ProductName + " " + Application.ProductVersion); if (labels != null) { w.PageLabels = labels; } Tracker.IncrementProgress(10); var creator = new PdfDocumentCreator(sink, option, impOptions, doc, w); foreach (var item in sources) { creator.ProcessFile(item, creator.PdfBookmarks.BookmarkRoot); } Tracker.TraceMessage("设置文档选项。"); DocInfoImporter.ImportDocumentInformation(option.MetaData.SpecifyMetaData ? option.MetaData : info, doc); DocInfoImporter.OverrideViewerPreferences(option.ViewerPreferences, null, w); if ((bookmarks == null || bookmarks.HasChildNodes == false) && creator.PdfBookmarks.DocumentElement.HasChildNodes) { bookmarks = creator.PdfBookmarks.BookmarkRoot; } if (bookmarks != null && bookmarks.HasChildNodes) { Tracker.TraceMessage("写入文档书签。"); OutlineManager.WriteOutline(w, bookmarks, w.GetPageCount()); w.ViewerPreferences = PdfWriter.PageModeUseOutlines; } Tracker.TraceMessage("写入文件索引。"); Tracker.TraceMessage(Tracker.Category.Alert, $"合并了 {creator.InputDocumentCount.ToText()} 个文件共 {w.GetPageCount()} 页到:<<" + targetFile.ToString() + ">>。"); w.Close(); } } catch (OperationCanceledException) { Tracker.TraceMessage(Tracker.Category.ImportantMessage, OperationCanceled); } catch (Exception ex) { Tracker.TraceMessage(ex); FormHelper.ErrorBox("在生成文档时出错:\n" + ex.Message); } finally { if (doc != null) { try { doc.Close(); } catch (Exception ex) { // ignore exception Tracker.TraceMessage(ex); } } } } internal static void CreateBookmark(string sourceFile, string bookmarkFile, AutoBookmarkOptions options) { Tracker.TraceMessage(Tracker.Category.InputFile, sourceFile); Tracker.TraceMessage(Tracker.Category.OutputFile, bookmarkFile); var r = OpenPdf(sourceFile, AppContext.LoadPartialPdfFile, false); if (r == null) { return; } if (FileHelper.IsPathValid(bookmarkFile) == false || Path.GetFileName(bookmarkFile).Length == 0) { Tracker.TraceMessage(Tracker.Category.Error, Messages.InfoFileNameInvalid); FormHelper.ErrorBox(Messages.InfoFileNameInvalid); return; } bookmarkFile = FileHelper.MakePathRootedAndWithExtension(bookmarkFile, sourceFile, Ext.Xml, false); Tracker.TraceMessage(Tracker.Category.OutputFile, bookmarkFile); if (options.CreateBookmarkForFirstPage) { options.FirstPageTitle = Path.GetFileNameWithoutExtension(sourceFile); } var creator = new AutoBookmarkCreator(r, options); try { Tracker.TraceMessage("正在分析 PDF 文件。"); var workload = creator.EstimateWorkload(); Tracker.SetProgressGoal(workload); using (XmlWriter w = XmlWriter.Create(bookmarkFile, DocInfoExporter.GetWriterSettings())) { w.WriteStartDocument(); w.WriteStartElement(Constants.PdfInfo); w.WriteAttributeString(Constants.Info.ProductName, Application.ProductName); w.WriteAttributeString(Constants.Info.ProductVersion, Constants.InfoDocVersion); w.WriteAttributeString(Constants.Info.ExportDate, DateTime.Now.ToString("yyyy年MM月dd日 HH:mm:ss")); //w.WriteAttributeString (Constants.Info.DocumentName, Path.GetFileNameWithoutExtension (sourceFile)); w.WriteAttributeString(Constants.Info.DocumentPath, sourceFile); w.WriteAttributeString(Constants.Info.PageNumber, r.NumberOfPages.ToText()); creator.ExportAutoBookmarks(w, options); w.WriteEndElement(); } Tracker.TraceMessage(Tracker.Category.Alert, "成功导出信息文件到 <<" + bookmarkFile + ">>。"); } catch (OperationCanceledException) { Tracker.TraceMessage(Tracker.Category.ImportantMessage, OperationCanceled); } catch (System.Text.EncoderFallbackException ex) { Tracker.TraceMessage(ex); FormHelper.ErrorBox("在导出信息文件时遇到错误:\n" + ex.Message + "\n\n请选择在导出信息选项中选择其它编码方式。"); } catch (Exception ex) { Tracker.TraceMessage(ex); FormHelper.ErrorBox("在导出信息文件时遇到错误:\n" + ex.Message); } finally { if (r != null) { r.Close(); } } } internal static void Ocr(string sourceFile, string bookmarkFile, OcrOptions options) { Tracker.TraceMessage(Tracker.Category.InputFile, sourceFile); Tracker.TraceMessage(Tracker.Category.OutputFile, bookmarkFile); var r = OpenPdf(sourceFile, AppContext.LoadPartialPdfFile, false); if (r == null) { return; } bool noOutputFile = false; if (String.IsNullOrEmpty(bookmarkFile)) { noOutputFile = true; } else if (FileHelper.IsPathValid(bookmarkFile) == false || Path.GetFileName(bookmarkFile).Length == 0) { Tracker.TraceMessage(Tracker.Category.Error, Messages.InfoFileNameInvalid); FormHelper.ErrorBox(Messages.InfoFileNameInvalid); return; } try { if (FileHelper.CheckOverwrite(bookmarkFile) == false) { return; } } catch (OperationCanceledException) { return; } bookmarkFile = FileHelper.MakePathRootedAndWithExtension(bookmarkFile, sourceFile, Ext.Xml, false); Tracker.TraceMessage(Tracker.Category.OutputFile, bookmarkFile); OcrProcessor ocr; try { ocr = new OcrProcessor(r, options); } catch (FileNotFoundException ex) { Tracker.TraceMessage(ex); FormHelper.ErrorBox(ex.Message); return; } try { Tracker.TraceMessage("正在分析 PDF 文件。"); var workload = ocr.EstimateWorkload(); Tracker.SetProgressGoal(workload); if (noOutputFile) { ocr.PerformOcr(); } else if (new FilePath(bookmarkFile).HasExtension(Ext.Txt)) { Tracker.TraceMessage(Tracker.Category.OutputFile, bookmarkFile); Tracker.TraceMessage("输出简易信息文件:" + bookmarkFile); using (var w = new StreamWriter(bookmarkFile, false, AppContext.Exporter.GetEncoding())) { DocInfoExporter.WriteDocumentInfoAttributes(w, sourceFile, r.NumberOfPages); ocr.SetWriter(w); ocr.PerformOcr(); } } else { Tracker.TraceMessage(Tracker.Category.OutputFile, bookmarkFile); Tracker.TraceMessage("输出信息文件:" + bookmarkFile); using (var w = XmlWriter.Create(bookmarkFile, DocInfoExporter.GetWriterSettings())) { w.WriteStartDocument(); w.WriteStartElement(Constants.PdfInfo); DocInfoExporter.WriteDocumentInfoAttributes(w, sourceFile, r.NumberOfPages); ocr.SetWriter(w); ocr.PerformOcr(); w.WriteEndElement(); } } if (noOutputFile == false) { Tracker.TraceMessage(Tracker.Category.Alert, "已完成导出信息文件到 <<" + bookmarkFile + ">>。"); } else { Tracker.TraceMessage(Tracker.Category.Alert, "分析文档完毕。"); } } catch (OperationCanceledException) { Tracker.TraceMessage(Tracker.Category.ImportantMessage, OperationCanceled); } catch (System.Text.EncoderFallbackException ex) { Tracker.TraceMessage(ex); FormHelper.ErrorBox("在导出信息文件时遇到错误:\n" + ex.Message + "\n\n请选择在导出信息选项中选择其它编码方式。"); } catch (Exception ex) { Tracker.TraceMessage(ex); FormHelper.ErrorBox("在导出信息文件时遇到错误:\n" + ex.Message); } finally { if (r != null) { r.Close(); } } } internal static string GetExpandedFileName(SourceItem.Pdf item, string template) { string t; var s = item.FilePath; var d = new PdfDictionary(); DocInfoImporter.UpdateInfoValue(d, PdfName.TITLE, item.DocInfo.Title, s); DocInfoImporter.UpdateInfoValue(d, PdfName.AUTHOR, item.DocInfo.Author, s); DocInfoImporter.UpdateInfoValue(d, PdfName.SUBJECT, item.DocInfo.Subject, s); DocInfoImporter.UpdateInfoValue(d, PdfName.KEYWORDS, item.DocInfo.Keywords, s); t = ReplaceTargetFileNameMacros(s.ToString(), template, d, item.PageCount); if (FileHelper.IsPathValid(t) == false) { return t; } else if (Path.GetFileName(t).Length > 0) { t = new FilePath(t).EnsureExtension(Ext.Pdf).ToString(); } return Path.GetFullPath(t); } internal static void RenameFiles(List items, string template, bool keepSourceFile) { Tracker.SetTotalProgressGoal(items.Count); Tracker.TraceMessage(String.Concat("使用“", template, "”模板重命名。")); foreach (var item in items) { try { var s = item.FilePath.ToFullPath(); if (s.ExistsFile == false) { Tracker.TraceMessage(Tracker.Category.Error, String.Concat("找不到 PDF 文件:", s)); continue; } var t = GetExpandedFileName(item, template); Tracker.TraceMessage(Tracker.Category.InputFile, s.ToString()); Tracker.TraceMessage(Tracker.Category.OutputFile, t); Tracker.TraceMessage(Tracker.Category.ImportantMessage, String.Concat("重命名 PDF 文件:", s)); Tracker.TraceMessage(Tracker.Category.ImportantMessage, String.Concat("到目标 PDF 文件:<<", t, ">>。")); if (FileHelper.IsPathValid(t) == false) { Tracker.TraceMessage(Tracker.Category.Error, String.Concat("输出文件名 ", t, " 无效。")); goto Exit; } if (s.Equals(t)) { Tracker.TraceMessage("源文件与目标文件名称相同。不需重命名。"); goto Exit; } else if (Path.GetFileName(t).Trim().Length == 0) { Tracker.TraceMessage("输出文件名为空,无法重命名。"); goto Exit; } else if (File.Exists(t)) { switch (FormHelper.YesNoCancelBox("是否覆盖已存在的 PDF 文件:" + t)) { case DialogResult.No: goto Exit; case DialogResult.Cancel: throw new OperationCanceledException(); default: File.Delete(t); break; } } if (Directory.Exists(Path.GetDirectoryName(t)) == false) { Directory.CreateDirectory(Path.GetDirectoryName(t)); } if (keepSourceFile) { File.Copy(s.ToString(), t); } else { File.Move(s.ToString(), t); } } catch (OperationCanceledException) { Tracker.TraceMessage(Tracker.Category.Alert, "已取消重命名操作。"); return; } catch (Exception ex) { Tracker.TraceMessage(ex); } Exit: Tracker.IncrementTotalProgress(); } Tracker.TraceMessage("重命名操作已完成。"); } internal static void ImportOcr(string sourceFile, string infoFile, string targetFile) { if (FileHelper.IsPathValid(sourceFile) == false) { Tracker.TraceMessage(Tracker.Category.Error, "输入 PDF 文件路径无效。"); return; } if (FileHelper.IsPathValid(targetFile) == false) { Tracker.TraceMessage(Tracker.Category.Error, "输出 PDF 文件路径无效。"); return; } if (FileHelper.IsPathValid(infoFile) == false) { Tracker.TraceMessage(Tracker.Category.Error, "信息文件路径无效。"); return; } if (FileHelper.HasExtension(infoFile, Ext.Xml) == false) { Tracker.TraceMessage(Tracker.Category.Error, "信息文件不是 XML 文件,只有扩展名为 XML 的识别结果文件才能写入 PDF 文档。"); return; } Tracker.TraceMessage(Tracker.Category.InputFile, sourceFile); Tracker.TraceMessage(Tracker.Category.OutputFile, targetFile); if (FileHelper.ComparePath(sourceFile, targetFile)) { Tracker.TraceMessage(Tracker.Category.Error, "输入文件和输出文件不能相同。"); return; } if (FileHelper.CheckOverwrite(targetFile) == false) { return; } var pdf = OpenPdf(sourceFile, AppContext.LoadPartialPdfFile, false); if (pdf == null) { return; } try { using (var infoReader = XmlReader.Create(infoFile, new XmlReaderSettings() { IgnoreComments = true, IgnoreProcessingInstructions = true })) { infoReader.MoveToContent(); // 移到根元素 using (var s = new FileStream(targetFile, FileMode.Create)) { var st = new PdfStamper(pdf, s); var en = new PdfProcessingEngine(pdf); en.ExtraData[DocProcessorContext.OcrData] = infoReader; en.DocumentProcessors.Add(new ImportOcrResultProcessor()); Tracker.SetProgressGoal(en.EstimateWorkload()); en.ProcessDocument(st.Writer); st.Close(); } } Tracker.TraceMessage(Tracker.Category.ImportantMessage, "成功写入识别结果到文件:<<" + targetFile + ">>。"); } catch (OperationCanceledException) { Tracker.TraceMessage(Tracker.Category.ImportantMessage, OperationCanceled); } catch (Exception ex) { Tracker.TraceMessage(ex); FormHelper.ErrorBox("在导入信息时发生错误:\n" + ex.Message); } finally { if (pdf != null) { try { pdf.Close(); } catch (Exception ex) { // ignore exception Tracker.TraceMessage(ex); } } } } } }