Files

1044 lines
41 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Windows.Forms;
using System.Xml;
using iTextSharp.text;
using iTextSharp.text.pdf;
using PDFPatcher.Common;
using PDFPatcher.Model;
using MuDocument = MuPDF.Document;
using ImageRendererOptions = MuPDF.ImageRendererOptions;
using MuImageFormat = MuPDF.ImageFormat;
using Ext = PDFPatcher.Constants.FileExtensions;
using MuPDF.Extensions;
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, $"打开 PDF 文件:<<{sourceFile}>>。");
return PdfHelper.OpenPdfFile(sourceFile, loadPartial, removeUnusedObjects);
}
catch (FileNotFoundException) {
FormHelper.ErrorBox($"找不到文件:“{sourceFile}”。");
return null;
}
catch (iTextSharp.text.exceptions.BadPasswordException) {
Tracker.TraceMessage(Tracker.Category.Error, Messages.PasswordInvalid);
FormHelper.ErrorBox(Messages.PasswordInvalid);
return null;
}
catch (Exception ex) {
AppContext.MainForm.ErrorBox("打开 PDF 文件时遇到错误", ex);
#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)) {
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);
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);
AppContext.MainForm.ErrorBox("导出图片时遇到错误", ex);
}
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)) {
Directory.CreateDirectory(options.ExtractImagePath);
}
MuDocument mupdf = null;
try {
mupdf = MuPdfHelper.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);
var cookie = new MuPDF.Cookie();
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 - 1))
using (var bmp = p.RenderBitmapPage(options.ImageWidth, 0, options, cookie)) {
if (bmp == null) {
Tracker.TraceMessage(Tracker.Category.Error, "页面" + i + "的尺寸为空。");
}
else if (options.FileFormat == MuImageFormat.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)) {
using (var b = Imaging.BitmapHelper.ToIndexImage(bmp, uc)) {
Imaging.BitmapHelper.SaveAs(b, fn);
}
}
else if (options.FileFormat == MuImageFormat.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);
AppContext.MainForm.ErrorBox("转换图片时遇到错误", ex);
}
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 = MuPdfHelper.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;
var cookie = new MuPDF.Cookie();
foreach (var range in ranges) {
foreach (var i in range) {
ms.Position = 0;
using (var p = mupdf.LoadPage(i - 1))
using (var bmp = p.RenderBitmapPage(options.ImageWidth, 0, options, cookie)) {
if (bmp == null) {
Tracker.TraceMessage(Tracker.Category.Error, "页面" + i + "的尺寸为空。");
}
else if (options.FileFormat == MuImageFormat.Tiff) {
using (var b = Imaging.BitmapHelper.ToBitonal(bmp)) {
b.SetResolution(options.Dpi, options.Dpi);
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)) {
b.SetResolution(options.Dpi, options.Dpi);
Imaging.BitmapHelper.SaveAs(b, fn, ms);
}
}
else if (uc.Length <= 256 && !Imaging.BitmapHelper.IsIndexed(bmp)) {
using (var b = Imaging.BitmapHelper.ToIndexImage(bmp, uc)) {
b.SetResolution(options.Dpi, options.Dpi);
Imaging.BitmapHelper.SaveAs(b, fn, ms);
}
}
else if (options.FileFormat == MuImageFormat.Jpeg) {
Imaging.JpgHelper.Save(bmp, ms, options.JpegQuality);
}
else {
Imaging.BitmapHelper.SaveAs(bmp, fn, ms);
}
}
doc.SetPageSize((options.Rotation % 180) == 0
? new Rectangle(p.Bound.Width, p.Bound.Height)
: new 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);
AppContext.MainForm.ErrorBox("转换图片时遇到错误", ex);
}
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 || 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.ExportBinaryStream) {
export.BinaryStreamPath = targetFile.Directory.Combine(targetFile.FileNameWithoutExtension).Combine("数据流");
}
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 (var 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);
AppContext.MainForm.ErrorBox("转换书签文件时发生错误", ex);
}
}
internal static bool PatchDocument(SourceItem.Pdf sourceFile, FilePath targetFile, object infoDoc, ImporterOptions options, PatcherOptions pdfSettings, bool suppressWarning = false) {
var sourcePath = sourceFile.FilePath.ToString();
if (sourceFile.FilePath.IsEmpty) {
Tracker.TraceMessage(Tracker.Category.Error, "输入文件名为空。");
return false;
}
if (String.IsNullOrEmpty(targetFile) || targetFile.IsEmpty) {
Tracker.TraceMessage(Tracker.Category.Error, "输出文件名为空。");
return false;
}
Tracker.TraceMessage(Tracker.Category.InputFile, sourcePath);
Tracker.TraceMessage(Tracker.Category.OutputFile, targetFile);
bool overwriteSource = false;
DateTime sourceCreationTime = DateTime.Now;
var pdf = OpenPdf(sourcePath, AppContext.LoadPartialPdfFile, true);
if (pdf == null) {
return false;
}
pdf.SeparateConjoinedRootAndInfo();
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, $"加载信息文件:<<{docPath}>>。");
import = new DocInfoImporter(options, docPath);
if (import.InfoDoc != null && !VerifyInfoDocument(import.InfoDoc)) {
return false;
}
}
var pdfEngine = new PdfProcessingEngine(pdf);
pdfEngine.CreateProcessors(pdfSettings);
int workload = 110;
workload += pdfEngine.EstimateWorkload();
Tracker.SetProgressGoal(workload);
Tracker.TrackProgress(10);
if (!pdf.ConfirmUnethicalMode()) {
return false;
}
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 (!suppressWarning && !FileHelper.CheckOverwrite(targetFile)) {
Tracker.TraceMessage(Tracker.Category.Error, Messages.SourceFileEqualsTargetFile);
return false;
}
overwriteSource = true;
sourceCreationTime = sourceFile.FilePath.ToFileInfo().CreationTime;
targetFile = sourceFile.FilePath.ChangeExtension(Ext.Tmp).ToString();
}
else if (!suppressWarning && !FileHelper.CheckOverwrite(targetFile)) {
return false;
}
targetFile.CreateContainingDirectory();
using (Stream s = new FileStream(targetFile, FileMode.Create)) {
var st = new PdfStamper(pdf, s);
pdf.Catalog.Put(new PdfName(Application.ProductName), Application.ProductVersion);
if (!String.IsNullOrWhiteSpace(info.CreationDate)) {
st.Writer.Info.Put(PdfName.CREATIONDATE, new PdfString(info.CreationDate));
}
pdfEngine.ProcessDocument(st.Writer);
#region 处理信息文件
var processors = new List<IInfoDocProcessor>();
if (pdfSettings.ViewerPreferences.RemoveZoomRate) {
processors.Add(new RemoveZoomRateProcessor());
}
if (pdfSettings.UnifiedPageSettings.ScaleContent
&& pdfEngine.ExtraData.TryGetValue(DocProcessorContext.CoordinateTransition, out var o)
&& o is CoordinateTranslationSettings[] cts) {
processors.Add(new GotoDestinationProcessor() {
TransitionMapper = cts
});
}
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 || 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)) {
pdf.Catalog.Put(PdfName.PAGEMODE, PdfName.USEOUTLINES);
}
}
else if (!String.IsNullOrEmpty(docPath)) {
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<PdfDictionary>(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 false;
}
catch (Exception ex) {
Tracker.TraceMessage(ex);
AppContext.MainForm.ErrorBox("导入信息时发生错误", ex);
return false;
}
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;
targetFile.ToFileInfo().CreationTime = sourceCreationTime;
}
catch (Exception) {
FormHelper.ErrorBox($"无法覆盖输入文件。\n请手工将“{Ext.Tmp}”后缀的临时文件更名为输入文件。");
return false;
}
}
Tracker.TraceMessage(Tracker.Category.Alert, $"成功导入信息到 <<{targetFile}>>。");
return true;
}
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);
}
}
/// <summary>
/// 替换目标文件名的替代符。
/// </summary>
/// <param name="sourceFile">用于替换的源文件名。</param>
/// <param name="targetFile">包含替代符的目标文件名。</param>
/// <param name="info">源文件的元数据属性。</param>
/// <param name="pageCount">源文件的页数。</param>
/// <returns>替换目标文件名后的文件名。</returns>
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($"信息文件不是用这个版本的程序生成的,可能会导入不成功,是否继续?\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 pdf = OpenPdf(sourceFile, AppContext.LoadPartialPdfFile, false);
if (pdf == null) {
return;
}
try {
if (!pdf.ConfirmUnethicalMode()) {
return;
}
PdfPageExtractor.ExtractPages(options, sourceFile, targetFile, pdf);
}
catch (OperationCanceledException) {
Tracker.TraceMessage(Tracker.Category.ImportantMessage, OperationCanceled);
}
catch (Exception ex) {
Tracker.TraceMessage(ex);
AppContext.MainForm.ErrorBox("导出页面内容时出错", ex);
}
finally {
if (pdf != null) {
try {
pdf.Close();
}
catch (Exception ex) {
// ignore exception
Tracker.TraceMessage(ex);
}
}
}
}
internal static void MergeDocuments(ICollection<SourceItem> 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)) {
Tracker.TraceMessage(Tracker.Category.ImportantMessage, $"加载信息文件:<<{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, $"输出到文件:{f}。");
if (!f.IsValidPath) {
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);
w.Info.Put(PdfName.PRODUCER, new PdfString($"{Application.ProductName} {Application.ProductVersion}"));
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) && 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);
AppContext.MainForm.ErrorBox("生成文档时出错", ex);
}
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) || 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) || Path.GetFileName(bookmarkFile).Length == 0) {
Tracker.TraceMessage(Tracker.Category.Error, Messages.InfoFileNameInvalid);
FormHelper.ErrorBox(Messages.InfoFileNameInvalid);
return;
}
try {
if (!FileHelper.CheckOverwrite(bookmarkFile)) {
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) {
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 {
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)) {
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<SourceItem.Pdf> items, string template, bool keepSourceFile) {
Tracker.SetTotalProgressGoal(items.Count);
Tracker.TraceMessage($"使用“{template}”模板重命名。");
foreach (var item in items) {
try {
var s = item.FilePath.ToFullPath();
if (!s.ExistsFile) {
Tracker.TraceMessage(Tracker.Category.Error, $"找不到 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, $"重命名 PDF 文件:{s}");
Tracker.TraceMessage(Tracker.Category.ImportantMessage, $"到目标 PDF 文件:<<{t}>>。");
if (!FileHelper.IsPathValid(t)) {
Tracker.TraceMessage(Tracker.Category.Error, $"输出文件名 {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))) {
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)) {
Tracker.TraceMessage(Tracker.Category.Error, "输入 PDF 文件路径无效。");
return;
}
if (!FileHelper.IsPathValid(targetFile)) {
Tracker.TraceMessage(Tracker.Category.Error, "输出 PDF 文件路径无效。");
return;
}
if (!FileHelper.IsPathValid(infoFile)) {
Tracker.TraceMessage(Tracker.Category.Error, "信息文件路径无效。");
return;
}
if (!FileHelper.HasExtension(infoFile, Ext.Xml)) {
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)) {
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);
AppContext.MainForm.ErrorBox("导入信息时发生错误", ex);
}
finally {
if (pdf != null) {
try {
pdf.Close();
}
catch (Exception ex) {
// ignore exception
Tracker.TraceMessage(ex);
}
}
}
}
}
}