Извлечение текста из массива TJ в PDF-оператор, используя PoDoFo lib

Я пытаюсь извлечь текст из файла PDF с помощью библиотеки PoDoFo, она работает для Tj оператор и не может сделать это для (Массив) TJ оператор. Я нашел этот кусок кода (с моей маленькой модификацией) Вот :

 const char*      pszToken = NULL;
PdfVariant       var;
EPdfContentsType eType;

PdfContentsTokenizer tokenizer( pPage );

double dCurPosX     = 0.0;
double dCurPosY     = 0.0;
double dCurFontSize = 0.0;
bool   bTextBlock   = false;
PdfFont* pCurFont   = NULL;

std::stack<PdfVariant> stack;while( tokenizer.ReadNext( eType, pszToken, var ) )
{

if( eType == ePdfContentsType_Keyword )
{
// support 'l' and 'm' tokens

_RPT1(_CRT_WARN, " %s\n", pszToken);

if( strcmp( pszToken, "l" ) == 0 ||
strcmp( pszToken, "m" ) == 0 )
{
dCurPosX = stack.top().GetReal();
stack.pop();
dCurPosY = stack.top().GetReal();
stack.pop();
}
else if (strcmp(pszToken, "Td") == 0)
{
dCurPosY = stack.top().GetReal();
stack.pop();
dCurPosX = stack.top().GetReal();
stack.pop();
}
else if (strcmp(pszToken, "Tm") == 0)
{
dCurPosY = stack.top().GetReal();
stack.pop();
dCurPosX = stack.top().GetReal();
stack.pop();
}
else if( strcmp( pszToken, "BT" ) == 0 )
{
bTextBlock   = true;
// BT does not reset font
// dCurFontSize = 0.0;
// pCurFont     = NULL;
}
else if( strcmp( pszToken, "ET" ) == 0 )
{
if( !bTextBlock )
fprintf( stderr, "WARNING: Found ET without BT!\n" );
}

if( bTextBlock )
{
if( strcmp( pszToken, "Tf" ) == 0 )
{
dCurFontSize = stack.top().GetReal();
stack.pop();
PdfName fontName = stack.top().GetName();
PdfObject* pFont = pPage->GetFromResources( PdfName("Font"), fontName );
if( !pFont )
{
PODOFO_RAISE_ERROR_INFO( ePdfError_InvalidHandle, "Cannot create font!" );
}

pCurFont = pDocument->GetFont( pFont );
if( !pCurFont )
{
fprintf( stderr, "WARNING: Unable to create font for object %i %i R\n",
pFont->Reference().ObjectNumber(),
pFont->Reference().GenerationNumber() );
}
}
else if( strcmp( pszToken, "Tj" ) == 0 ||
strcmp( pszToken, "'" ) == 0 )
{
AddTextElement( dCurPosX, dCurPosY, pCurFont, stack.top().GetString() );
stack.pop();
}
else if( strcmp( pszToken, "\"" ) == 0 )
{
AddTextElement( dCurPosX, dCurPosY, pCurFont, stack.top().GetString() );
stack.pop();
stack.pop(); // remove char spacing from stack
stack.pop(); // remove word spacing from stack
}
else if( strcmp( pszToken, "TJ" ) == 0 )
{
PdfArray array = stack.top().GetArray();
stack.pop();

for( int i=0; i<static_cast<int>(array.GetSize()); i++ )
{
_RPT1(_CRT_WARN, " variant: %s", array[i].GetDataTypeString());
if(array[i].IsHexString()) {
if(!pCurFont) {
_RPT1(_CRT_WARN, " : Could not Get font!!%d\n", i);
}
else {
if(!pCurFont->GetEncoding()) {
_RPT1(_CRT_WARN, ": could not get encoding\n",0);
} else {
PdfString s = array[i].GetString();
_RPT1(_CRT_WARN, " : valid :%s   ", s.IsValid()?"yes":"not");
_RPT1(_CRT_WARN, " ;hex :%s   ", s.IsHex()?"yes":"not");
_RPT1(_CRT_WARN, " ;unicode: %s   ", s.IsUnicode()?"yes":"not");

PdfString unicode = pCurFont->GetEncoding()->ConvertToUnicode(s,pCurFont);
const char* szText = unicode.GetStringUtf8().c_str();
_RPT1(_CRT_WARN, " : %s\n", strlen(szText)> 0? szText: "nothing");

}

}
}
else if(array[i].IsNumber()) {
_RPT1(_CRT_WARN, " : %d\n", array[i].GetNumber());
}

if( array[i].IsString() )//|| array[i].IsHexString())
AddTextElement( dCurPosX, dCurPosY, pCurFont, array[i].GetString() );
}
}
}
}
else if ( eType == ePdfContentsType_Variant )
{
stack.push( var );

_RPT1(_CRT_WARN, " variant: %s\n", var.GetDataTypeString());
}
else
{
// Impossible; type must be keyword or variant
PODOFO_RAISE_ERROR( ePdfError_InternalLogic );
}
}

