2016-11-17 8 views
0

Я использовал расширенную версию LocationTextExtractionStrategy для извлечения связных текстов pdf и их позиций/размеров. Я сделал это, используя locationalResult. Это работало хорошо, пока я не протестировал pdf, содержащий тексты с другим шрифтом (ttf). Внезапно эти тексты разделяются на отдельные символы или небольшие фрагменты.iText LocationTextExtractionStrategy/HorizontalTextExtractionStrategy разбивает текст на отдельные символы

Например, "Деталь" не больше одного объекта в списке locationalResult но расщепляется на шесть элементов (D, E, T, A, I, L)

Я попытался с помощью HorizontalTextExtractionStrategy сделав метод getLocationalResult общественности:

public List<TextChunk> GetLocationalResult() 
{ 
    return (List<TextChunk>)locationalResultField.GetValue(this); 
} 

и используя PdfReaderContentParser для извлечения текста:

reader = new PdfReader("some_pdf"); 
PdfReaderContentParser parser = new PdfReaderContentParser(reader); 
var strategy = parser.ProcessContent(i, HorizontalTextExtractionStrategy()); 

foreach (HorizontalTextExtractionStrategy.HorizontalTextChunk chunk in strategy.GetLocationalResult()) 
{ 
    // Do something with the chunk  
} 

но это также возвращает тот же результат. Есть ли другой способ извлечь связанные тексты из pdf?

ответ

0

После отладки в библиотеке iTextSharp я понял, что мои тексты рисуются с помощью оператора TJ, как упоминалось также в mkl.

[<0027> -0.2<00280037> 0.2<0024002c> 0.2<002f> -0.2<0003>] TJ 

IText обрабатывает эти тексты не как единый PdfString, но как массив PdfObjects, который заканчивается в вызове renderListener.RenderText(renderInfo) для каждого PdfString элемента в нем (см ShowTextArray класса и DisplayPdfString метода). Однако в методе RenderText информация о связи строк PDF внутри массива теряется, и каждый элемент добавляется к locationalResult в качестве независимого объекта.

Как моя цель - извлечь «аргумент одной инструкции рисования текста», я расширил класс PdfContentStreamProcessor по поводу нового метода ProcessTexts, который возвращает список этих атомных строк. Мое обходное решение не очень красивое, поскольку мне пришлось скопировать несколько частных полей и методов из original source, но он работает для меня.

class PdfContentStreamProcessorEx : PdfContentStreamProcessor 
{ 
    private IDictionary<int, CMapAwareDocumentFont> cachedFonts = new Dictionary<int, CMapAwareDocumentFont>(); 
    private ResourceDictionary resources = new ResourceDictionary(); 
    private CMapAwareDocumentFont font = null; 

    public PdfContentStreamProcessorEx(IRenderListener renderListener) : base(renderListener) 
    { 
    } 

    public List<string> ProcessTexts(byte[] contentBytes, PdfDictionary resources) 
    { 
     this.resources.Push(resources); 
     var texts = new List<string>(); 
     PRTokeniser tokeniser = new PRTokeniser(new RandomAccessFileOrArray(new RandomAccessSourceFactory().CreateSource(contentBytes))); 
     PdfContentParser ps = new PdfContentParser(tokeniser); 
     List<PdfObject> operands = new List<PdfObject>(); 
     while (ps.Parse(operands).Count > 0) 
     { 
      PdfLiteral oper = (PdfLiteral)operands[operands.Count - 1]; 
      if ("Tj".Equals(oper.ToString())) 
      { 
       texts.Add(getText((PdfString)operands[0])); 
      } 
      else if ("TJ".Equals(oper.ToString())) 
      { 
       string text = string.Empty; 
       foreach (PdfObject entryObj in (PdfArray)operands[0]) 
       { 
        if (entryObj is PdfString) 
        { 
         text += getText((PdfString)entryObj); 
        } 
       } 
       texts.Add(text); 
      } 
      else if ("Tf".Equals(oper.ToString())) 
      { 
       PdfName fontResourceName = (PdfName)operands[0]; 
       float size = ((PdfNumber)operands[1]).FloatValue; 

       PdfDictionary fontsDictionary = resources.GetAsDict(PdfName.FONT); 
       CMapAwareDocumentFont _font; 
       PdfObject fontObject = fontsDictionary.Get(fontResourceName); 
       if (fontObject is PdfDictionary) 
        _font = GetFont((PdfDictionary)fontObject); 
       else 
        _font = GetFont((PRIndirectReference)fontObject); 

       font = _font; 
      } 
     } 

     this.resources.Pop(); 

     return texts; 
    } 

    string getText(PdfString @in) 
    { 
     byte[] bytes = @in.GetBytes(); 
     return font.Decode(bytes, 0, bytes.Length); 
    } 

