Major part of my job is automation of engineering process, so I have to create simple program, that compares 2 different version of 1 drawn element, by overlapping drawings, in order to review differences. Drawings represent single sheet PDF files.
I'm using .Net Framework and C# 4.5;
iTextSharp library for editing PDF files;
Initially, I'm getting 2 files, read them and create the third one, that contains the result;
var file1 = "file1.pdf"; var file2 = "file2.pdf"; var result = "result.pdf"; using (Stream f1Stream = new FileStream(file1, FileMode.Open)) using (Stream f2Stream = new FileStream(file2, FileMode.Open)) using (Stream resultStream = new FileStream(result, FileMode.Create, FileAccess.ReadWrite)) using (PdfReader f2Reader = new PdfReader(f2Stream)) using (PdfReader f1Reader = new PdfReader(f1Stream)) { PdfStamper pdfStamper = new PdfStamper(f1Reader, resultStream); PdfContentByte pdfContentByte = pdfStamper.GetOverContent(1); var page = pdfStamper.GetImportedPage(f2Reader, 1); pdfContentByte.AddTemplate(page,2,2); pdfStamper.Close(); }
The code above makes just that, but a few sequential questions are arising
- I want to change the color of elements in the result file i.e. elements that come from the 1st drawing in green and the others from 2nd one - in red color. Maybe I have to change the color of entities in initial 2 PDFs and then to merge;
- Initial files have layers, and because they are two sequential revision of the same construction element and differences between them are very few, they have identical layers. And I want to have " layerFoo " and " layerFoo@ " in the result PDF. Maybe I have to rename all the layers in one the the 2 initial PDFs and then to merge them.
Аll suggestions are welcomed including usage of another library :)
--> Edit1 Big thanks to Chris Haas! You are absolutely right for token type and string value! iTextRUPS is great helping tool for understanding the structure of PDF files. Following code is taken from the post that you pointed me out.
The following statement:
stream.SetData(System.Text.Encoding.ASCII.GetBytes(String.Join("\n", newBuf.ToArray())));
updates the stream of the file and then with
using (var fs = new FileStream(file2, FileMode.Create, FileAccess.Write, FileShare.None))
{
var stamper = new PdfStamper(reader, fs);
reader.SetPageContent(1,reader.GetPageContent(1));
stamper.Close();
}
the new file is created with updated stream.
I made 1 simple test file with only 2 lines, change their color and save back to a new file. No problem! After that, I tried the same simple operation with real file, that represents real drawing of construction element, the result file was less than half of the original and was broken. What comes to mind is the updated stream is saved to the new file but the other information inside other containers is not saved, it's just the stream.
Because I stuck with that, I continue to the next step of investigation -> layers I wrote this code in order to get available layers in a PDF file. I will try to insert more records into layers dictionary to see what will happen.
var resourcesReference = page.Get(PdfName.RESOURCES) as PdfIndirectReference;
var resources = PdfReader.GetPdfObject(resourcesReference) as PdfDictionary;
var propertiesObjhectReferences = resources.Get(PdfName.PROPERTIES);
var properties = PdfReader.GetPdfObject(propertiesObjhectReferences) as PdfDictionary;
foreach (var property in properties.Keys)
{
var layerReference = properties.Get(property);
var layerObject = PdfReader.GetPdfObject(layerReference) as PdfDictionary;
foreach (var key in layerObject.Keys)
{
if (key.ToString()!=PdfName.TYPE.ToString())
{
var layerName = layerObject.GetAsString(key).ToUnicodeString();
}
}
}
If I come back to my main goal from the top of the post, I tends to insert the stream and layers from first file into second in order to obtain result file, that contains objects from the previous 2, painted in different colors + layers from both.
Feel free to suggest me another, more simpler and beautiful solution! I will be happy if you revise my code and correct it! Thank You very much!
EDIT 2
I will simplify the work because the lack of time, just change the color of entities inside one PDF and put it on the background on the other.
const string Pdf = "file1.pdf";
var reader = new PdfReader(Pdf);
var page = reader.GetPageN(1);
var objectReference = page.Get(PdfName.CONTENTS) as PdfIndirectReference;
var stream = (PRStream)PdfReader.GetPdfObject(objectReference);
var streamBytes = PdfReader.GetStreamBytes(stream);
var tokenizer = new PRTokeniser(new RandomAccessFileOrArray(streamBytes));
var newBuf = new List<string>();
while (tokenizer.NextToken())
{
var token = tokenizer.StringValue;
newBuf.Add(token);
if (tokenizer.TokenType == PRTokeniser.TokType.OTHER
&& newBuf[newBuf.Count - 1].Equals("S", StringComparison.CurrentCultureIgnoreCase))
{
newBuf.Insert(newBuf.Count - 1, "0");
newBuf.Insert(newBuf.Count - 1, "1");
newBuf.Insert(newBuf.Count - 1, "1");
newBuf.Insert(newBuf.Count - 1, "RG");
}
}
var resultStream = String.Join("\n", newBuf.ToArray());
stream.SetData(System.Text.Encoding.ASCII.GetBytes(resultStream));
var file2 = Pdf.Insert(Pdf.Length - 4, "Result");
using (var fs = new FileStream(file2, FileMode.Create, FileAccess.Write, FileShare.None))
{
var stamper = new PdfStamper(reader, fs);
reader.SetPageContent(1, reader.GetPageContent(1));
stamper.Close();
}
Result PDF is broken and iTextRUPS throws exception when try to get the stream data from the page.