iText или iTextSharp элементарное редактирование текста

Я могу извлечь текст из страниц в формате PDF разными способами:

String pageText = PdfTextExtractor.GetTextFromPage(reader, i);

Это может быть использовано для получения любого текста на странице.

В качестве альтернативы:

byte[] contentBytes = iTextSharp.text.pdf.parser.ContentByteUtils.GetContentBytesForPage(reader, i);

Возможности бесконечны.

Теперь я хочу удалить / отредактировать определенное слово, например, явные слова, конфиденциальную информацию (очевидно, что ставить над ними черные ящики - плохая идея:) или что-то еще из PDF (простой и текстовый). Я могу найти это слово очень хорошо, используя подход выше. Я могу сосчитать его появления и т. Д.

Меня не волнует макет или тот факт, что PDF не предназначен для манипулирования таким образом.

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

3 ответа

Решение

Если вы хотите изменить содержимое страницы, недостаточно изменить поток содержимого страницы. Страница может содержать ссылки на объекты XObject, содержащие контент, который вы хотите удалить.

Вторичная проблема состоит из изображений. Например: предположим, что ваш документ состоит из отсканированного документа, который был распознан. В этом случае недостаточно удалить (векторный) текст, вам также нужно будет манипулировать (пиксельным) текстом на изображении.

Предполагая, что вашей вторичной проблемы не существует, вам понадобится двойной подход:

  1. получить содержимое страницы в виде текста, чтобы определить, на каких страницах есть имена или слова, которые вы хотите удалить.
  2. рекурсивно переберите все потоки контента, чтобы найти этот текст и переписать эти потоки контента без этого текста.

Исходя из вашего вопроса, я предполагаю, что вы уже решили проблему 1. Решение проблемы 2 не так тривиально. В главе 15 моей книги у меня есть пример, где при извлечении текста возвращается "Hello World", но когда вы смотрите внутрь потока контента, вы видите:

BT
/F1 12 Tf
88.66 367 Td
(ld) Tj
-22 0 Td
(Wor) Tj
-15.33 0 Td
(llo) Tj
-15.33 0 Td
(He) Tj
ET

Прежде чем вы сможете удалить "Hello World" из этого фрагмента потока, вам потребуется некоторая эвристика, чтобы ваша программа распознала текст в этом синтаксисе.

Как только вы нашли текст, вам нужно переписать поток. Для вдохновения вы можете взглянуть на функциональность удаления OCG в пакете itext-xtra.

Короче говоря: если ваши PDF-файлы относительно просты, то есть: текст может быть легко обнаружен в другом потоке контента (контент страницы и контент Form XObject), тогда это просто вопрос переписывания этих потоков после некоторых манипуляций со строками.

Я сделал вам простой пример с именем ReplaceStream это заменяет "Hello World" с "HELLO WORLD" в PDF.

public void manipulatePdf(String src, String dest) throws IOException, DocumentException {
    PdfReader reader = new PdfReader(src);
    PdfDictionary dict = reader.getPageN(1);
    PdfObject object = dict.getDirectObject(PdfName.CONTENTS);
    if (object instanceof PRStream) {
        PRStream stream = (PRStream)object;
        byte[] data = PdfReader.getStreamBytes(stream);
        stream.setData(new String(data).replace("Hello World", "HELLO WORLD").getBytes());
    }
    PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
    stamper.close();
    reader.close();
}

Некоторые предостережения:

  • Я проверяю object это поток. Это также может быть массив потоков. В этом случае вам нужно перебрать этот массив.
  • Я не проверяю, существуют ли формы XObject для этой страницы.
  • Я предполагаю что Hello World может быть легко обнаружен в синтаксисе PDF.
  • ...

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

C# эквивалент кода Бруно:

static void manipulatePdf(String src, String dest)
    {
        PdfReader reader = new PdfReader(src);
        PdfDictionary dict = reader.GetPageN(1);
        PdfObject pdfObject = dict.GetDirectObject(PdfName.CONTENTS);
        if (pdfObject.IsStream()) {
            PRStream stream = (PRStream)pdfObject;
            byte[] data = PdfReader.GetStreamBytes(stream);
            stream.SetData(System.Text.Encoding.ASCII.GetBytes(System.Text.Encoding.ASCII.GetString(data).Replace("Hello World", "HELLO WORLD")));
        }
        FileStream outStream = new FileStream(dest, FileMode.Create);
        PdfStamper stamper = new PdfStamper(reader, outStream);
        reader.Close();
    }

Я обновлю это, если окажется, что он все еще содержит ошибки.

В продолжение моего предыдущего кода на C# и замечания Бруно о том, что GetDirectObject(PdfName.CONTENTS) также может возвращать массив, а не поток: в моем конкретном случае это оказалось правдой.

Возвращенный объект PdfObject вернул "true" для IsArray(). Я проверил, и все элементы массива были PdfIndirectReference.

Дальнейший взгляд на API дал две полезные части информации:

  1. У PdfIndirectReference есть свойство "Number", которое ведет вас к другому PdfObject.
  2. Вы можете получить доступ к ссылочному объекту, используя reader.GetPdfObject(int ref), где ref - это свойство "Number" объекта IndirectReferenceObject.

С этого момента вы получаете новый объект PdfObject, который вы можете проверить с помощью IsStream() и изменить его согласно ранее опубликованному коду.

Так что это работает (заметьте, это быстро и грязно, но это работает для моих конкретных целей...):

      // Get the contents of my page...
      PdfObject pdfObject = pageDict.GetDirectObject(PdfName.CONTENTS);

      // Check that this is, in fact, an array or something else...
      if (pdfObject.IsArray())
      {
          PdfArray streamArray = pageDict.GetAsArray(PdfName.CONTENTS);

          for (int j = 0; j < streamArray.Size; j++)
             {
                  PdfIndirectReference arrayEl = (PdfIndirectReference)streamArray[j];

                  PdfObject refdObj = reader.GetPdfObject(arrayEl.Number);

                  if (refdObj.IsStream())
                     {
                        PRStream stream = (PRStream)refdObj;
                        byte[] data = PdfReader.GetStreamBytes(stream);
                        stream.SetData(System.Text.Encoding.ASCII.GetBytes(System.Text.Encoding.ASCII.GetString(data).Replace(targetedText, newText)));
                     }
              }

       }
Другие вопросы по тегам