【问题标题】:C# iTextSharp - Code overwriting instead of appending pagesC# iTextSharp - 代码覆盖而不是附加页面
【发布时间】:2015-01-12 16:24:49
【问题描述】:

我看到很多帖子帮助我到达了现在的位置,我是编程新手。我的意图是获取目录“sourceDir”中的文件并查找正则表达式匹配。当它找到匹配时,我想创建一个以匹配为名称的新文件。如果代码找到具有相同匹配项的另一个文件(该文件已存在),则在该文档中创建一个新页面。

现在代码可以工作,但是它没有添加新页面,而是覆盖了文档的第一页。注意:目录中的每个文档只有一页!

string sourceDir = @"C:\Users\bob\Desktop\results\";
string destDir = @"C:\Users\bob\Desktop\results\final\";
string[] files = Directory.GetFiles(sourceDir);
foreach (string file in files)
    {
       using (var pdfReader = new PdfReader(file.ToString()))
            {
                for (int page = 1; page <= pdfReader.NumberOfPages; page++)
                {
                    var text = new StringBuilder();

                    ITextExtractionStrategy strategy = new SimpleTextExtractionStrategy();
                    var currentText = 
                    PdfTextExtractor.GetTextFromPage(pdfReader, page, strategy);

                    currentText = Encoding.UTF8.GetString(Encoding.Convert(Encoding.Default, Encoding.UTF8, Encoding.Default.GetBytes(currentText)));
                    text.Append(currentText);

                    Regex reg = new Regex(@"ABCDEFG");
                    MatchCollection matches = reg.Matches(currentText);

                    foreach (Match m in matches)
                    {
                        string newFile = destDir + m.ToString() + ".pdf";

                        if (!File.Exists(newFile))
                        {
                            using (PdfReader reader = new PdfReader(File.ReadAllBytes(file)))
                            {
                                using (Document doc = new Document(reader.GetPageSizeWithRotation(page)))
                                {
                                    using (PdfCopy copy = new PdfCopy(doc, new FileStream(newFile, FileMode.Create)))
                                    {
                                        var importedPage = copy.GetImportedPage(reader, page);
                                        doc.Open();
                                        copy.AddPage(importedPage);
                                        doc.Close();
                                    }
                                }
                            }
                        }
                        else
                        {
                            using (PdfReader reader = new PdfReader(File.ReadAllBytes(newFile)))
                            {
                                using (Document doc = new Document(reader.GetPageSizeWithRotation(page)))
                                {
                                    using (PdfCopy copy = new PdfCopy(doc, new FileStream(newFile, FileMode.OpenOrCreate)))
                                    {
                                        var importedPage = copy.GetImportedPage(reader, page);
                                        doc.Open();
                                        copy.AddPage(importedPage);
                                        doc.Close();
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

【问题讨论】:

  • 您似乎经常覆盖您的文件。您应该在外循环中创建 PdfCopy 实例。实际上,我不明白你的代码。它似乎与您想要的不匹配。您能否记录您的代码(例如,通过向其中添加描述您想要做什么的 cmets)?
  • 您是指 PdfCopy、PdfReader、Document 吗?此时我什至需要 PdfReader 吗?我正在尝试将文件(具有正则表达式匹配)添加为最终文档中的第二页、第三页等。
  • 我的目标是在最终文档中添加一个页面而不是覆盖它
  • 好的,但如果我正确理解您的代码,您当前正在使用 PdfCopy 创建单页 PDF,每次遇到需要添加的新页面时都会丢弃旧版本。这没有意义,不是吗?将DocumentPdfCopy 移出内部循环。
  • 如果我没看错,我猜我需要对 copy.AddPage(importedPage); 做一些不同的事情。 else 语句中的行。

标签: c# pdf itextsharp


【解决方案1】:

Bruno 很好地解释了这个问题以及如何解决它,但既然你说你是编程新手,而且你已经进一步 posted a very similar and related question 我会更深入一点,希望能帮助你。

首先,让我们写下已知的:

  1. 有一个全是 PDF 的目录
  2. 每个 PDF 只有一页

然后是目标:

  1. 提取每个 PDF 的文本
  2. 将提取的文本与模式进行比较
  3. 如果存在匹配项,则使用匹配项作为文件名执行以下操作之一:
    1. 如果文件存在,请将源 PDF 添加到该文件
    2. 如果不匹配,请使用 PDF 创建一个新文件

在继续之前,您需要了解几件事。您尝试使用FileMode.OpenOrCreate 在“附加模式”下工作。这是一个很好的猜测,但不正确。 PDF格式既有开始也有结束,所以“从这里开始”和“从这里结束”。当您尝试将另一个 PDF(或与此相关的任何内容)附加到现有文件时,您只是在写到“此处结束”部分。充其量,这是被忽略的垃圾数据,但更有可能的是您最终会得到一个损坏的 PDF。几乎任何文件格式都是如此。连接的两个 XML 文件无效,因为一个 XML 文档只能有一个根元素。

第二个但相关的是,iText/iTextSharp 无法编辑现有文件。这个非常重要。但是,它可以创建恰好具有其他文件的确切版本或可能已修改版本的全新文件。我不知道我是否可以强调这有多重要。

第三,您使用的行会被一遍又一遍地复制,但这是非常错误的,实际上可能会损坏您的数据。为什么不好,read this

currentText = Encoding.UTF8.GetString(Encoding.Convert(Encoding.Default, Encoding.UTF8, Encoding.Default.GetBytes(currentText)));

第四,您正在使用正则表达式,这是一种执行搜索的过于复杂的方法。也许您发布的代码只是一个示例,但如果不是,我建议您只使用currentText.Contains("") 或者如果您需要忽略大小写currentText.IndexOf( "", StringComparison.InvariantCultureIgnoreCase )。为了避免疑问,下面的代码假设您有一个更复杂的 RegEx。

综上所述,下面是一个完整的工作示例,应该可以引导您完成所有操作。由于我们无权访问您的 PDF,因此第二部分实际上创建了 100 个示例 PDF,其中偶尔添加了我们的搜索词。您的真实代码显然不会这样做,但我们需要共同点与您合作。第三部分是您尝试执行的搜索和合并功能。希望代码中的 cmets 能解释一切。

/**
 * Step 1 - Variable Setup
 */

//This is the folder that we'll be basing all other directory paths on
var workingFolder = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);

//This folder will hold our PDFs with text that we're searching for
var folderPathContainingPdfsToSearch = Path.Combine(workingFolder, "Pdfs");

var folderPathContainingPdfsCombined = Path.Combine(workingFolder, "Pdfs Combined");

//Create our directories if they don't already exist
System.IO.Directory.CreateDirectory(folderPathContainingPdfsToSearch);
System.IO.Directory.CreateDirectory(folderPathContainingPdfsCombined);

var searchText1 = "ABC";
var searchText2 = "DEF";

/**
 * Step 2 - Create sample PDFs
 */

//Create 100 sample PDFs
for (var i = 0; i < 100; i++) {
    using (var fs = new FileStream(Path.Combine(folderPathContainingPdfsToSearch, i.ToString() + ".pdf"), FileMode.Create, FileAccess.Write, FileShare.None)) {
        using (var doc = new Document()) {
            using (var writer = PdfWriter.GetInstance(doc, fs)) {
                doc.Open();

                //Add a title so we know what page we're on when we combine
                doc.Add(new Paragraph(String.Format("This is page {0}", i)));

                //Add various strings every once in a while.
                //(Yes, I know this isn't evenly distributed but I haven't
                // had enough coffee yet.)
                if (i % 10 == 3) {
                    doc.Add(new Paragraph(searchText1));
                } else if (i % 10 == 6) {
                    doc.Add(new Paragraph(searchText2));
                } else if (i % 10 == 9) {
                    doc.Add(new Paragraph(searchText1 + searchText2));
                } else {
                    doc.Add(new Paragraph("Blah blah blah"));
                }

                doc.Close();
            }
        }
    }
}

/**
 * Step 3 - Search and merge
 */


//We'll search for two different strings just to add some spice
var reg = new Regex("(" + searchText1 + "|" + searchText2 + ")");

//Loop through each file in the directory
foreach (var filePath in Directory.EnumerateFiles(folderPathContainingPdfsToSearch, "*.pdf")) {
    using (var pdfReader = new PdfReader(filePath)) {
        for (var page = 1; page <= pdfReader.NumberOfPages; page++) {

            //Get the text from the page
            var currentText = PdfTextExtractor.GetTextFromPage(pdfReader, page, new SimpleTextExtractionStrategy());

            currentText.IndexOf( "",  StringComparison.InvariantCultureIgnoreCase )



            //DO NOT DO THIS EVER!! See this for why https://stackoverflow.com/a/10191879/231316
            //currentText = Encoding.UTF8.GetString(Encoding.Convert(Encoding.Default, Encoding.UTF8, Encoding.Default.GetBytes(currentText)));

            //Match our pattern against the extracted text
            var matches = reg.Matches(currentText);

            //Bail early if we can
            if (matches.Count == 0) {
                continue;
            }

            //Loop through each match
            foreach (var m in matches) {

                //This is the file path that we want to target
                var destFile = Path.Combine(folderPathContainingPdfsCombined, m.ToString() + ".pdf");

                //If the file doesn't already exist then just copy the file and move on
                if (!File.Exists(destFile)) {
                    System.IO.File.Copy(filePath, destFile);
                    continue;
                }

                //The file exists so we're going to "append" the page
                //However, writing to the end of file in Append mode doesn't work,
                //that would be like "add a file to a zip" by concatenating two
                //two files. In this case, we're actually creating a brand new file
                //that "happens" to contain the original file and the matched file.
                //Instead of writing to disk for this new file we're going to keep it
                //in memory, delete the original file and write our new file
                //back onto the old file
                using (var ms = new MemoryStream()) {

                    //Use a wrapper helper provided by iText
                    var cc = new PdfConcatenate(ms);

                    //Open for writing
                    cc.Open();

                    //Import the existing file
                    using (var subReader = new PdfReader(destFile)) {
                        cc.AddPages(subReader);
                    }

                    //Import the matched file
                    //The OP stated a guarantee of only 1 page so we don't
                    //have to mess around with specify which page to import.
                    //Also, PdfConcatenate closes the supplied PdfReader so
                    //just use the variable pdfReader.
                    using (var subReader = new PdfReader(filePath)) {
                        cc.AddPages(subReader);
                    }

                    //Close for writing
                    cc.Close();

                    //Erase our exisiting file
                    File.Delete(destFile);

                    //Write our new file
                    File.WriteAllBytes(destFile, ms.ToArray());
                }
            }
        }
    }
}

【讨论】:

  • 非常感谢您提供的所有信息。我同意布鲁诺是绝对正确的,我只是缺乏对如何创建 pdf 的理解。您对 PDF 与 XML 的解释对我来说非常有意义。我能够在您提供的测试用例以及我的实际项目中使用它。
【解决方案2】:

我会用伪代码来写。

你做这样的事情:

// loop over different single-page documents
for () {
    // introduce a condition
    if (condition == met) {
        // create single-page PDF
        new Document();
        new PdfCopy();
        document.Open();
        copy.add(singlePage);
        document.Close();
    }
}

这意味着每次满足条件时您都在创建单页 PDF。顺便说一句,您会多次覆盖现有文件。

你应该做的是这样的:

// Create a document with as many pages as times a condition is met
new Document();
new PdfCopy();
document.Open();
// loop over different single-page documents
for () {
    // introduce a condition
    if (condition == met) {
        copy.addPage(singlePage);
    }
}
document.Close();

现在您可能会在使用PdfCopy 创建的新文档中添加多个页面。注意:如果条件不满足,可能会抛出异常。

【讨论】:

  • 我已经尝试了您的建议(或我相信您的建议),并且得到了相同的结果。我认为我对整个 iText 缺乏了解。我不想让你为我把它完全分解,我知道你有一本书。感谢您的帮助!
猜你喜欢
  • 2020-07-01
  • 2011-12-17
  • 1970-01-01
  • 1970-01-01
  • 2015-05-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-11
相关资源
最近更新 更多