带有 iTextSharp 的 ShiftPageNumbers

ShiftPageNumbers with iTextSharp

我在使用 iTextSharp 合并一些 PDF 同时保留书签时遇到了一些问题。我编写了以下骇人听闻的代码来测试一个想法,但 ShiftPageNumbers 方法似乎甚至对我不起作用。有人知道为什么吗?

string[] files = Directory.GetFiles(folder);
string path = Path.GetDirectoryName(files[0]);
IList<Dictionary<string, object>> oldBookmarks
    = SimpleBookmark.GetBookmark(new PdfReader(files[0]));
List<Dictionary<string, object>> newBookmarks = new List<Dictionary<string, object>>();

FileStream outFile = new FileStream(output, FileMode.Create);
PdfConcatenate newPdf = new PdfConcatenate(outFile);

switch (oldBookmarks.Count())
{
    case 0:
        break;

    case 1:
        oldBookmarks = (IList<Dictionary<string, object>>)(oldBookmarks[0])["Kids"];
        break;

    default:
        break;
}

files = oldBookmarks.Select(b => b["File"]).Cast<string>().ToArray();

foreach (string filename in files)
{
    Console.Write:ine(filename);
    PdfReader reader = new PdfReader(Path.Combine(path, filename));
    newPdf.AddPages(reader);

    List<Dictionary<string, object>> tempBookmarks =
        oldBookmarks.Where(b => (string)b["File"] == filename).ToList();

    // handles bookmarks
    SimpleBookmark.ShiftPageNumbers(tempBookmarks, length, null);
    newBookmarks.AddRange(tempBookmarks);
    length += reader.NumberOfPages;

    reader.Close();
}

newPdf.Writer.Outlines = newBookmarks;
newPdf.Close();

最后,我使用递归函数来调整页码。我认为如果我只是包含整个应用程序的代码,那么最容易显示。该程序旨在编译由美国无线电中继联盟 (ARRL) 出版的电子书,这些电子书被分解成许多 PDF,它们都包含相同的书签列表。每个一级书签及其子书签 link 到不同的 PDF 文件。因此,标准的 PDF 合并程序将无法正确保留书签。

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using iTextSharp.text.pdf;

namespace ARRL_Book_Compiler
{
    internal class Program
    {
        private const string ACTION_KEY = "Action";
        private const string FILE_KEY = "File";
        private const string KIDS_KEY = "Kids";
        private const string PAGE_KEY = "Page";
        private const char DELIM = ' ';

        private static void Main(string[] args)
        {
            if (args.Count() != 2)
            {
                Console.WriteLine("Arg 1: PDF directory");
                Console.WriteLine("Arg 2: Output filename");
                Environment.Exit(1);
            }

            Merge(args[0], args[1]);
        }

        /// <summary>
        /// Compiles ARRL books.
        /// </summary>
        /// <param name="folder">Directory containing all the PDFs in the entire book.</param>
        /// <param name="output">Path and filename of the output (compiled) PDF.</param>
        private static void Merge(string folder, string output)
        {
            int offset = 1;  // Setting to 0 causes all bookmarks to be off by 1
            string[] dirFiles
                = Directory.GetFiles(folder).Where(f => f.EndsWith("pdf")).ToArray();  // PDFs in directory
            IList<Dictionary<string, object>> oldBookmarks
                = SimpleBookmark.GetBookmark(new PdfReader(dirFiles[0]));
            List<Dictionary<string, object>> newBookmarks = new List<Dictionary<string, object>>();

            FileStream outFile = new FileStream(output, FileMode.Create);
            PdfConcatenate newPdf = new PdfConcatenate(outFile);

            if (oldBookmarks.Count() == 1)
            {
                oldBookmarks = (IList<Dictionary<string, object>>)(oldBookmarks[0])[KIDS_KEY];
            }

            string[] bFiles
                = oldBookmarks.Select(b => b[FILE_KEY]).Cast<string>().ToArray();  // get files in bookmark order
            IEnumerable<string> missingFiles = bFiles.Except(dirFiles.Select(f => Path.GetFileName(f)));

            if (missingFiles.Any())
            {
                Console.Error.WriteLine("The following files are present in the bookmarks but not in the directory:");
                foreach (string filename in missingFiles) Console.Error.WriteLine(filename);
                Environment.Exit(2);
            }

            for(int i = 0; i < bFiles.Count(); i++)
            {
                Console.Write(string.Format("\r{0}% complete", (int)((i + 1f)/bFiles.Count()*100)));
                string filename = bFiles[i];
                PdfReader reader = new PdfReader(Path.Combine(folder, filename));
                List<Dictionary<string, object>> tempBookmarks =
                    oldBookmarks.Where(b => b.ContainsKey(FILE_KEY) && (string)b[FILE_KEY] == filename).ToList();

                // handles bookmarks
                newBookmarks.AddRange(ModifyBookmarks(  // Exception if LINQ can't find FILE_KEY key in ANY list item
                    oldBookmarks.Where(b => b.ContainsKey(FILE_KEY) && (string)b[FILE_KEY] == filename).ToList(),
                    offset));
                offset += reader.NumberOfPages;

                newPdf.AddPages(reader);
                reader.Close();
            }

            newPdf.Writer.Outlines = newBookmarks;
            newPdf.Close();
        }

        private static List<Dictionary<string, object>>
            ModifyBookmarks(List<Dictionary<string, object>> list, int offset)
        {
            for (int i = 0; i < list.Count(); i++)
            {
                string pageKey = (string)list[i][PAGE_KEY];
                int index = pageKey.IndexOf(DELIM);
                list[i][PAGE_KEY] = (int.Parse(pageKey.Substring(0, index)) + offset).ToString()
                    + pageKey.Substring(index);

                if (list[i].ContainsKey(FILE_KEY)) list[i].Remove(FILE_KEY);
                if (list[i].ContainsKey(ACTION_KEY)) list[i][ACTION_KEY] = "GoTo";

                if (list[i].ContainsKey(KIDS_KEY))
                    list[i][KIDS_KEY] = ModifyBookmarks((List<Dictionary<string, object>>)list[i][KIDS_KEY], offset);
            }

            return list;
        }
    }
}