Как извлечь фактические названия шрифтов из PDF с помощью iTextSharp?

Я использую iTextSharp для обработки PDF, и мне нужно извлечь весь текст из существующего PDF, который написан определенным шрифтом.

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

CIDFont+F1
CIDFont+F2
CIDFont+F3
CIDFont+F4
CIDFont+F5

что совсем не похоже на реальные названия шрифтов, которые я ищу.

  • Я попробовал проанализировать файл с помощью iText RUPS. Тот же результат.

То есть я не смог увидеть фактические названия шрифтов нигде в структуре документа.

Тем не менее, Adobe Acrobat DC показывает правильные названия шрифтов в панели Format, когда я выбираю различные текстовые поля на холсте документа (например, Arial, Courier New, Roboto), так что эта информация должна где-то храниться.

Как получить эти реальные названия шрифтов при разборе PDF с помощью iTextSharp?

Комментарии к вопросу (3)
Решение

Как было определено в ходе комментариев к вопросу, названия шрифтов анонимизированы во всех метаданных PDF для шрифта, но сама встроенная программа шрифта содержит фактическое название шрифта.

(Таким образом, PDF, строго говоря, нарушен, хотя вряд ли какая-либо программа будет жаловаться на это).

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

Вот пример концепции, основанной на архитектуре, использованной в этом ответе, на которую вы ссылались, т.е. с использованием RenderFilter:

class FontProgramRenderFilter : RenderFilter
{
    public override bool AllowText(TextRenderInfo renderInfo)
    {
        DocumentFont font = renderInfo.GetFont();
        PdfDictionary fontDict = font.FontDictionary;
        PdfName subType = fontDict.GetAsName(PdfName.SUBTYPE);
        if (PdfName.TYPE0.Equals(subType))
        {
            PdfArray descendantFonts = fontDict.GetAsArray(PdfName.DESCENDANTFONTS);
            PdfDictionary descendantFont = descendantFonts[0] as PdfDictionary;
            PdfDictionary fontDescriptor = descendantFont.GetAsDict(PdfName.FONTDESCRIPTOR);
            PdfStream fontStream = fontDescriptor.GetAsStream(PdfName.FONTFILE2);
            byte[] fontData = PdfReader.GetStreamBytes((PRStream)fontStream);
            MemoryStream dataStream = new MemoryStream(fontData);
            dataStream.Position = 0;
            MemoryPackage memoryPackage = new MemoryPackage();
            Uri uri = memoryPackage.CreatePart(dataStream);
            GlyphTypeface glyphTypeface = new GlyphTypeface(uri);
            memoryPackage.DeletePart(uri);
            ICollection names = glyphTypeface.FamilyNames.Values;
            return names.Where(name => name.Contains("Arial")).Count() > 0;
        }
        else
        {
            // analogous code for other font subtypes
            return false;
        }
    }
}

Класс MemoryPackage взят из этого ответа, который был моей первой находкой при поиске того, как считать информацию из шрифта в памяти с помощью .Net.

Применяется к вашему PDF-файлу следующим образом:

using (PdfReader pdfReader = new PdfReader(SOURCE))
{
    FontProgramRenderFilter fontFilter = new FontProgramRenderFilter();
    ITextExtractionStrategy strategy = new FilteredTextRenderListener(
            new LocationTextExtractionStrategy(), fontFilter);
    Console.WriteLine(PdfTextExtractor.GetTextFromPage(pdfReader, 1, strategy));
}

результат

This is Arial.

Остерегайтесь: Это всего лишь доказательство концепции.

С одной стороны, вам, конечно же, потребуется реализовать часть, закомментированную как аналогичный код для других подтипов шрифтов выше; и даже часть TYPE0 не готова к использованию в производстве, поскольку она учитывает только FONTFILE2 и не обрабатывает значения null изящно.

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

Комментарии (2)