ÐзвлиÑанеÑо на ÑекÑÑ Ð¾Ñ PDF изглежда леÑно, докаÑо не Ñе ÑблÑÑкаÑе Ñ Ð´Ð¾ÐºÑменÑ, пÑи койÑо ÑекÑÑовиÑÑ Ñлой липÑва, повÑеден е или е Ñазделен на деÑеÑки малки ÑаÑÑи Ð¾Ñ Ñимволи без логиÑен Ñед. PDFium VCL ви пÑедоÑÑÐ°Ð²Ñ Ð´Ð²Ðµ вÑ
одни ÑоÑки: маÑива Character[] за ÑÑÑов доÑÑÑп по Ð¸Ð½Ð´ÐµÐºÑ Ð´Ð¾ вÑеки Ð³Ð»Ð¸Ñ Ð½Ð° ÑÑÑаниÑаÑа, и ReadablePageContent за ÑÑÑÑкÑÑÑиÑан изглед, койÑо вÑзÑÑановÑва абзаÑи и Ð·Ð°Ð³Ð»Ð°Ð²Ð¸Ñ Ð¾Ñ Ð´ÑÑвоÑо Ñ Ñагове на PDF или ÑÑез евÑиÑÑиÑен анализ. ÐиÑо един Ð¾Ñ Ð´Ð²Ð°Ñа подÑ
ода не е ÑнивеÑÑален, Ñака Ñе е важно да ÑазбеÑеÑе какво пÑедоÑÑÐ°Ð²Ñ Ð²Ñеки Ð¾Ñ ÑÑÑ
.
ÐÑваÑÑне на докÑменÑа и капанÑÑ Ð½Ð° ÑÐ¸Ñ Ð¸Ñ Ð½ÐµÑÑпеÑ
TPdf оÑваÑÑ Ñайл ÑÑез задаване на FileName и пÑомÑна на Active := True. Ðажен деÑайл: Active := True никога не пÑедизвиква изклÑÑение. Ðко ÑайлÑÑ Ð»Ð¸Ð¿Ñва, заÑиÑен е Ñ Ð¿Ð°Ñола или е повÑеден, PDFium ÑÐ»Ð°Ð²Ñ Ð³ÑеÑкаÑа вÑÑÑеÑно и Active пÑоÑÑо оÑÑава False. Това ознаÑава, Ñе вÑеки ÑикÑл за извлиÑане ÑÑÑбва да бÑде заÑиÑен по ÑÐ»ÐµÐ´Ð½Ð¸Ñ Ð½Ð°Ñин:
Pdf := TPdf.Create(nil);
try
Pdf.FileName := 'report.pdf';
Pdf.Active := True;
if not Pdf.Active then
begin
ShowMessage('Could not open PDF (damaged or wrong password)');
Exit;
end;
// extraction follows here
finally
Pdf.Active := False;
Pdf.Free;
end;
ÐаÑиÑениÑе Ñ Ð¿Ð°Ñола Ñайлове изиÑÐºÐ²Ð°Ñ Ð·Ð°Ð´Ð°Ð²Ð°Ð½Ðµ на Pdf.Password := '...' пÑеди Active := True. ÐÑма вÑоÑи ÑанÑ: веднÑж Ñлед каÑо Active вÑÑне False, ÑÑÑбва да заÑвоÑиÑе и оÑвоÑиÑе оÑново докÑменÑа Ñ Ð¿ÑавилнаÑа паÑола.
ÐзвлиÑане ÑÑÑаниÑа по ÑÑÑаниÑа Ñ Character[]
Ðай-ниÑко ниво на подÑ
од обÑ
ожда вÑеки Ñимвол на вÑÑка ÑÑÑаниÑа. ÐадайÑе Pdf.PageNumber, за да заÑедиÑе ÑекÑÑÐ¾Ð²Ð¸Ñ Ñлой за Ñази ÑÑÑаниÑа, Ñлед коеÑо обÑ
одеÑе елеменÑиÑе до CharacterCount, използвайки ÑвойÑÑвоÑо Character[]. СÑÑÑва Ñи да пÑовеÑÑваÑе два Ñлага за вÑеки запиÑ: CharacterGenerated[i] обознаÑава авÑомаÑиÑно генеÑиÑани глиÑове, вмÑкнаÑи Ð¾Ñ ÑендеÑиÑаÑоÑо ÑдÑо (напÑÐ¸Ð¼ÐµÑ Ð¼ÐµÐºÐ¸ ÑиÑеÑа пÑи пÑенаÑÑне на Ñедове), коиÑо нÑÐ¼Ð°Ñ Ñеална Unicode ÑÑойноÑÑ, а CharacterMapError[i] ÑигнализиÑа, Ñе PDFium не е могÑл да ÑвÑÑже глиÑа Ñ ÐºÐ¾Ð´Ð¾Ð²Ð° ÑоÑка, коеÑо Ñе ÑлÑÑва пÑи кодиÑане на ÑÑиÑÑове без ToUnicode ÑаблиÑа.
procedure ExtractAllText(Pdf: TPdf; Output: TStrings);
var
Page, I: Integer;
Line: string;
Ch: WideChar;
begin
for Page := 1 to Pdf.PageCount do
begin
Pdf.PageNumber := Page;
Line := '';
for I := 0 to Pdf.CharacterCount - 1 do
begin
if Pdf.CharacterGenerated[I] or Pdf.CharacterMapError[I] then
Continue;
Ch := Pdf.Character[I];
if Ch = #13 then
Ch := #10; // normalize CR to LF
Line := Line + Ch;
end;
Output.Add(Line);
end;
end;
РезÑлÑаÑÑÑ Ðµ пÑоÑÑ Ð½Ð¸Ð· Ð¾Ñ Unicode кодови ÑоÑки в Ñеда, в койÑо PDFium ги избÑоÑва â?Ñова е ÑедÑÑ, в койÑо Ñе Ñе поÑвÑÐ²Ð°Ñ Ð² поÑока Ð¾Ñ ÑÑдÑÑжание, коеÑо невинаги ÑÑвпада Ñ Ð¿Ð¾ÑокаÑа на ÑеÑене Ð¾Ñ Ð»Ñво на дÑÑно. Ðа повеÑеÑо докÑменÑи на лаÑиниÑа, ÑÑздадени Ð¾Ñ ÑÑандаÑÑни оÑÐ¸Ñ Ð¿ÑогÑами, Ñова е напÑлно доÑÑаÑÑÑно. ÐÑи ÑканиÑани PDF Ñайлове, ÑазпознаÑи Ñ OCR Ñ Ð½ÐµÐ¾Ð±Ð¸Ñайни поÑледоваÑелноÑÑи Ð¾Ñ Ð³Ð»Ð¸Ñове, или пÑи ÑекÑÑ Ð¾Ñ Ð´ÑÑно на лÑво, ÑедÑÑ Ð¼Ð¾Ð¶Ðµ да бÑде гÑеÑен. Ð Ñакива ÑлÑÑаи ÑвойÑÑвоÑо ReadablePageContent ÑÑава по-полезно.
СÑÑÑкÑÑÑиÑано извлиÑане Ñ ReadablePageContent
ReadablePageContent оÑива едно ниво нагоÑе: Ñо вÑÑÑа Ð·Ð°Ð¿Ð¸Ñ TPdfReadableContent, ÑийÑо маÑив Fragments ÑÑдÑÑжа ÑÑагменÑи Ñ ÐµÑикеÑиÑано ÑÑдÑÑжание, вÑеки Ñ Kind, койÑо иденÑиÑиÑиÑа абзаÑи, заглавиÑ, елеменÑи Ð¾Ñ ÑпиÑÑÑи, клеÑки на ÑаблиÑи и Ñ.н. ÐогаÑо PDF ÑайлÑÑ ÑÑдÑÑжа ÑÑÑÑкÑÑÑиÑано дÑÑво (пÑовеÑеÑе Ñ Pdf.IsTagged), изÑоÑникÑÑ Ðµ rosStructure и ÑедÑÑ Ð½Ð° ÑеÑене е авÑоÑиÑеÑен. Ðа Ñайлове без еÑикеÑи, PDFium Ñе вÑÑÑа кÑм rosHeuristic, койÑо гÑÑпиÑа ÑимволиÑе по ÑеÑ
ниÑе огÑаниÑиÑелни Ñамки в логиÑеÑки единиÑи за ÑеÑене, но не може да гаÑанÑиÑа пÑлна ÑоÑноÑÑ.
procedure ExtractStructured(Pdf: TPdf; Output: TStrings);
var
Page: Integer;
Content: TPdfReadableContent;
Fragment: TPdfContentFragment;
begin
for Page := 1 to Pdf.PageCount do
begin
Content := Pdf.ReadablePageContent(Page);
for Fragment in Content.Fragments do
begin
case Fragment.Kind of
cfHeading : Output.Add('# ' + Fragment.Text);
cfParagraph : Output.Add(Fragment.Text);
cfListItem : Output.Add('- ' + Fragment.Text);
else
Output.Add(Fragment.Text);
end;
end;
end;
end;
Ðко Content.Source = rosHeuristic и ваÑиÑÑ ÑезÑлÑÐ°Ñ Ð¸Ð·Ð³Ð»ÐµÐ¶Ð´Ð° ÑазбÑÑкан, ÑекÑÑовиÑÑ Ñлой на докÑменÑа веÑоÑÑно не е бил запиÑан Ñ Ð¼Ð¸ÑÑл за Ñеда на ÑеÑене. Ð Ñози Ð¼Ð¾Ð¼ÐµÐ½Ñ ÐµÐ´Ð¸Ð½ÑÑвеноÑо надеждно ÑеÑение е повÑоÑно екÑпоÑÑиÑане Ð¾Ñ Ð¾ÑигиналноÑо пÑиложение Ñ Ð¿Ñавилно еÑикеÑиÑане, или изпÑлнение на ÑÑÑпка за поÑледваÑа обÑабоÑка, коÑÑо ÑоÑÑиÑа кооÑдинаÑиÑе на ÑимволиÑе по Y и Ñлед Ñова по X.
Ðакво ви Ð´Ð°Ð²Ð°Ñ CharacterOrigin и CharacterRectangle
РдвеÑе ÑвойÑÑва вÑÑÑÐ°Ñ Ð¿Ð¾Ð·Ð¸ÑиÑÑа на Ñимвол в пÑоÑÑÑанÑÑвоÑо на ÑÑÑаниÑаÑа (в ÑоÑки, Ñ Ð½Ð°Ñало в Ð´Ð¾Ð»Ð½Ð¸Ñ Ð»Ñв ÑгÑл и Y, наÑаÑÑÐ²Ð°Ñ Ð½Ð°Ð³Ð¾Ñе). CharacterOrigin[i] е базоваÑа опоÑна ÑоÑка на глиÑа; CharacterRectangle[i] е пÑлнаÑа Ð¼Ñ Ð¾Ð³ÑаниÑиÑелна Ñамка (bounding box). Те Ñа гÑадивниÑе елеменÑи за вÑиÑко извÑн Ð¾Ð±Ð¸ÐºÐ½Ð¾Ð²ÐµÐ½Ð¸Ñ ÑекÑÑ: оÑкÑиване на гÑаниÑи на колони, гÑÑпиÑане на Ñимволи в Ñедове ÑÑез ÑÑавнÑване на кооÑдинаÑиÑе Y в ÑамкиÑе на опÑеделен ÑолеÑанÑ, или изгÑаждане на каÑÑа на ÑÑвпадениÑÑа за ÑелекÑÐ¸Ñ Ð½Ð° ÑекÑÑ Ð² ÑеÑеÑ. Ðко ÑÑÑбва да оÑкÑиеÑе кой Ñимвол Ñе намиÑа под кликване на миÑкаÑа, CharacterIndexAtPos(X, Y, ToleranceX, ToleranceY) пÑави Ñова диÑекÑно, без да Ñе налага да обÑ
ождаÑе пÑавоÑгÑлниÑи.
ÐаÑÑÑойка на DLL ÑайловеÑе
PDFium VCL делегиÑа ÑÑлоÑо анализиÑане на PDF на оÑигинална DLL библиоÑека â?pdfium32.dll или pdfium64.dll в завиÑимоÑÑ Ð¾Ñ Ð²Ð°ÑаÑа Ñелева плаÑÑоÑма. ÐомпоненÑÑÑ Ñе доÑÑÐ°Ð²Ñ ÑÑÑ ÑкÑÐ¸Ð¿Ñ CopyDlls.bat, койÑо копиÑа пÑÐ°Ð²Ð¸Ð»Ð½Ð¸Ñ Ñайл в ÑиÑÑемнаÑа диÑекÑоÑÐ¸Ñ Ð½Ð° Windows. ÐзпÑлнениеÑо Ð¼Ñ Ð²ÐµÐ´Ð½Ñж каÑо админиÑÑÑаÑÐ¾Ñ Ð½Ð° маÑинаÑа за ÑазÑабоÑка е доÑÑаÑÑÑно; пÑи внедÑÑване копиÑаÑе DLL Ñайла в папкаÑа на изпÑÐ»Ð½Ð¸Ð¼Ð¸Ñ Ñайл на пÑиложениеÑо. ÐеÑÑииÑе Ñ Ð¿Ð¾Ð´Ð´ÑÑжка на V8 (pdfium32v8.dll, pdfium64v8.dll) Ñа знаÑиÑелно по-големи и Ñа необÑ
одими Ñамо ако ваÑиÑе PDF докÑменÑи ÑÑдÑÑÐ¶Ð°Ñ JavaScript, койÑо ÑÑÑбва да Ñе изпÑлнÑва. Ðа ÑиÑÑо извлиÑане на ÑекÑÑ ÑÑандаÑÑнаÑа веÑÑÐ¸Ñ Ðµ пÑавилниÑÑ Ð¸Ð·Ð±Ð¾Ñ.
Ðко DLL липÑва по вÑеме на изпÑлнение, задаванеÑо на Active := True Ñе доведе до ÑиÑ
а гÑеÑка, ÑоÑно какÑо пÑи липÑÐ²Ð°Ñ Ñайл, ÑÑй каÑо компоненÑÑÑ ÑÐ»Ð°Ð²Ñ Ð³ÑеÑкаÑа пÑи заÑеждане вÑÑÑеÑно. Ðинаги ÑеÑÑвайÑе на ÑиÑÑа ÑиÑÑема пÑеди ÑазпÑоÑÑÑанение.
Ðзползване на FontSize[] заедно Ñ Character[] за анализ на оÑоÑмлениеÑо
ÐÑвен обикновен ÑекÑÑ, API на ниво Ñимволи пÑедоÑÑÐ°Ð²Ñ Ð´Ð¾ÑÑÑп до FontSize[i], коеÑо вÑÑÑа ÑендиÑÐ°Ð½Ð¸Ñ ÑÐ°Ð·Ð¼ÐµÑ Ð² ÑоÑки на вÑеки глиÑ. РкомбинаÑÐ¸Ñ Ñ CharacterOrigin[i] и CharacterRectangle[i], Ñова ви позволÑва да ÑазгÑаниÑиÑе оÑÐ½Ð¾Ð²Ð½Ð¸Ñ ÑекÑÑ Ð¾Ñ Ð·Ð°Ð³Ð»Ð°Ð²Ð¸ÑÑа, без да ÑазÑиÑаÑе на ÑÑÑÑкÑÑÑиÑаноÑо дÑÑво. ÐаÑаж Ð¾Ñ Ñимволи, пÑи койÑо ÑазмеÑÑÑ Ð½Ð° ÑÑиÑÑа надвиÑава опÑеделен пÑаг, поÑÑи ÑигÑÑно е заглавие в докÑÐ¼ÐµÐ½Ñ Ð±ÐµÐ· еÑикеÑи. СÑÑаÑа ÑеÑ
ника Ñе пÑилага за Ñазпознаване на надпиÑи (малÑк ÑекÑÑ Ð¿Ð¾Ð´ огÑаниÑиÑелнаÑа Ñамка на изобÑажение) или бележки под Ð»Ð¸Ð½Ð¸Ñ (малÑк ÑекÑÑ Ð² долнаÑа ÑаÑÑ Ð½Ð° ÑÑÑаниÑаÑа). ÐиÑо Ð¾Ñ Ñова не изиÑква ÑендиÑане; и ÑÑиÑе ÑвойÑÑва ÑеÑÐ°Ñ Ð´Ð¸ÑекÑно Ð¾Ñ ÑекÑÑÐ¾Ð²Ð¸Ñ Ñлой, койÑо PDFium изгÑажда пÑи Active := True.
Ðдин нÑанÑ: FontSize[i] оÑÑазÑва ÑазмеÑа Ñлед пÑилагане на CTM (current transformation matrix) на ÑÑÑаниÑаÑа, Ñака Ñе докÑменÑ, пÑи койÑо авÑоÑÑÑ Ðµ маÑабиÑал ÑÑлаÑа ÑÑÑаниÑа, Ñе докладва пÑопоÑÑионално пÑоменени ÑазмеÑи. Ðко ÑÑавнÑваÑе ÑазмеÑи Ð¼ÐµÐ¶Ð´Ñ ÑÑÑаниÑи Ñ ÑазлиÑни ÑазмеÑи, ноÑмализиÑайÑе ÑпÑÑмо виÑоÑинаÑа на MediaBox на вÑÑка ÑÑÑаниÑа, пÑеди да вземаÑе ÑеÑÐµÐ½Ð¸Ñ Ð·Ð° пÑаговиÑе ÑÑойноÑÑи.
ÐапиÑване на извлеÑÐµÐ½Ð¸Ñ ÑекÑÑ Ð²Ñв Ñайл
ÐлаÑÑÑ TStringList на Delphi ÑабоÑи безпÑоблемно Ñ UTF-8 изÑ
од Ð¾Ñ Ð²ÐµÑÑÐ¸Ñ XE наÑам. ÐадайÑе WriteBOM := False, ако имаÑе нÑжда Ð¾Ñ Ñайл без BOM маÑÐºÐµÑ (много поÑледваÑи ÑиÑÑеми го изиÑкваÑ):
var
Lines: TStringList;
begin
Lines := TStringList.Create;
try
ExtractAllText(Pdf, Lines);
Lines.WriteBOM := False;
Lines.SaveToFile('output.txt', TEncoding.UTF8);
finally
Lines.Free;
end;
end;
ÐÑи изклÑÑиÑелно големи докÑменÑи, кÑдеÑо памеÑÑа е Ð¾Ñ Ð·Ð½Ð°Ñение, запиÑвайÑе диÑекÑно в TStreamWriter Ñ TEncoding.UTF8 в ÑикÑла за ÑÑÑаниÑи, вмеÑÑо пÑÑво да наÑÑÑпваÑе вÑиÑко в ÑпиÑÑк.
Ðзползване на TPdfView вÑв VCL пÑиложение
ÐÐ°Ð¼ÐµÑ Ð¸ пÑоизводиÑелноÑÑ Ð¿Ñи пакеÑна обÑабоÑка
ÐÑи големи аÑÑ
иви най-важноÑо неÑо, коеÑо ÑÑÑбва да ÑледиÑе, е бÑджеÑÑÑ Ð½Ð° памеÑÑа. ÐÑÑко извикване на Bitmap[] Ð·Ð°Ð´ÐµÐ»Ñ Ð½Ð¾Ð² TBitmap в кÑпÑинаÑа (heap), коеÑо пÑи ÑканиÑана ÑÑÑаниÑа Ñ 300 DPI е леÑно 25 MB необÑабоÑени пикÑелни данни пÑеди вÑÑкакво кодиÑане. Ðко обÑабоÑваÑе ÑÑÑаниÑиÑе в заÑвоÑен ÑикÑл без оÑвобождаване на памеÑÑа Ð¼ÐµÐ¶Ð´Ñ Ð¸ÑеÑаÑииÑе, обемÑÑ Ð½Ð° заеÑаÑа Ð¿Ð°Ð¼ÐµÑ Ñе наÑаÑÑва линейно Ñ Ð±ÑÐ¾Ñ Ð½Ð° изобÑажениÑÑа. ÐÑавилниÑÑ Ð¿Ð¾Ð´Ñ
од винаги е: извлиÑаÑе един bitmap, извÑÑÑваÑе необÑ
одимиÑе опеÑаÑии, оÑвобождаваÑе го и извлиÑаÑе ÑледваÑиÑ. Ðко ÑÑÑбва да пазиÑе пÑепÑаÑки кÑм нÑколко ÑаÑÑеÑни изобÑÐ°Ð¶ÐµÐ½Ð¸Ñ ÐµÐ´Ð½Ð¾Ð²Ñеменно за ÑÑавнение, пÑÑво ги пÑебÑойÑе Ñ BitmapCount, ÑазпÑеделеÑе ÑÑоÑвеÑно конÑейнеÑа Ñи и Ñлед Ñова оÑвобождавайÑе вÑÑко едно веднага Ñом пÑиклÑÑиÑе Ñ Ð½ÐµÐ³Ð¾, вмеÑÑо да оÑлагаÑе поÑиÑÑванеÑо за кÑÐ°Ñ Ð½Ð° докÑменÑа. ÐÑи докÑÐ¼ÐµÐ½Ñ Ð¾Ñ 500 ÑканиÑани ÑÑÑаниÑи Ñази Ñазлика може да бÑде Ð¼ÐµÐ¶Ð´Ñ 25 MB и 12 GB пикова заеÑоÑÑ Ð½Ð° памеÑÑа (peak RSS).
ÐомпоненÑÑÑ TPdfView пÑедлага ÑÑÑиÑе ÑвойÑÑва BitmapCount и Bitmap[], но ÑÑÑаниÑаÑа, Ð¾Ñ ÐºÐ¾ÑÑо ÑеÑе, е ÑекÑÑо показанаÑа ÑÑÑаниÑа на изгледа, а не TPdf.PageNumber. ÐваÑа ÑказаÑÐµÐ»Ñ Ð·Ð° ÑÑÑаниÑи Ñа незавиÑими; пÑомÑнаÑа на ÐµÐ´Ð¸Ð½Ð¸Ñ Ð½Ðµ пÑемеÑÑва дÑÑгиÑ. ÐÑв VCL пÑиложение Ñ Ð¿Ñеглед на живо можеÑе да извикаÑе Pdf.PageNumber := N, за да ÑпÑавлÑваÑе извлиÑанеÑо ÑÑез TPdf, докаÑо изгледÑÑ Ð¾ÑÑава на ÑÑÑаниÑаÑа, коÑÑо поÑÑебиÑелÑÑ Ð¿Ð¾Ñледно е пÑелиÑÑил. Това Ñазделение е ÑмиÑлено и запазва ÑÑÑÑоÑниеÑо на диÑÐ¿Ð»ÐµÑ Ð½Ð° ÑеÑеÑа ÑиÑÑо, докаÑо извлиÑанеÑо Ñе изпÑлнÑва вÑв Ñонов Ñежим.
ÐÑиложниÑе пÑогÑамни инÑеÑÑейÑи (API) за Character[], CharacterCount, CharacterOrigin[], CharacterRectangle[], ReadablePageContent и CharacterIndexAtPos, показани ÑÑк, Ñа ÑаÑÑ Ð¾Ñ ÐºÐ¾Ð¼Ð¿Ð¾Ð½ÐµÐ½Ñа PDFium VCL Component за Delphi и C++Builder.