и для кода я получаю этот вывод:

    BT
variant: Name
variant: Real
Tf
variant: Number
variant: Number
variant: Number
rg
variant: Real
variant: Number
variant: Number
variant: Number
variant: Real
variant: Real
Tm
variant: Array
TJ
variant: HexString : valid :yes    ;hex :yes    ;unicode: not    : nothing
variant: Number : -7
variant: HexString : valid :yes    ;hex :yes    ;unicode: not    : nothing
variant: Number : -15
variant: HexString : valid :yes    ;hex :yes    ;unicode: not    : nothing
variant: Number : -15
variant: HexString : valid :yes    ;hex :yes    ;unicode: not    : nothing
variant: Number : -11
variant: HexString : valid :yes    ;hex :yes    ;unicode: not    : nothing
variant: Number : -11
variant: HexString : valid :yes    ;hex :yes    ;unicode: not    : nothing
variant: Number : -19
variant: HexString : valid :yes    ;hex :yes    ;unicode: not    : nothing
variant: Number : -11
variant: HexString : valid :yes    ;hex :yes    ;unicode: not    : nothing
variant: Number : -15
variant: HexString : valid :yes    ;hex :yes    ;unicode: not    : nothing
variant: Number : -11
variant: HexString : valid :yes    ;hex :yes    ;unicode: not    : nothing
ET

Объект потока PDF хотел бы это (извините, но мне не разрешено давать вам файл PDF):

    q
Q
q
Q
q
q
q
1 0 0 1 37.68 785.28 cm
91.92 0 0 31.44 0 0 cm
/Img1 Do
Q
Q
q
q
1 0 0 1 431.28 780.24 cm
42.72 0 0 7.2 0 0 cm
/Img2 Do
Q
Q
q
BT
/F1 8.88 Tf
0 0 0 rg
0.9998 0 0 1 377.28 704.4 Tm
[<0026>-7<004F>-15<004C>-15<0048>-11<0051>-11<0057>-19<0058>-11<004F>-15<0058>-11<004C>] TJ
ET
Q
q
1 0 0 1 0 0 cm
0.4799 w
0 0 0 RG
377.28 703.44 m
415.2 703.44 l
S
Q
q
BT
/F1 8.16 Tf
0 0 0 rg
0.9998 0 0 1 377.28 687.36 Tm
[<0030>9<0027>-13<002C>-16<0003>1<0026>-13<0032>13<0031>-13<0036>-9<0037>-6<0035>-13<0038>-13<0026>-13<0037>-6<0003>1<0037>-6<0035>-13<0024>-9<0031>-13<0036>-9<0003>1<0028>-9<003B>-9<0033>-9<0028>-9<0035>-13<0037>-6<0003>1<0036>-9<0035>-13<002F>] TJ
ET

Файл PDF должен быть найден Вот или же Вот

1

Решение

1. Ответ на оригинальный вопрос, для которого центральная часть кода была такой:

else if( strcmp( pszToken, "TJ" ) == 0 )
{
PdfArray array = stack.top().GetArray();
stack.pop();

for( int i=0; i<static_cast<int>(array.GetSize()); i++ )
{
if( array[i].IsString() )
AddTextElement( dCurPosX, dCurPosY, pCurFont, array[i].GetString() );
}
}
}

