Почему моя таблица XREF повреждена после удаления или добавления аннотаций? - PullRequest
0 голосов
/ 22 апреля 2020

Я оцениваю iText 7 как альтернативу для наших текущих пакетов обработки PDF. Я создаю Po C, где вход представляет собой PDF-документ с контейнерами (т.е. аннотации). Logi c должен удалить существующие QR-коды, а затем добавить новые в контейнеры.

Хотя logi c, кажется, работает и коды удалены и / или добавлены, все еще существуют проблемы с измененными файл. В нем говорится об ошибках с таблицей XREF. Ошибка вводится также, если я только удаляю или добавляю код, поэтому я подумал, что причина может быть в общем коде.

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

Интересно, где я ошибся и что делать, чтобы это исправить или исследовать дальше. С кодом ниже легко воспроизвести. Есть мысли по этому поводу?

Я тестирую с помощью iText 7.1.11 в. NET Базовом консольном приложении, написанном с C#.

Основной код процесса:

using (var pdfFileStream = new MemoryStream(pdfFileBytes))
{
  using (var outputFileStream = new ByteArrayOutputStream())
  {
    var pdfReader = new PdfReader(pdfFileStream);
    var pdfWriter = new PdfWriter(outputFileStream);
    var pdfDocument = new PdfDocument(pdfReader, pdfWriter);

    var pageCount = pdfDocument.GetNumberOfPages();
    PdfPage page = null;
    IList<PdfAnnotation> annotations = null;
    for (int i = 0; i < pageCount; i++)
    {
      page = pdfDocument.GetPage(i + 1);
      annotations = page.GetAnnotations();

      if (annotations.Count == 0)
      {
        continue;
      }

      // Get QR code containers
      var containers = FilterAnnotations(annotations, containerName);

      if (containers.Count == 0)
      {
        continue;
      }

      // Get QR codes instances
      var instances = FilterAnnotations(annotations, instanceName);

      if (instances.Count > 0)
      {
         foreach (var instance in instances)
         {
            RemoveQRCode(instance, page);
         }
      }

      foreach (var container in containers)
      {
         AddQRCode(container, page, pdfDocument, url);
      }

    }

    pdfDocument.Close();

    return outputFileStream.ToArray();
  }
}

Я отфильтровываю аннотации, которые я хотел бы удалить (т.е. коды) по темам. То же самое касается поиска контейнеров, в которые нужно поместить аннотации кода:

private List<PdfAnnotation> FilterAnnotations(IList<PdfAnnotation> annotations, string subject)
{
  return annotations.Where(a =>
    !string.IsNullOrEmpty(GetSubject(a)) &&
    GetSubject(a).ToLowerInvariant().Equals(subject, StringComparison.OrdinalIgnoreCase)).ToList();
}

Затем я прохожу через отфильтрованные аннотации oop:

private void RemoveQRCode(PdfAnnotation annotation, PdfPage page)
{
  page.RemoveAnnotation(annotation);
}

Следующий код описывает добавление кодов:

private void AddQRCode(PdfAnnotation container, PdfPage page, PdfDocument document, string url)
{
  BarcodeQRCode qrCode = new BarcodeQRCode(url);
  PdfFormXObject qrCodeForm = qrCode.CreateFormXObject(ColorConstants.BLACK, document);

  var rectangle = GetRectangle(container);

  var canvas = new PdfCanvas(page);
  canvas
    .SaveState()
    .AddXObject(qrCodeForm, rectangle)
    .RestoreState();

  canvas.Release();

  PdfStampAnnotation stampAnnotation = new PdfStampAnnotation(rectangle);
  stampAnnotation.SetSubject(new PdfString(instanceName));
  stampAnnotation.SetAppearance(PdfName.N, new PdfAnnotationAppearance(qrCodeForm.GetPdfObject()));
  stampAnnotation.SetFlags(PdfAnnotation.PRINT);

  page.AddAnnotation(stampAnnotation);
}

Я для краткости пропустил код.

Заранее спасибо.

Обновление 1

Согласно запросу @ mkl два файла , результатом которых является test2_mod. Я также добавил больше кода. Спасибо за ваш быстрый ответ.

1 Ответ

0 голосов
/ 23 апреля 2020

Проблема, о которой заявляет PDF-XChange Editor Plus, действительно является именно той, которая была объяснена в записи PDF-XChange на форуме , на которую ссылается запись на форуме , на которую вы ссылаетесь в комментарии

Проблема очень простая - поток XRef содержит неверную запись / Index .

/Index[0 185]/Size 186

Файл имеет только один раздел XRef, в котором описаны все объекты в файле, поэтому эта запись не является обязательной. Запись / Size правильная, поскольку в документе есть объекты с максимальным числом 185, но запись / Index говорит, что в нем всего 185 записей вместо 186 обязательных, а сам поток XRef не содержит запись о последнем объекте (185, сам поток XRef). Обычно это не мешает открытию файлов PDF большинством читателей, но не соответствует спецификации PDF.

( Ответ Lzcat - Tracker Supp )

При использовании потоков перекрестных ссылок iText 7 также не добавляет запись для самого потока перекрестных ссылок в перекрестные ссылки, в случае вашего примера документа:

80 0 obj 
  [...] /Index[0 80] [...] /Size 81

Значение массива Индекс здесь означает, что этот поток перекрестных ссылок содержит 80 записей, начиная с 0, то есть записей для объекта 0..79. Но поток перекрестных ссылок здесь находится в объекте 80, поэтому он не содержит записи для себя.

Но, действительно, спецификация PDF требует

Как и любой поток, поток перекрестных ссылок должен быть косвенным объектом. Поэтому запись для него должна существовать либо в потоке перекрестных ссылок (обычно в самом себе), либо в таблице перекрестных ссылок (в файлах гибридных ссылок; см. 7.5.8.4, «Совместимость с приложениями, которые не поддерживают сжатые ссылочные потоки»). ).

(ISO 32000-1, раздел 7.5.8.3 «Данные потока перекрестных ссылок»)

Кстати, пример PDF из этого В ветке форума поддержки есть записи о создателе и продюсере, утверждающие, что он был создан "Apitron PDF Kit". Очевидно, iText - не единственная библиотека с этой причудой.

Быстрое исправление

Я попытался быстро исправить проблему; поскольку я больше разбираюсь в Java, я сделал это для Java версии iText:

protected void writeXrefTableAndTrailer(PdfDocument document, PdfObject fileId, PdfObject crypto) throws IOException {
    [...]
    if (writer.isFullCompression()) {
        [...]
        xrefStream.put(PdfName.Size, new PdfNumber(this.size()));

        // vvv--- add these two lines
        xrefStream.getIndirectReference().setOffset(startxref);
        sections = createSections(document, false);

        int offsetSize = getOffsetSize(Math.max(startxref, size()));

(класс модуля ядра PdfXrefTable)

Это должно выглядеть очень похоже в C# версии iText 7, только инициалы имен методов пишутся с большой буквы.

Осторожно, однако, это просто проверка на дым. Для производственного использования требуется гораздо больше испытаний.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...