2

i try to draw simple shapes (rectangles, circles..) on an existing PDF using ITextSharp, without having to create a new PDF. I found a post who talk about this issue (itextsharp modify existing pdf (no new source pdf) and add watermark) and i would like to know if anybody could tell me more about it.

my aim is to modify a pdf by adding a circle on it, the current solution involve the creation of a new PDF (Itextsharp). Is it possible to add a circle on a PDF without creating a new one ? Thank you.

J.

Community
  • 1
  • 1
Json
  • 153
  • 2
  • 7
  • 17
  • 1
    You should specify your precise question before your question gets closed as being "too broad". – vib May 27 '15 at 08:59
  • i want to modify a pdf by adding a circle on it, the current solution involve the creation of a new PDF (Itextsharp). Is it possible to add a circle on a PDF without creating a new one ? – Json May 27 '15 at 09:09
  • Edit your question to clarify it and best also add a few lines of code you have tried..! – TaW May 27 '15 at 09:19
  • Your question is a duplicate of [How to update a PDF without creating a new PDF?](http://stackoverflow.com/questions/16081831/how-to-update-a-pdf-without-creating-a-new-pdf) You are asking something that goes against elementary logic: you want to write to an existing file and at the same time read that existing file. However, as soon as you write to the file, you can no longer read it. What applies to Word documents, also applies to PDF. – Bruno Lowagie May 27 '15 at 09:34
  • thank you very much for your explaination Bruno. – Json May 27 '15 at 09:50
  • you should post your comment as answer, i will accept it. – Json May 27 '15 at 09:54
  • I'm not sure if it's OK to copy/paste an already existing answer. Normally questions like this get closed as duplicate and the answer to the original question gets upvoted, but since you've asked to answer it here, I've added the answer. – Bruno Lowagie May 27 '15 at 10:00

3 Answers3

3

You can't read a file and write to it simultaneously. Think of how Word works: you can't open a Word document and write directly to it. Word always creates a temporary file, writes the changes to it, then replaces the original file with it and then throws away the temporary file.

You can do that too:

  • read the original file with PdfReader,
  • create a temporary file for PdfStamper, and when you're done,
  • replace the original file with the temporary file.

Or:

  • read the original file into a byte[],
  • create PdfReader with this byte[], and
  • use the path to the original file for PdfStamper.

This second option is more dangerous, as you'll lose the original file if you do something that causes an exception in PdfStamper.

As for adding content with PdfStamper, please take a look at the section entitled "Manipulating existing PDFs" in the free ebook The Best iText Questions on StackOverflow. You'll find questions such as:

All of these examples add content by creating a PdfContentByte instance like this:

PdfContentByte canvas = stamper.getOverContent(pagenumber);

It's this canvas you need to use when drawing a circle on the page with page number pagenumber. It is important that you use the correct coordinates when you do this. That's explained here: How to position text relative to page using iText?

Update:

Json posted the following code in the comments:

string oldFile = @"C:\Users\ae40394\Desktop\hello.pdf";
string newFile = @"C:\Users\ae40394\Desktop\NEW.pdf";
// creating a reader with the original PDF
PdfReader reader = new PdfReader(oldFile);
Rectangle rect = reader.GetPageSize(1);
FileStream fs = new FileStream(newFile,FileMode.Create);
using (PdfStamper stamper = new PdfStamper(reader, fs)) {
    // modify the pdf content
    PdfContentByte cb = stamper.GetOverContent(1);
    cb.SetColorStroke(iTextSharp.text.BaseColor.GREEN);
    cb.SetLineWidth(5f);
    cb.Circle(rect.GetLeft() + 30, rect.GetBottom() + 30 ,20f);
    cb.Stroke();
}    
reader.Close();
File.Replace(@"C:\Users\ae40394\Desktop\NEW.pdf", @"C:\Users\ae40394\Desktop\hello.pdf", @"C:\Users\ae40394\Desktop\hello.pdf.bac"); 

I slightly adapted the code, because:

  • There is no need for a Document object,
  • The stamper is closed when using is closed,
  • When the stamper is closed, so is the FileStream
  • the coordinates of the circle were hard coded. I used the page size to make sure they are made relative to the origin of the coordinate system, although to be sure, you may also want to check if there's a Crop Box.
Community
  • 1
  • 1
Bruno Lowagie
  • 75,994
  • 9
  • 109
  • 165
  • thx for your quick reply, i still need some explanations, to add a circle in the pdfStamper, i need a "PdfContentByte" and a "PdfWriter" no ? also, i don't see clearly how to remplace the original file with the temporary file :/ , thx again ! – Json May 27 '15 at 11:52
  • No, you don't need a `PdfWriter`. The answer provided by @shajeer is completely wrong. `PdfStamper` has a `getOverContent()` method. There are many examples available. I'll update my answer with some examples. – Bruno Lowagie May 27 '15 at 12:13
  • It's hard to read code added to comments. I'll copy/paste your code to my answer. At first sight, your code looks OK. – Bruno Lowagie May 27 '15 at 13:45
  • thank you @BrunoLowagie ! what i don't understand is that we still have two files with this solution :/ – Json May 27 '15 at 14:01
  • 1
    Not if you replace the old one with the new one. I forgot that line. – Bruno Lowagie May 27 '15 at 14:02
  • File.Replace(@"C:\Users\ae40394\Desktop\NEW.pdf", @"C:\Users\ae40394\Desktop\hello.pdf", @"C:\Users\ae40394\Desktop\hello.pdf"); – Json May 27 '15 at 14:14
  • but i get an error saying that the file is already used by another process – Json May 27 '15 at 14:14
  • Which file is in use? Do you have it open in a PDF viewer? Why is the path to the backup file the same as the path to the new file. Are you sure you closed the `PdfReader`? Maybe it's necessary to explicitly close the `FileStream`. I am a Java developer. I don't know how C# deals with files. – Bruno Lowagie May 27 '15 at 14:19
  • it,'s ok, it's working now, i changed the name of the backup file. But that is the problem, i don't want the backupfile, but i v=have no choice... thank you anyway for everything :) – Json May 27 '15 at 14:35
  • Can't you remove the backup file afterwards? – Bruno Lowagie May 27 '15 at 14:47
  • ha yes good idea =) but anyway, the problem is that, even if it's quick, i still create a file on the hard drive, what i wanted to avoid, but it's not possible as you explained ;) thx again for everything – Json May 27 '15 at 15:05
  • It depends on what you need. You can always create the PDF in memory instead of on the hard drive. – Bruno Lowagie May 27 '15 at 15:20
  • the last question, would it be possible to create a copy of the original PDF? but we don't save it on the hard drive, we use it has stream ( or something else), so the user is looking at a stream that only exist in the program and once he close the program, that destroy the copy that we created before ? – Json May 27 '15 at 15:22
  • Yes, just write to a `MemoryStream` instead of to a `FileStream`: http://stackoverflow.com/questions/2815761/create-pdf-in-memory-instead-of-physical-file – Bruno Lowagie May 27 '15 at 15:24
  • and can we display a pdf document who is in memoryStream ? thx – Json Jun 01 '15 at 05:42
  • Just send the bytes to a browser. – Bruno Lowagie Jun 02 '15 at 01:44
  • ok, i just read some indications on how to do so, but i'am not sure to understand exactly [link](http://stackoverflow.com/questions/6498732/view-byte-pdf-in-wpf-app) – Json Jun 02 '15 at 08:36
  • [Copy/Pasted reply:] I was in Singapore for the Communicasia conference the whole week. If you have an additional question, hence I can't answer your question today. [Extra comment:] I have no idea what you're asking. Sending bytes to a browser from an application server is as basic as a web application gets. – Bruno Lowagie Jun 06 '15 at 10:21
