I have an issue with trying to create a large PDF file. Basically I have a list of byte arrays, each containing a PDF in a form of a byte array. I wanted to merge the byte arrays into a single PDF. This works great for smaller files (under 2000 pages), but when I tried creating a 12,00 page file it bombed). Originally I was using MemoryStream but after some research, a common solution was to use a FileStream instead. So I tried a file stream approach, however get similar results. The List contains 3,800 records, each containing 4 pages. MemoryStream bombs after around 570. FileStream after about 680 records. The current file size after the code crashed was 60MB. What am I doing wrong? Here is the code I have, and the code crashes on "copy.AddPage(curPg);" directive, inside the "for(" loop.
private byte[] MergePDFs(List<byte[]> PDFs)
{
iTextSharp.text.Document doc = new iTextSharp.text.Document();
byte[] completePDF;
Guid uniqueId = Guid.NewGuid();
string tempFileName = Server.MapPath("~/" + uniqueId.ToString() + ".pdf");
//using (MemoryStream ms = new MemoryStream())
using(FileStream ms = new FileStream(tempFileName, FileMode.Create, FileAccess.Write, FileShare.Read))
{
iTextSharp.text.pdf.PdfCopy copy = new iTextSharp.text.pdf.PdfCopy(doc, ms);
doc.Open();
int i = 0;
foreach (byte[] PDF in PDFs)
{
i++;
// Create a reader
iTextSharp.text.pdf.PdfReader reader = new iTextSharp.text.pdf.PdfReader(PDF);
// Cycle through all the pages
for (int currentPageNumber = 1; currentPageNumber <= reader.NumberOfPages; ++currentPageNumber)
{
// Read a page
iTextSharp.text.pdf.PdfImportedPage curPg = copy.GetImportedPage(reader, currentPageNumber);
// Add the page over to the rest of them
copy.AddPage(curPg);
}
// Close the reader
reader.Close();
}
// Close the document
doc.Close();
// Close the copier
copy.Close();
// Convert the memorystream to a byte array
//completePDF = ms.ToArray();
}
//return completePDF;
return GetPDFsByteArray(tempFileName);
}
A couple of notes:
PdfCopy implements iDisposable, so you should try and see if a using helps.
PdfCopy.FreeReader() will help.
Anyway, not sure if you're using MVC or WebForms, but here's a simple working HTTP handler tested with a 15 page 125KB test file that runs on my workstation:
<%# WebHandler Language="C#" Class="MergeFiles" %>
using System;
using System.Collections.Generic;
using System.Web;
using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;
public class MergeFiles : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
List<byte[]> pdfs = new List<byte[]>();
var pdf = File.ReadAllBytes(context.Server.MapPath("~/app_data/test.pdf"));
for (int i = 0; i < 4000; ++i) pdfs.Add(pdf);
var Response = context.Response;
Response.ContentType = "application/pdf";
Response.AddHeader(
"content-disposition",
"attachment; filename=MergeLotsOfPdfs.pdf"
);
Response.BinaryWrite(MergeLotsOfPdfs(pdfs));
}
byte[] MergeLotsOfPdfs(List<byte[]> pdfs)
{
using (var ms = new MemoryStream())
{
using (Document document = new Document())
{
using (PdfCopy copy = new PdfCopy(document, ms))
{
document.Open();
for (int i = 0; i < pdfs.Count; ++i)
{
using (PdfReader reader = new PdfReader(
new RandomAccessFileOrArray(pdfs[i]), null))
{
copy.AddDocument(reader);
copy.FreeReader(reader);
}
}
}
}
return ms.ToArray();
}
}
public bool IsReusable { get { return false; } }
}
Tried to make the output file similar to what you described in the question, but YMMV, depending on how large the individual PDFs you're dealing with are in size. Here's the test output from my run:
So after a lot of messing around, I realized that there just was no way around it. However, I did manage to find a work-around. Instead of returning byte array, I return a temp file path, which I then transmit and delete there after.
private string MergeLotsOfPDFs(List<byte[]> PDFs)
{
Document doc = new Document();
Guid uniqueId = Guid.NewGuid();
string tempFileName = Server.MapPath("~/__" + uniqueId.ToString() + ".pdf");
using (FileStream ms = new FileStream(tempFileName, FileMode.Create, FileAccess.Write, FileShare.Read))
{
PdfCopy copy = new PdfCopy(doc, ms);
doc.Open();
int i = 0;
foreach (byte[] PDF in PDFs)
{
i++;
// Create a reader
PdfReader reader = new PdfReader(new RandomAccessFileOrArray(PDF), null);
// Cycle through all the pages
for (int currentPageNumber = 1; currentPageNumber <= reader.NumberOfPages; ++currentPageNumber)
{
// Read a page
PdfImportedPage curPg = copy.GetImportedPage(reader, currentPageNumber);
// Add the page over to the rest of them
copy.AddPage(curPg);
// This is a lie, it still costs money, hue hue hue :)~
copy.FreeReader(reader);
}
reader.Close();
}
// Close the document
doc.Close();
// Close the document
copy.Close();
}
// Return temp file path
return tempFileName;
}
And here is how I send that data to the client.
// Send the merged PDF file to the user.
System.Web.HttpResponse response = System.Web.HttpContext.Current.Response;
response.ClearContent();
Response.ClearHeaders();
response.ContentType = "application/pdf";
response.AddHeader("Content-Disposition", "attachment; filename=1094C.pdf;");
response.WriteFile(tempFileName);
HttpContext.Current.Response.Flush(); // Sends all currently buffered output to the client.
DeleteFile(tempFileName); // Call right after flush but before close
HttpContext.Current.Response.SuppressContent = true; // Gets or sets a value indicating whether to send HTTP content to the client.
HttpContext.Current.ApplicationInstance.CompleteRequest(); // Causes ASP.NET to bypass all events and filtering in the HTTP pipeline chain of execution and directly execute the EndRequest event.
Lastly, here is a fancy DeleteFile method
private void DeleteFile(string fileName)
{
if (File.Exists(fileName))
{
try
{
File.Delete(fileName);
}
catch (Exception ex)
{
//Could not delete the file, wait and try again
try
{
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
File.Delete(fileName);
}
catch
{
//Could not delete the file still
}
}
}
}
Related
i have a webapi where i have route to concatenate pdfs and return the byte array using memorystream
public HttpResponseMessage ConcatPDFs(string id, ICollection<int> pdfs) {
using (var db = new ApplicationDbContext())
using (MemoryStream stream = new MemoryStream())
using (Document doc = new Document())
using (PdfCopy pdf = new PdfCopy(doc, stream))
{
doc.Open();
PdfReader reader = null;
PdfImportedPage page = null;
db.PDFForms.Where(p => pdfs.Contains(p.Id)).ToList().ForEach(file =>
{
var filePath = Path.Combine(System.Web.Hosting.HostingEnvironment.MapPath("~/" + string.Format("Content/Uploads/PDFForms/")), file.FileName);
reader = new PdfReader(filePath);
for (int i = 0; i < reader.NumberOfPages; i++)
{
page = pdf.GetImportedPage(reader, i + 1);
pdf.AddPage(page);
}
pdf.FreeReader(reader);
reader.Close();
});
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
result.Content = new ByteArrayContent(stream.ToArray());
result.Content.Headers.ContentType =
new MediaTypeHeaderValue(string.Format("{0}", "application/pdf"));
return result;
}
}
this is my code, however when i stream the data back to the client the browser give me the error, failed to load pdf document. Any idea what i might be doing wrong? thank you.
Edit:
This works if i create a physical file and not use MemoryStream
Perhaps you can debug your application using wireshark?
Get the bytes from the response, paste them in a document and see whether that document can be read with something like Adobe Reader.
From the looks of it though, this does not seem like an iText issue, since you have confirmed that you can create a physical file.
So either it is the implementation of MemoryStream, or some other step that comes in between the creation of the document and sending the get response.
At any rate, I think the first step in solving this problem is storing the bytes you do get back, and comparing them against the physical file.
This question is related to one I asked here on "How can I prompt the user for a save location from a Sharepoint page?"
What I need to do is, after generating a PDF thus:
private void GeneratePDF(List<ListColumns> listOfListItems)
{
. . .
//Create a stream that we can write to, in this case a MemoryStream
StringBuilder sb = new StringBuilder();
using (var ms = new MemoryStream())
{
using (var doc = new Document(PageSize.A4, 25, 25, 10, 10))
{
//Create a writer that's bound to our PDF abstraction and our stream
using (var writer = PdfWriter.GetInstance(doc, ms))
{
//Open the document for writing
doc.Open();
Paragraph docTitle = new Paragraph("UCSC - Direct Payment Form", timesBold16UCSCBlue);
doc.Add(docTitle);
Paragraph subTitle = new Paragraph("(Not to be used for reimbursement of services)", timesRoman9Font);
subTitle.IndentationLeft = 20;
doc.Add(subTitle);
. . . // tons of iTextSharp PDF-generation code elided for brevity
String htmlToRenderAsPDF = sb.ToString();
//XMLWorker also reads from a TextReader and not directly from a string
using (var srHtml = new StringReader(htmlToRenderAsPDF))
{
XMLWorkerHelper.GetInstance().ParseXHtml(writer, doc, srHtml);
}
doc.Close();
}
}
try
{
var bytes = ms.ToArray();
// This is currently saved to a location on the server such as C:\Users\TEMP.SP.005\Desktop\iTextSharpTest.pdf (but the number (such as
"005" irregularly increments))
String pdfFileID = GetYYYYMMDDAndUserNameAndAmount();
String pdfFileName = String.Format("DirectPayDynamic_{0}.pdf", pdfFileID);
String fileFullpath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), pdfFileName);
String fileLinkBase = "Generated PDF: {1}";
String filelink = String.Format(fileLinkBase, fileFullpath, pdfFileName);
File.WriteAllBytes(fileFullpath, bytes);
var pdflink = new Label
{
CssClass = "finaff-webform-field-label",
Text = filelink
};
this.Controls.Add(pdflink);
}
catch (DocumentException dex)
{
throw (dex);
}
catch (IOException ioex)
{
throw (ioex);
}
catch (Exception ex)
{
throw (ex);
}
}
} // GeneratePDF
...afford the user the opportunity to save that file to their local machine (currently, it is being saved on the server).
IOW, what I basically want to do is replace this line:
String fileFullpath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), pdfFileName);
...with something like:
String fileFullpath = "Hey, bloke (or blokette), where do you want to save this?"
This is obviously possible, and even somewhat common, as many websites will generate files for you and then allow you to save those
generated files to either a location you select or a default location, such as your downloads folder.
Either a server-side (C#) or a client-side (jQuery) solution is acceptable, although I prefer server-side, as that is where the PDF is being generated.
When I had to force an Excel spreadsheet to download to the client, I used this code, which may point you to where you are wanting to get:
Control xxx of type 'LinkButton' must be placed inside a form tag with runat=server
protected void ExportExcelFile(object Sender, EventArgs e) { //export to excel
var grdResults = new DataGrid(); // REF: https://stackoverflow.com/q/28155089/153923
if (periodCriteria.SelectedValue == "year") {
grdResults = RollupDG;
} else {
grdResults = QuarterDG;
}
var response = HttpContext.Current.Response;
response.Clear();
response.Charset = String.Empty;
response.ContentType = "application/vnd.ms-excel";
response.AddHeader("Content-Disposition", "attachment; filename=GlBudgetReport.xls");
using (var sw = new StringWriter()) {
using (var htw = new HtmlTextWriter(sw)) {
grdResults.RenderControl(htw);
response.Write(sw.ToString());
response.End();
}
}
}
This is my code which is used in a wcf service. It successfully generates the PDF, but after generating the document, the folder in which the PDF is generated gives the error: "access is denied"
The PDF is closed for the website, but for the continuous web service it is not closed.
string r = builder.ToString();
string pdfname = Fuhre + "_" + ProtokolType + "_" + GeraeteNr + "_" + r;
PdfWriter.GetInstance(document, new FileStream(#"C:\inetpub\wwwroot\protokoll_pdfs\"+pdfname+".pdf",FileMode.Create));
document.Open();
WebClient wc = new WebClient();
string htmlText = html;
//Response.Write(htmlText);
List<IElement> htmlarraylist = HTMLWorker.ParseToList(new StringReader(htmlText), null);
for (int k = 0; k < htmlarraylist.Count; k++)
{
document.Add((IElement)htmlarraylist[k]);
}
pdflink1 = pdfname + ".pdf";
htmlpdflink =""+pdflink1;
document.Close();
You need to be careful to dispose of everything.
using(var filesStream = new FileStream())
{
using(PdfWriter wri = PdfWriter.GetInstance(doc, fileStream))
{
...
}
}
There are a few other objects (Stream, Document) you may want to close. A sample of how to perform the operation is shown below.
FileStream stream = new FileStream(filePath, FileMode.CreateNew);
Document doc = new Document(PageSize.A4, 24, 24, 24, 24);
PdfWriter writer = PdfWriter.GetInstance(doc, stream);
doc.Open();
//PDF writing operations here
writer.Flush();
doc.Close();
writer.Close();
stream.Close();
You want to serve a file to a browser and/or you want to save the file on disk.
In that case, you would benefit from creating the file in memory and then send the bytes to the browser as well as to a file on disk. This is explained in the answer to the following question: Pdf file not loading properly created by the servlet
The answer mentioned above is written in Java, so you'll have to adapt it.
You can do so by looking at other examples. For instance: Create PDF in memory instead of physical file
byte[] pdfBytes;
using(var mem = new MemoryStream())
{
using(PdfWriter wri = PdfWriter.GetInstance(doc, mem))
{
doc.Open();//Open Document to write
Paragraph paragraph = new Paragraph("This is my first line using Paragraph.");
Phrase pharse = new Phrase("This is my second line using Pharse.");
Chunk chunk = new Chunk(" This is my third line using Chunk.");
doc.Add(paragraph);
doc.Add(pharse);
doc.Add(chunk);
}
pdfBytes = mem.ToArray();
}
Now you can write the pdfBytes to the Response object in your web application:
private void ShowPdf(byte[] strS)
{
Response.ClearContent();
Response.ClearHeaders();
Response.ContentType = "application/pdf";
Response.AddHeader("Content-Disposition", "attachment; filename=" + DateTime.Now);
Response.BinaryWrite(strS);
Response.End();
Response.Flush();
Response.Clear();
}
And you can reuse the bytes to write them to a File: Can a Byte[] Array be written to a file in C#?
File.WriteAllBytes(string path, byte[] bytes)
If the problem persist, then you know that it isn't caused by iTextSharp, because iTextSharp merely produces the bytes.
In addition to what everyone else said, I strongly recommend you break each process into multiple sections that don't interact and aren't even aware of each other. Then test each section independently as much as possible. For instance:
private void makePDF( filePath )
{
//Create the PDF
}
private void main()
{
//Make the PDF
makePDF( "test.pdf" );
//If this line fails then your makePDF code has an open handle
File.Delete( "test.pdf" );
}
Then continue:
private void makePDF( filePath )
{
//Create the PDF
}
private void emailPDF( filePath )
{
//Email the PDF
}
private void main()
{
//Make the PDF
makePDF( "test.pdf" );
emailPDF( "test.pdf" );
//If this line fails now then your emailPDF code has an open handle
File.Delete( "test.pdf" );
}
The important part if that you don't try 500 things all at once because that leads to "something is locking my file but I don't know what".
I have a problem with a MemoryStream from OpenXML. I succeed with opening a Word file, changing it and downloading it through the HttpResponse if I do all the steps in a single method.
But if I try to do it in two different classes (or methods) by returning the MemoryStream, I get a corrupted word file. I thought about a flushing or buffer problem but I don't find a solution.
Here is the working code :
public void FillTemplateOpenXmlWord(HttpResponse response)
{
string filePath = #"c:\template.docx";
byte[] filebytes = File.ReadAllBytes(filePath);
using (MemoryStream stream = new MemoryStream(filebytes))
{
using (WordprocessingDocument myDoc = WordprocessingDocument.Open(stream, true))
{
// do some changes
...
myDoc.MainDocumentPart.Document.Save();
}
string docx = "docx";
response.Clear();
response.ClearHeaders();
response.ClearContent();
response.AddHeader("content-disposition", "attachment; filename=\"" + docx + ".docx\"");
response.ContentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
response.ContentEncoding = Encoding.GetEncoding("ISO-8859-1");
stream.Position = 0;
stream.CopyTo(response.OutputStream);
response.End();
}
}
Here is the non-working code :
public void OpenFile(HttpResponse response)
{
MemoryStream stream = this.FillTemplateOpenXmlWord();
string docx = "docx";
response.Clear();
response.ClearHeaders();
response.ClearContent();
response.AddHeader("content-disposition", "attachment; filename=\"" + docx + ".docx\"");
response.ContentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
response.ContentEncoding = Encoding.GetEncoding("ISO-8859-1");
stream.Position = 0;
stream.CopyTo(response.OutputStream);
response.End();
}
public MemoryStream FillTemplateOpenXmlWord()
{
string filePath = #"c:\template.docx";
byte[] filebytes = File.ReadAllBytes(filePath);
using (MemoryStream stream = new MemoryStream(filebytes))
{
using (WordprocessingDocument myDoc = WordprocessingDocument.Open(stream, true))
{
// do some changes
...
myDoc.MainDocumentPart.Document.Save();
}
return stream;
}
}
Any idea ?
thank you
Here's what I'm using for generating OpenXML files from memory stream. In this case it makes XLSX file from template on server, but it should be similar for other OpenXml formats.
Controller action:
public class ExportController : Controller
{
public FileResult Project(int id)
{
var model = SomeDateModel.Load(id);
ProjectExport export = new ProjectExport();
var excelBytes = export.Export(model);
FileResult fr = new FileContentResult(excelBytes, "application/vnd.ms-excel")
{
FileDownloadName = string.Format("Export_{0}_{1}.xlsx", DateTime.Now.ToString("yyMMdd"), model.Name)
};
return fr;
}
}
// Helper class
public class ProjectExport
{
private WorkbookPart workbook;
private Worksheet ws;
public byte[] Export(SomeDateModel model)
{
var template = new FileInfo(HostingEnvironment.MapPath(#"~\Export\myTemplate.xlsx"));
byte[] templateBytes = File.ReadAllBytes(template.FullName);
using (var templateStream = new MemoryStream())
{
templateStream.Write(templateBytes, 0, templateBytes.Length);
using (var excelDoc = SpreadsheetDocument.Open(templateStream, true))
{
workbook = excelDoc.WorkbookPart;
var sheet = workbook.Workbook.Descendants<Sheet>().First();
ws = ((WorksheetPart)workbook.GetPartById(sheet.Id)).Worksheet;
sheet.Name = model.Name;
// Here write some other stuff for setting values in cells etc...
}
templateStream.Position = 0;
var result = templateStream.ToArray();
templateStream.Flush();
return result;
}
}
looks like stream is closing when you return. it is in a using block. wouldn't that close the memory stream as soon as the filltemplate procedure ends?
The answer posted by gashac does not describe the issues you are going to get by not calling dispose on a stream.
Not disposing a memory stream causes memory leaks (same as a "using clause").
Memory streams keeps data in memory whereas file streams keeps data on the hdd.
Solution:
Save the memory stream into a byte array, dispose the memory stream and return the bytearray.
How to return bytearray instead stream
See the following thread to return a file as a bytearray:
HttpResponseMessage Content won't display PDF
I am using this code to import different pdf files pages to a single document. When i import large files (200 pages or above) I am getting a OutOfMemory exception. Am i doing something wrong here?
private bool SaveToFile(string fileName)
{
try
{
iTextSharp.text.Document doc;
iTextSharp.text.pdf.PdfCopy pdfCpy;
string output = fileName;
doc = new iTextSharp.text.Document();
pdfCpy = new iTextSharp.text.pdf.PdfCopy(doc, new System.IO.FileStream(output, System.IO.FileMode.Create));
doc.Open();
foreach (DataGridViewRow item in dvSourcePreview.Rows)
{
string pdfFileName = item.Cells[COL_FILENAME].Value.ToString();
int pdfPageIndex = int.Parse(item.Cells[COL_PAGE_NO].Value.ToString());
pdfPageIndex += 1;
iTextSharp.text.pdf.PdfReader reader = new iTextSharp.text.pdf.PdfReader(pdfFileName);
int pageCount = reader.NumberOfPages;
// set page size for the documents
doc.SetPageSize(reader.GetPageSizeWithRotation(1));
iTextSharp.text.pdf.PdfImportedPage page = pdfCpy.GetImportedPage(reader, pdfPageIndex);
pdfCpy.AddPage(page);
reader.Close();
}
doc.Close();
return true;
}
catch (Exception ex)
{
return false;
}
}
You're creating a new PdfReader for each pass. That's horribly inefficient. And because you've got a PdfImportedPage from each one, all those (probably redundant) PdfReader instances are never GC'ed.
Suggestions:
Two passes. First build a list of files & pages. Second operate on each file in turn, so you only ever have one PdfReader "open" at a time. Use PdfCopy.freeReader() when you're done with a given reader. This will almost certainly change the order in which your pages are added (maybe a Very Bad Thing).
One pass. Cache your PdfReader instances based on the file name. FreeReader again when you're done... but you probably won't be able to free any of them until you've dropped out of your loop. The caching alone may be enough to keep you from running out of memory.
Keep your code as is, but call freeReader() after you close a given PdfReader instance.
I haven't run into an OOM problems with iTextSharp. Are the PDFs created with iTextSharp or something else? Can you isolate the problem to a single PDF or a set of PDFs that might be corrupt? Below is sample code that creates 10 PDFs with 1,000 pages in each. Then it creates one more PDF and randomly pulls 1 page from those PDFs 500 times. On my machine it takes a little while to run but I don't see any memory issues or anything. (iText 5.1.1.0)
using System;
using System.Windows.Forms;
using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
//Folder that we will be working in
string WorkingFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "Big File PDF Test");
//Base name of PDFs that we will be creating
string BigFileBase = Path.Combine(WorkingFolder, "BigFile");
//Final combined PDF name
string CombinedFile = Path.Combine(WorkingFolder, "Combined.pdf");
//Number of "large" files to create
int NumberOfBigFilesToMakes = 10;
//Number of pages to put in the files
int NumberOfPagesInBigFile = 1000;
//Number of pages to insert into combined file
int NumberOfPagesToInsertIntoCombinedFile = 500;
//Create our test directory
if (!Directory.Exists(WorkingFolder)) Directory.CreateDirectory(WorkingFolder);
//First step, create a bunch of files with a bunch of pages, hopefully code is self-explanatory
for (int FileCount = 1; FileCount <= NumberOfBigFilesToMakes; FileCount++)
{
using (FileStream FS = new FileStream(BigFileBase + FileCount + ".pdf", FileMode.Create, FileAccess.Write, FileShare.Read))
{
using (iTextSharp.text.Document Doc = new iTextSharp.text.Document(PageSize.LETTER))
{
using (PdfWriter writer = PdfWriter.GetInstance(Doc, FS))
{
Doc.Open();
for (int I = 1; I <= NumberOfPagesInBigFile; I++)
{
Doc.NewPage();
Doc.Add(new Paragraph("This is file " + FileCount));
Doc.Add(new Paragraph("This is page " + I));
}
Doc.Close();
}
}
}
}
//Second step, loop around pulling random pages from random files
//Create our output file
using (FileStream FS = new FileStream(CombinedFile, FileMode.Create, FileAccess.Write, FileShare.Read))
{
using (Document Doc = new Document())
{
using (PdfCopy pdfCopy = new PdfCopy(Doc, FS))
{
Doc.Open();
//Setup some variables to use in the loop below
PdfReader reader = null;
PdfImportedPage page = null;
int RanFileNum = 0;
int RanPageNum = 0;
//Standard random number generator
Random R = new Random();
for (int I = 1; I <= NumberOfPagesToInsertIntoCombinedFile; I++)
{
//Just to output our current progress
Console.WriteLine(I);
//Get a random page and file. Remember iText pages are 1-based.
RanFileNum = R.Next(1, NumberOfBigFilesToMakes + 1);
RanPageNum = R.Next(1, NumberOfPagesInBigFile + 1);
//Open the random file
reader = new PdfReader(BigFileBase + RanFileNum + ".pdf");
//Set the current page
Doc.SetPageSize(reader.GetPageSizeWithRotation(1));
//Grab a random page
page = pdfCopy.GetImportedPage(reader, RanPageNum);
//Add it to the combined file
pdfCopy.AddPage(page);
//Clean up
reader.Close();
}
//Clean up
Doc.Close();
}
}
}
}
}
}