带有 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;
}
}
}
我在使用 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;
}
}
}