0

You CAN read a file and write to it simultaneously.

Here is an example:

private void button4_Click(object sender, EventArgs e) 
{ 
    using (PdfReader pdfReader = new PdfReader(new FileStream(pdfInput, FileMode.Open, FileAccess.Read, FileShare.Read))) 
    { 
        using (PdfStamper pdfStamper = new PdfStamper(pdfReader, new FileStream(pdfInput, FileMode.Open, FileAccess.Write, FileShare.None))) 
        { 
            PdfContentByte canvas = pdfStamper.GetUnderContent(1); 
            canvas.SetColorFill(BaseColor.YELLOW); 
            canvas.Rectangle(36, 786, 66, 16); 
            canvas.Fill(); 
        } 
    } 
    // PDF Datei im Anschluss anzeigen/öffnen 
    System.Diagnostics.Process.Start(pdfInput); 
} 
BR75
  • 633
  • 7
  • 25
  • By using a `Stream` instead of a file name as `PdfReader` constructor argument, you effectively make iText read the stream into a byte array before `PdfReader` construction. Thus, your code essentially is an implementation of the second option in @Bruno's answer: *read the original file into a `byte[]`, create `PdfReader` with this `byte[]`, and use the path to the original file for `PdfStamper`.* – mkl Apr 21 '17 at 18:53
  • One correction, though: You use `FileMode.Open` for the `FileStream` argument of the `PdfWriter` constructor. This is a bad idea as this causes you to write into the existing file, and if the new file is shorter than the original one, an end piece of the old file remains and you effectively get a broken PDF. You should use `FileMode.Create` or `FileMode.Truncate` instead. – mkl Apr 21 '17 at 19:00
  • Thank you! I am a beginner, sorry :D – BR75 Apr 22 '17 at 20:05
-1
string oldFile = @"C:\...6166-21.pdf";
        string newFile = @"C:\...NEW.pdf";

        // open the reader
        PdfReader reader = new PdfReader(oldFile);
        Rectangle size = reader.GetPageSizeWithRotation(1);
        Document document = new Document(size);

        FileStream fs = new FileStream(newFile, FileMode.Create, FileAccess.Write);
        PdfWriter writer = PdfWriter.GetInstance(document, fs);
        document.Open();

        // the pdf content
        PdfContentByte cb = writer.DirectContent;

        cb.SetColorStroke(iTextSharp.text.BaseColor.GREEN);
        cb.Circle(150f, 150f, 50f);
        cb.Stroke();

        // create the new page and add it to the pdf
        PdfImportedPage page = writer.GetImportedPage(reader, 1);
        cb.AddTemplate(page, 0, 0);

        // close the streams and voilá the file should be changed :)
        document.Close();
        fs.Close();
        writer.Close();
        reader.Close();
shajeer
  • 50
  • 3
  • 1
    This answer is not correct. Suppose that the original file contains annotations, a form, bookmarks,... then using `Document` and `PdfWriter` will **throw away all this content!** If you want to stamp new content on a document, you should use `PdfStamper`! Also: this doesn't answer the question. The question asks **How can I avoid that a new file is created?** You create a file that ends with `NEW.pdf` which is what the OP tries to avoid! – Bruno Lowagie May 27 '15 at 09:45
  • thank for your reply shajeer but that solution create a new PDF called "New", what i would like to do is to draw on the original one. – Json May 27 '15 at 09:46