и вопрос был:

Я заметил, что array[i].IsString() никогда не станет правдой. Это правильный способ получить текст от TJ оператор?

Краткий ответ:

Шестнадцатеричные строки в PoDoFo PdfVariants признаны IsHexString() вместо IsString(), Таким образом, вы должны проверить оба варианта строки:

if( array[i].IsString() || array[i].IsHexString() )

Длинный ответ:

В PDF есть два основных варианта строк:

Строковые объекты должны быть записаны одним из следующих двух способов:

  • В качестве последовательности буквенных символов, заключенных в круглые скобки () (с использованием ЛЕВОГО ПАРЕНТЕЗА (28h) и ПРАВОГО ПАРЕНТЕЗА (29h)); см. 7.3.4.2, «Литеральные строки».

  • В качестве шестнадцатеричных данных, заключенных в угловые скобки < > (используя ЗНАК МЕНЬШЕ (3Ch) и ЗНАЧЕНИЕ БОЛЬШЕ (3Eh)); см. 7.3.4.3, «Шестнадцатеричные строки».

(раздел 7.3.4 в ISO 32000-1)

Модели PoDoFo обе с использованием PdfString класс, который в контексте анализа часто оборачивается внутри PdfVariant или даже более конкретно в PdfObject,

Однако при определении типа объекта, содержащегося в нем, PdfVariant различает буквенные строки и шестнадцатеричные строки:

/** \returns true if this variant is a string (i.e. GetDataType() == ePdfDataType_String)
*/
inline bool IsString() const { return GetDataType() == ePdfDataType_String; }

/** \returns true if this variant is a hex-string (i.e. GetDataType() == ePdfDataType_HexString)
*/
inline bool IsHexString() const { return GetDataType() == ePdfDataType_HexString; }

(PdfVariant.h)

Тип PdfString внутри PdfVariant определяется в упаковке:

PdfVariant::PdfVariant( const PdfString & rsString )
{
Init();
Clear();

m_eDataType  = rsString.IsHex() ? ePdfDataType_HexString : ePdfDataType_String;
m_Data.pData = new PdfString( rsString );
}

(PdfVariant.cpp)

В случае вашего TJ компоненты массива аргументов, рассматриваемые строки читаются как шестнадцатеричные строки.

Поэтому в вашем коде вы должны учитывать IsHexString() а также IsString():

if( array[i].IsString() || array[i].IsHexString() )

2. После этого и после того, как код был пересмотрен, чтобы проверить с помощью IsHexString(), вопрос сосредоточился на

PdfString s = array[i].GetString();
_RPT1(_CRT_WARN, " : valid :%s   ", s.IsValid()?"yes":"not");
_RPT1(_CRT_WARN, " ;hex :%s   ", s.IsHex()?"yes":"not");
_RPT1(_CRT_WARN, " ;unicode: %s   ", s.IsUnicode()?"yes":"not");

PdfString unicode = pCurFont->GetEncoding()->ConvertToUnicode(s,pCurFont);
const char* szText = unicode.GetStringUtf8().c_str();
_RPT1(_CRT_WARN, " : %s\n", strlen(szText)> 0? szText: "nothing");

и проблема (как указано в комментариях), что

s.GetLength() возвращает 2 и unicode.GetLength() возвращает 0, преобразование не работает?

Анализ примеров документов Document2.pdf показывает, что рассматриваемый документ содержит необходимую информацию для извлечения текста. Единственный шрифт, присутствующий в этом документе, который используется с шестнадцатеричным кодированием, / F1, и его словарь шрифтов действительно содержит соответствующий / ToUnicode карта для надежного извлечения текста.

К сожалению, однако, PoDoFo, похоже, еще не реализован должным образом, используя эту карту для анализа. Я не вижу его нигде, извлекая / ToUnicode карта, чтобы сделать содержащуюся информацию доступной для разбора текста. Похоже, что PoDoFo нельзя использовать для правильного анализа текста документов с использованием составного шрифта Type0.

4

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]