    private CMapAwareDocumentFont GetFont(PRIndirectReference ind) 
    { 
     CMapAwareDocumentFont font; 
     cachedFonts.TryGetValue(ind.Number, out font); 
     if (font == null) 
     { 
      font = new CMapAwareDocumentFont(ind); 
      cachedFonts[ind.Number] = font; 
     } 
     return font; 
    } 

    private CMapAwareDocumentFont GetFont(PdfDictionary fontResource) 
    { 
     return new CMapAwareDocumentFont(fontResource); 
    } 

    private class ResourceDictionary : PdfDictionary 
    { 
     private IList<PdfDictionary> resourcesStack = new List<PdfDictionary>(); 

     virtual public void Push(PdfDictionary resources) 
     { 
      resourcesStack.Add(resources); 
     } 

     virtual public void Pop() 
     { 
      resourcesStack.RemoveAt(resourcesStack.Count - 1); 
     } 

     public override PdfObject GetDirectObject(PdfName key) 
     { 
      for (int i = resourcesStack.Count - 1; i >= 0; i--) 
      { 
       PdfDictionary subResource = resourcesStack[i]; 
       if (subResource != null) 
       { 
        PdfObject obj = subResource.GetDirectObject(key); 
        if (obj != null) return obj; 
       } 
      } 
      return base.GetDirectObject(key); // shouldn't be necessary, but just in case we've done something crazy 
     } 
    } 
} 
1

Я использовал расширенную версию LocationTextExtractionStrategy для извлечения связных текстов pdf и их положений/размеров. Я сделал это, используя locationalResult. Это работало хорошо, пока я не протестировал pdf, содержащий тексты с другим шрифтом (ttf). Внезапно эти тексты разделяются на отдельные символы или небольшие фрагменты.

Эта проблема возникает из-за неправильных ожиданий относительно содержимого переменной частного члена .

Этот список экземпляров TextChunk содержит фрагменты текста, которые были перенаправлены в стратегию из ракурса синтаксического анализа (или, вероятно, по мере того, как они были предварительно обработаны некоторым классом фильтров), и фреймворк пересылает каждую отдельную строку, встречающуюся в контенте поток отдельно.

Таким образом, если, по-видимому, связанное слово в потоке контента фактически нарисовано с использованием нескольких строк, вы получаете для него несколько экземпляров TextChunk.

На самом деле существует некоторый «интеллект» в методе getResultantText, который правильно соединяет эти куски, добавляя пространство, где это необходимо, и так далее.

В случае Вашего документа, «ДЕТАЛЬ» обычно рисуется так:

[<0027> -0.2<00280037> 0.2<0024002c> 0.2<002f> -0.2<0003>] TJ 

Как вы видите, есть небольшие точки вставки текста перемещается между «D» и «Е», «Т» и " A ',' I 'и' L ', и' L 'и' '. (Такие мини-ходы обычно представляют кернинг.) Таким образом, вы получите индивидуальные TextChunk экземпляров для 'D', 'ET', 'AI' и 'L'.

Следует признать, что член LocationTextExtractionStrategy.locationalResult не очень хорошо документирован; но поскольку это частный член, это ИМХО простительно.


Это это работало хорошо для многих документов из-за многие PDF создателей не прилагая кернинг и просто рисунок, связанный текст одиночных строковые объектов.


The HorizontalTextExtractionStrategy происходит от LocationTextExtractionStrategy и в основном отличается от него, как он организует TextChunk экземпляры в одной строке. Таким образом, вы увидите такую ​​же фрагментацию здесь.


Есть ли другой способ извлечь связные тексты из PDF?

Если вы хотите «подключенные тексты», как в «объектах атомной строки в потоке контента», вы уже имеете их.

Если вы хотите «связные тексты», как в «визуально связанных текстов, независимо от того, где составные буквы рисуются в потоке контента», вы должны склеить эти TextChunk экземпляры вместе как LocationTextExtractionStrategy и HorizontalTextExtractionStrategy делать в getResultantText в комбинации с методами сравнения в их соответствующих реализациях TextChunkLocationDefaultImp и HorizontalTextChunkLocation.

+0

Использование термина «связанные тексты» Я имел в виду «объекты атомной струны в контенте», но, как вы можете прочитать в моем ответе, у меня их уже нет. Поэтому поэтому не принимал в качестве ответа. Спасибо вам за хорошее объяснение! – seeb

+0

@seeb. Строка в формате pdf ограничена угловыми или круглыми скобками ('<...>' или '(...)'), которые я называю «объектами атомной струны». Кажется, вы ищете «аргумент инструкции * для рисования одиночного текста» (который в случае ** TJ ** представляет собой массив строк и чисел). В зависимости от фактических чисел в этом массиве составляющие строки могут быть отрисованы довольно далеко друг от друга. – mkl

+0

Да, это именно то, что я ищу. Извините за запутанные термины, которые я использовал. Я не знаком с терминологией pdf. – seeb

Смежные вопросы