ITextSharp 4.1.6 - Remove existing line from PDF Template

前端 未结 1 1486
终归单人心
终归单人心 2020-12-22 02:57

I have a PDF template created in LibreOffice and I\'m filling it in using AcroFields. In some rare cases I\'d like to hide a particular field, thus I remove it using RemoveF

相关标签:
1条回答
  • 2020-12-22 03:17

    Removing individual drawing objects can get a little tricky but its not impossible. The hardest part is deciding which objects that you want to remove. Below is some sample code targetting iTextSharp 4.1.6 that first creates a PDF with two rectangles and then creates a second PDF based on the first with one of the rectangles removed. You'll need to apply your logic to figure out which rectangle you want to remove. Its possible that you don't actually have rectangles but lines that happen to form a rectangle, in that case you'll need to modify the code a bit, too.

    This first bit just creates a basic PDF on the desktop with two rectangles:

    //Create a file on the desktop with two rectangles
    var file1 = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "File1.pdf");
    using (var fs = new FileStream(file1, FileMode.Create, FileAccess.Write, FileShare.None)) {
        var doc = new Document();
        var writer = PdfWriter.GetInstance(doc, fs);
        doc.Open();
    
        var cb = writer.DirectContent;
    
        //Draw two rectangles
        cb.SaveState();
        cb.SetColorStroke(iTextSharp.text.Color.RED);
        cb.Rectangle(40, 60, 200, 100);
        cb.Stroke();
        cb.RestoreState();
    
        cb.SaveState();
        cb.SetColorStroke(iTextSharp.text.Color.BLUE);
        cb.Rectangle(500, 80, 90, 50);
        cb.Stroke();
        cb.RestoreState();
    
        doc.Close();
    }
    

    This next part is the more complicated part. I encourage you to do a Console.WriteLine(tokenizer.StringValue); inside of the while loop to see all of the PDF commands. You'll notice that they use RPN syntax which can take a little getting used. See the comments in the code for more questions.

    //Bind a reader to our first file
    var reader = new PdfReader(file1);
    //Get the first page (this would normally be done in a loop)
    var page = reader.GetPageN(1);
    //Get the "contents" of that page
    var objectReference = (PdfIndirectReference)page.Get(PdfName.CONTENTS);
    //Get the actual stream of the "contents"
    var stream = (PRStream)PdfReader.GetPdfObject(objectReference);
    //Get the raw bytes of the stream
    var streamBytes = PdfReader.GetStreamBytes(stream);
    //Convert the bytes to actual PDF tokens/commands
    var tokenizer = new PRTokeniser(new RandomAccessFileOrArray(streamBytes));
    //We're going to re-append each token to this below buffer and remove the ones that we don't want
    List<string> newBuf = new List<string>();
    //Loop through each PDf token
    while (tokenizer.NextToken()) {
        //Add them to our master buffer
        newBuf.Add(tokenizer.StringValue);
        //The "Other" token is used for most commands, so if we're on "Other" and the current command is "re" which is rectangle
        if (
            tokenizer.TokenType == PRTokeniser.TK_OTHER && //The "Other" token is used for most commands
            newBuf[newBuf.Count - 1] == "re" &&            //re is the rectangle command
            newBuf[newBuf.Count - 5] == "40"               //PDFs use RPN syntax so the red rectangle command was "40 60 200 100 re"
            ) {
            newBuf.RemoveRange(newBuf.Count - 5, 5);       //If the above conditions were met remove the last 5 commands
        }
    }
    
    //Convert our array to a string with newlines between each token, convert that to an ASCII byte array and push that back into the stream (erasing the current contents)
    stream.SetData(System.Text.Encoding.ASCII.GetBytes(String.Join("\n", newBuf.ToArray())));
    
    //Create a new file with the rectangle removed
    var file2 = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "File2.pdf");
    using (var fs = new FileStream(file2, FileMode.Create, FileAccess.Write, FileShare.None)) {
        //Bind a stamper to our read above which has the altered stream
        var stamper = new PdfStamper(reader, fs);
        //Loop through each page
        int total = reader.NumberOfPages;
        for (int i = 1; i <= total; i++) {
            //Push the content over page by page
            reader.SetPageContent(i, reader.GetPageContent(i));
        }
        stamper.Close();
    }
    reader.Close();
    
    0 讨论(0)
提交回复
热议问题