Как с помощью itextsharp (или любой библиотеки pdf c #) открыть PDF-файл, заменить текст и снова сохранить его?

Используя itextsharp (или любую другую pdf-библиотеку C #), мне нужно открыть PDF-файл, заменить текст заполнителя фактическими значениями и вернуть его как byte [].

Может кто подскажет, как это сделать? Я просмотрел документацию itext и не могу понять, с чего начать. Пока я застрял на том, как получить исходный PDF-файл из PDFReader в объект Document, я полагаю, я, вероятно, подхожу к этому неправильно.

Большое спасибо


person Chris    schedule 19.11.2010    source источник
comment
Пока нашел это: johnnycode.com/blog/2010/03/05/   -  person Chris    schedule 19.11.2010


Ответы (3)


В конце концов, я использовал PDFescape, чтобы открыть существующий PDF-файл, и поместить некоторые поля формы туда, где мне нужно разместить свои поля, а затем снова сохранить его, чтобы создать свой PDF-файл.

http://www.pdfescape.com

Затем я нашел эту запись в блоге о том, как заменить поля формы:

http://www.johnnycode.com/blog/2010/03/05/using-a-template-to-programmatically-create-pdfs-with-c-and-itextsharp/

Все прекрасно работает! Вот код:

public static byte[] Generate()
{
  var templatePath = HttpContext.Current.Server.MapPath("~/my_template.pdf");

  // Based on:
  // http://www.johnnycode.com/blog/2010/03/05/using-a-template-to-programmatically-create-pdfs-with-c-and-itextsharp/
  var reader = new PdfReader(templatePath);
  var outStream = new MemoryStream();
  var stamper = new PdfStamper(reader, outStream);

  var form = stamper.AcroFields;
  var fieldKeys = form.Fields.Keys;

  foreach (string fieldKey in fieldKeys)
  {
    if (form.GetField(fieldKey) == "MyTemplatesOriginalTextFieldA")
      form.SetField(fieldKey, "1234");
    if (form.GetField(fieldKey) == "MyTemplatesOriginalTextFieldB")
      form.SetField(fieldKey, "5678");
  }

  // "Flatten" the form so it wont be editable/usable anymore  
  stamper.FormFlattening = true;  

  stamper.Close();
  reader.Close();

  return outStream.ToArray();
}
person Chris    schedule 19.11.2010
comment
Я не думаю, что вам нужно использовать ключи поля - вместо этого вы можете использовать: form.SetField (MyTemplatesOriginalTextFieldA, 1234); и так далее. - person Lachlan; 13.12.2010
comment
Ах да, именно так я и делаю сейчас. - person Chris; 14.12.2010
comment
Когда я писал этот код, это было именно так, потому что я заменял фактическое значение в (безымянных) полях, вместо того, чтобы указывать поля и присваивать им имена, как вы правильно предполагаете, это лучший вариант. - person Chris; 14.12.2010
comment
Ох, почему я не прочитал ваш комментарий раньше, это действительно правильный путь! :) Я редактирую ваш ответ, чтобы отразить ваш комментарий. И теперь я занимаюсь этим вместо того, чтобы зацикливать форму до тех пор, пока она не достигнет жестко запрограммированного значения, я сделал ее немного более динамичной, зациклив словарь с параметрами для заполнения. Не стесняйтесь комментировать, если у вас есть вопросы или замечания. Ваше здоровье! - person T_D; 04.04.2016

К сожалению, я искал что-то похожее и не мог понять. Ниже было примерно то, что я получил, может быть, вы можете использовать это как отправную точку. Проблема в том, что PDF на самом деле не сохраняет текст, а вместо этого использует таблицы поиска и некоторые другие загадочные волшебства. Этот метод считывает байтовые значения для страницы и пытается преобразовать их в строку, но, насколько я могу судить, он может работать только с английским языком и пропускает некоторые специальные символы, поэтому я отказался от своего проекта и двинулся дальше.

string contents = string.Empty();
Document doc = new Document();
PdfReader reader = new PdfReader("pathToPdf.pdf");
using (MemoryStream memoryStream = new MemoryStream())
{

    PdfWriter writer = PdfWriter.GetInstance(doc, memoryStream);
    doc.Open();
    PdfContentByte cb = writer.DirectContent;
    for (int p = 1; p <= reader.NumberOfPages; p++)
    {
        // add page from reader
        doc.SetPageSize(reader.GetPageSize(p));
        doc.NewPage();

        // pickup here something like this:
        byte[] bt = reader.GetPageContent(p);
        contents = ExtractTextFromPDFBytes(bt);

        if (contents.IndexOf("something")!=-1)
        {
            // make your own pdf page and add to cb (contentbyte)

        }
        else
        {
            PdfImportedPage page = writer.GetImportedPage(reader, p);
            int rot = reader.GetPageRotation(p);
            if (rot == 90 || rot == 270)
                cb.AddTemplate(page, 0, -1.0F, 1.0F, 0, 0, reader.GetPageSizeWithRotation(p).Height);
            else
                cb.AddTemplate(page, 1.0F, 0, 0, 1.0F, 0, 0);
        }
    }
    reader.Close();
    doc.Close();
    File.WriteAllBytes("pathToOutputOrSamePathToOverwrite.pdf", memoryStream.ToArray());

Это взято с этого сайта.

private string ExtractTextFromPDFBytes(byte[] input) 
{ 
    if (input == null || input.Length == 0) return ""; 

     try 
     { 
         string resultString = ""; 

         // Flag showing if we are we currently inside a text object 
         bool inTextObject = false; 

         // Flag showing if the next character is literal  
         // e.g. '\\' to get a '\' character or '\(' to get '(' 
         bool nextLiteral = false; 

         // () Bracket nesting level. Text appears inside () 
         int bracketDepth = 0; 

         // Keep previous chars to get extract numbers etc.: 
         char[] previousCharacters = new char[_numberOfCharsToKeep]; 
         for (int j = 0; j < _numberOfCharsToKeep; j++) previousCharacters[j] = ' '; 


          for (int i = 0; i < input.Length; i++) 
          { 
              char c = (char)input[i]; 

              if (inTextObject) 
              { 
                  // Position the text 
                  if (bracketDepth == 0) 
                  { 
                      if (CheckToken(new string[] { "TD", "Td" }, previousCharacters)) 
                      { 
                          resultString += "\n\r"; 
                      } 
                      else 
                      { 
                          if (CheckToken(new string[] { "'", "T*", "\"" }, previousCharacters)) 
                          { 
                               resultString += "\n"; 
                           } 
                           else 
                           { 
                               if (CheckToken(new string[] { "Tj" }, previousCharacters)) 
                                { 
                                    resultString += " "; 
                                } 
                            } 
                        } 
                    }

                    // End of a text object, also go to a new line. 
                    if (bracketDepth == 0 && 
                        CheckToken(new string[] { "ET" }, previousCharacters)) 
                    { 

                        inTextObject = false; 
                        resultString += " "; 
                   } 
                   else 
                   { 
                        // Start outputting text 
                        if ((c == '(') && (bracketDepth == 0) && (!nextLiteral)) 
                        { 
                            bracketDepth = 1; 
                        } 
                        else 
                        { 
                            // Stop outputting text 
                            if ((c == ')') && (bracketDepth == 1) && (!nextLiteral)) 
                            { 
                                 bracketDepth = 0; 
                            } 
                            else 
                            { 
                                // Just a normal text character: 
                                if (bracketDepth == 1) 
                                { 
                                    // Only print out next character no matter what.  
                                    // Do not interpret. 
                                    if (c == '\\' && !nextLiteral) 
                                    { 
                                        nextLiteral = true; 
                                    } 
                                    else 
                                    { 
                                        if (((c >= ' ') && (c <= '~')) || 
                                            ((c >= 128) && (c < 255))) 
                                        { 
                                            resultString += c.ToString(); 
                                        } 

                                        nextLiteral = false; 
                                    } 
                                } 
                            } 
                        } 
                    } 
                } 

                // Store the recent characters for  
                // when we have to go back for a checking 
                for (int j = 0; j < _numberOfCharsToKeep - 1; j++) 
                { 
                    previousCharacters[j] = previousCharacters[j + 1]; 
                } 
                previousCharacters[_numberOfCharsToKeep - 1] = c; 

                // Start of a text object 
                if (!inTextObject && CheckToken(new string[] { "BT" }, previousCharacters)) 
                { 
                    inTextObject = true; 
                } 
            } 
        return resultString; 
    } 
    catch 
    { 
        return ""; 
     } 
} 

 private bool CheckToken(string[] tokens, char[] recent) 
 { 
     foreach (string token in tokens) 
     { 
         if ((recent[_numberOfCharsToKeep - 3] == token[0]) && 
           (recent[_numberOfCharsToKeep - 2] == token[1]) && 
           ((recent[_numberOfCharsToKeep - 1] == ' ') || 
           (recent[_numberOfCharsToKeep - 1] == 0x0d) || 
           (recent[_numberOfCharsToKeep - 1] == 0x0a)) && 
           ((recent[_numberOfCharsToKeep - 4] == ' ') || 
           (recent[_numberOfCharsToKeep - 4] == 0x0d) || 
           (recent[_numberOfCharsToKeep - 4] == 0x0a))) 
           { 
               return true; 
           } 
    }
    return false; 
} 
person WSkid    schedule 19.11.2010
comment
чего _numberOfCharsToKeep не хватает, чтобы объявить это. Итак, расскажите мне, как это определить. - person Manish Sharma; 13.09.2013

У меня есть скрипт на Python, который заменяет текст в PDF:

import re
import sys
import zlib

# Module to find and replace text in PDF files
#
# Usage:
#   python pdf_replace.py <input_filename> <text_to_find> <text_to_replace> <output_filename>
#
# @author Ionox0

input_filename = sys.argv[1]
text_to_find = sys.argv[2]
text_to_replace = sys.argv[3]
output_filename sys.argv[4]

pdf = open(input_filename, "rb").read()

# Create a copy of the PDF content to make edits to
pdf_copy = pdf[0:]

# Search for stream objects with text to replace
stream = re.compile(r'.*?FlateDecode.*?stream(.*?)endstream', re.S)

for s in stream.findall(pdf):
    s = s.strip('\r\n')

    try:
        text = zlib.decompress(s)

        if text_to_find in text:
            print('Found match:')
            print(text)

            text = text.replace(text_to_find, text_to_replace)
            pdf_copy = pdf_copy.replace(s, zlib.compress(text))
    except:
        pass

with open(output_filename, 'wb') as out:
    out.write(pdf_copy)
person ionox0    schedule 06.02.2019
comment
В большинстве случаев результат будет либо без изменений, либо в недопустимом формате PDF. (Зрители PDF могут показать его правильно, исправив его под капотом, но, тем не менее, он недействителен.) - person mkl; 08.02.2019