СимпÑомÑÑ Ñе поÑви в помоÑна пÑогÑама за копиÑане на ÑÑÑаниÑи, изгÑадена вÑÑÑ Ñ HotPDF Component: изиÑкванеÑо на ÑÑÑаниÑа 1 Ð¾Ñ Ð´Ð¾ÐºÑÐ¼ÐµÐ½Ñ Ð¾Ñ ÑÑи ÑÑÑаниÑи поÑледоваÑелно пÑоизвеждаÑе ÑÑÑаниÑа 2. ÐÑовеÑкаÑа на логикаÑа на индекÑиÑане не оÑкÑи ниÑо неÑедно. ÐзвикванеÑо използваÑе логиÑеÑки индекÑ, базиÑан на 0, аÑиÑмеÑикаÑа беÑе пÑавилна, гÑаниÑниÑе ÑÑÐ»Ð¾Ð²Ð¸Ñ Ð±ÑÑ Ð° наÑед. ÐÑпÑеки Ñова вÑеки пÑÑ Ð¸Ð·Ð»Ð¸Ð·Ð°Ñе гÑеÑнаÑа ÑÑÑаниÑа.
ÐÑгÑÑ Ð¸Ð·Ð¾Ð±Ñо не беÑе в кода за копиÑане. Той беÑе в наÑина, по койÑо HotPDF изгÑаждаÑе ÑÐ²Ð¾Ñ Ð²ÑÑÑеÑен маÑив Ð¾Ñ ÑÑÑаниÑи пÑи заÑеждане на Ñайла.

Ðве подÑедби, един изÑоÑник на обÑÑкване
PDF ÑайлÑÑ Ðµ колекÑÐ¸Ñ Ð¾Ñ Ð½ÐµÐ¿Ñеки обекÑи, вÑеки иденÑиÑиÑиÑан Ñ Ð½Ð¾Ð¼ÐµÑ Ð½Ð° обекÑ. ФайловаÑа ÑÑÑÑкÑÑÑа не налага никакво задÑлжение Ñези номеÑа да оÑÑазÑÐ²Ð°Ñ Ñеда на ÑеÑене. ÐÐ±ÐµÐºÑ 1 може да ÑÑдÑÑжа ÑÑÑаниÑа 2; Ð¾Ð±ÐµÐºÑ 20 може да ÑÑдÑÑжа ÑÑÑаниÑа 1. Това, коеÑо дейÑÑвиÑелно опÑÐµÐ´ÐµÐ»Ñ Ñеда на ÑеÑене, е дÑÑвоÑо на ÑÑÑаниÑиÑе: йеÑаÑÑ
Ð¸Ñ Ð¾Ñ ÑеÑниÑи /Pages, ÑииÑо маÑиви /Kids избÑоÑÐ²Ð°Ñ Ð¿ÑепÑаÑкиÑе кÑм ÑÑÑаниÑиÑе в поÑледоваÑелноÑÑÑа, в коÑÑо ÑеÑеÑÑÑ ÑÑÑбва да ги покаже (ISO 32000-1 §7.7.3).
ÐокÑменÑÑÑ, койÑо задейÑÑва бÑга, имаÑе ÑледнаÑа ÑÑÑÑкÑÑÑа на дÑÑвоÑо на ÑÑÑаниÑиÑе:
{ Pages tree root, object 16 }
16 0 obj
<<
/Type /Pages
/Count 3
/Kids [20 0 R { logical page 1 }
1 0 R { logical page 2 }
4 0 R] { logical page 3 }
>>
endobj
СлÑÑайно Ñе оказа, Ñе ÑайлÑÑ Ð¸Ð·Ð±ÑоÑва Ð¾Ð±ÐµÐºÑ 1 и Ð¾Ð±ÐµÐºÑ 4 пÑеди Ð¾Ð±ÐµÐºÑ 20 в поÑока Ð¾Ñ Ð±Ð°Ð¹Ñове. ÐÑеки паÑÑеÑ, койÑо обÑ
ожда непÑекиÑе обекÑи в Ñеда на Ñайла и ги запиÑва в PageArr пÑи намиÑане на ÑеÑниÑи Ð¾Ñ Ñип ÑÑÑаниÑа, би завÑÑÑил Ñ Ð¾Ð±ÐµÐºÑ 1 на Ð¸Ð½Ð´ÐµÐºÑ 0, Ð¾Ð±ÐµÐºÑ 4 на Ð¸Ð½Ð´ÐµÐºÑ 1 и Ð¾Ð±ÐµÐºÑ 20 на Ð¸Ð½Ð´ÐµÐºÑ 2. Така логиÑеÑка ÑÑÑаниÑа 1 Ñе намиÑа на PageArr[2]. ÐзиÑкванеÑо на ÑÑÑаниÑа Ñ Ð¸Ð½Ð´ÐµÐºÑ 0 извлиÑа логиÑеÑка ÑÑÑаниÑа 2 вмеÑÑо неÑ.
ТоÑно Ñова пÑавеÑ
а и дваÑа вÑÑÑеÑни пÑÑÑ Ð·Ð° анализ на HotPDF. ТÑадиÑионниÑÑ Ð¿ÑÑ, използван за PDF 1.3/1.4 Ñайлове, и модеÑниÑÑ Ð¿ÑÑ, използван за докÑменÑи Ñ Ð¿Ð¾ÑоÑи Ð¾Ñ Ð¾Ð±ÐµÐºÑи (PDF 1.5+), изгÑаждаÑ
а PageArr ÑÑез обÑ
ождане на непÑекиÑе обекÑи вÑв ÑизиÑеÑÐºÐ¸Ñ Ñед на Ñайла, вмеÑÑо да ÑÐ»ÐµÐ´Ð²Ð°Ñ Ð²ÐµÑигаÑа /Kids.
ÐоÑвÑÑждаване на Ñ Ð¸Ð¿Ð¾ÑезаÑа
ÐÑеди да Ñе пÑедпÑиеме какваÑо и да е коÑекÑиÑ, неÑÑоÑвеÑÑÑвиеÑо ÑÑÑбваÑе да бÑде доказано, а не пÑоÑÑо пÑедположено. ÐнÑÑÑÑменÑÑÑ Ð·Ð° команден Ñед qpdf пÑави Ñова леÑно:
{ shell }
qpdf --show-pages input.pdf
{ Output reveals Kids order: 20 0 R, then 1 0 R, then 4 0 R }
qpdf --show-object="16 0 R" input.pdf
{ Shows the Pages dictionary with /Kids in reading order }
ÐзвлиÑанеÑо на вÑÑка ÑÑÑаниÑа пооÑделно и пÑовеÑкаÑа на ÑазмеÑиÑе на ÑайловеÑе поÑвÑÑдиÑ
а ÑÑоÑвеÑÑÑвиеÑо: Ñова, коеÑо PageArr[0] пÑоизвеждаÑе, беÑе ÑÑдÑÑжаниеÑо на логиÑеÑка ÑÑÑаниÑа 2, а PageArr[2] ÑÑдÑÑжаÑе логиÑеÑка ÑÑÑаниÑа 1. ЦиклиÑноÑо измеÑÑване беÑе неоÑпоÑимоÑо доказаÑелÑÑво. Това ÑÑÑо обÑÑни заÑо пÑоблемÑÑ Ñе поÑвÑваÑе в множеÑÑво ÑазлиÑни изÑ
одни докÑменÑи: вÑеки PDF Ñайл, в койÑо обекÑиÑе на ÑÑÑаниÑиÑе ÑлÑÑайно Ð¸Ð¼Ð°Ñ Ð¿Ð¾-малки номеÑа Ð¾Ñ Ð¿Ð¾-Ñанна логиÑеÑка ÑÑÑаниÑа, би задейÑÑвал Ñози бÑг.
Ðма пÑоÑÑа пÑиÑина, поÑади коÑÑо PDF ÑайловеÑе Ñе Ð¾ÐºÐ°Ð·Ð²Ð°Ñ Ð² Ñова ÑÑÑÑоÑние. ÐнкÑеменÑиÑе пÑи Ð·Ð°Ð¿Ð¸Ñ (incremental saves) добавÑÑ Ð°ÐºÑÑализиÑани обекÑи Ñ Ð½Ð¾Ð²Ð¸ номеÑа на обекÑи, оÑÑавÑйки ÑÑаÑиÑе ÑлоÑове в ÑаблиÑаÑа Ñ ÐºÑÑÑÑоÑани пÑепÑаÑки да ÑоÑÐ°Ñ Ð½Ð°Ð½Ð¸ÐºÑде. РедакÑоÑиÑе, коиÑо добавÑÑ Ð·Ð°Ð³Ð»Ð°Ð²Ð½Ð° ÑÑÑаниÑа, Ñ Ð²Ð¼ÑÐºÐ²Ð°Ñ Ñ Ð²Ð¸Ñок Ð½Ð¾Ð¼ÐµÑ Ð½Ð° обекÑ, незавиÑимо Ð¾Ñ Ð¿Ð¾Ð·Ð¸ÑиÑÑа й в маÑива Kids. ÐÑкои генеÑаÑоÑи пÑоÑÑо пиÑÐ°Ñ ÑÑÑаниÑи в Ñед, Ñдобен за пÑедаване на ÑÑдÑÑжаниеÑо, а не в логиÑеÑка поÑледоваÑелноÑÑ. PDF ÑоÑмаÑÑÑ Ð½Ðµ изиÑква Ð¾Ñ ÑÑÑ Ð´Ð° пÑавим неÑо дÑÑго.
ÐоÑекÑиÑÑа: Ñледване на маÑива Kids
ÐÑавилниÑÑ Ð¿Ð¾Ð´Ñ
од е да Ñе изгÑади PageArr ÑÑез обÑ
ождане на веÑигаÑа /Kids Ð¾Ñ ÐºÐ¾Ñена на каÑалога, а не ÑÑез ÑканиÑане на непÑеки обекÑи. След каÑо и дваÑа пÑÑÑ Ð·Ð° анализ завÑÑÑÐ°Ñ Ð¿ÑÑвонаÑалноÑо Ñи пÑеминаване, ÑÑÑпка за поÑледваÑа обÑабоÑка вÑзÑÑановÑва логиÑеÑÐºÐ¸Ñ Ñед:
procedure THotPDF.ReorderPageArrByPagesTree;
var
PagesObj : THPDFDictionaryObject;
KidsArray : THPDFArrayObject;
NewPageArr: array of THPDFDictArrItem;
I, J, PageIndex, KidsIndex: Integer;
RefObj : THPDFLink;
PageObjNum: Integer;
Found : Boolean;
begin
{ Locate root /Pages dictionary via FRootIndex }
PagesObj := FindPagesRootFromCatalog;
if PagesObj = nil then Exit;
KidsIndex := PagesObj.FindValue('Kids');
if KidsIndex < 0 then Exit;
KidsArray := THPDFArrayObject(PagesObj.GetIndexedItem(KidsIndex));
SetLength(NewPageArr, KidsArray.Items.Count);
PageIndex := 0;
for I := 0 to KidsArray.Items.Count - 1 do
begin
RefObj := THPDFLink(KidsArray.GetIndexedItem(I));
PageObjNum := RefObj.Value.ObjectNumber;
Found := False;
for J := 0 to Length(PageArr) - 1 do
begin
if PageArr[J].PageLink.ObjectNumber = PageObjNum then
begin
NewPageArr[PageIndex] := PageArr[J];
Inc(PageIndex);
Found := True;
Break;
end;
end;
{ Non-page Kids (intermediate /Pages nodes) produce no match; skip }
end;
if PageIndex > 0 then
begin
SetLength(PageArr, PageIndex);
for I := 0 to PageIndex - 1 do
PageArr[I] := NewPageArr[I];
end;
end;
ÐзвикванеÑо Ñе поÑÑÐ°Ð²Ñ Ð² кÑÐ°Ñ Ð½Ð° вÑеки пÑÑ Ð·Ð° анализ, Ñлед каÑо вÑиÑки обекÑи Ñа каÑалогизиÑани, но пÑеди да бÑде обÑлÑжена какваÑо и да е опеÑаÑÐ¸Ñ Ð¿Ð¾ ÑÑÑаниÑиÑе:
{ Traditional path }
ListExtDictionary(THPDFDictionaryObject(IndirectObjects.Items[I]), FPageslink);
ReorderPageArrByPagesTree;
Break;
{ Modern path (object streams) }
if TryParseModernPDF then
begin
Result := ModernPageCount;
ReorderPageArrByPagesTree;
Exit;
end;
СÑÑпкаÑа на пÑенаÑеждане е O(n * m), кÑдеÑо n е бÑоÑÑ Ð½Ð° Kids, а m е ÑекÑÑаÑа дÑлжина на PageArr, но за вÑеки докÑÐ¼ÐµÐ½Ñ Ñ Ð¿Ð»Ð¾Ñко дÑÑво на ÑÑÑаниÑиÑе (вÑиÑки лиÑÑа на дÑлбоÑина 1, коеÑо покÑива огÑомноÑо мнозинÑÑво Ð¾Ñ ÑеалниÑе PDF Ñайлове) и двеÑе Ñа Ñ ÐµÐ´Ð½Ð°ÐºÐ²Ð° ÑÑойноÑÑ Ð¸ ÑенаÑа е пÑенебÑежима. ÐÑлбоко вложениÑе дÑÑвеÑа на ÑÑÑаниÑи изиÑÐºÐ²Ð°Ñ ÑекÑÑÑивно Ð¾Ð±Ñ Ð¾Ð¶Ð´Ð°Ð½Ðµ, а не едноеÑÐ°Ð¿Ð½Ð¸Ñ Ð¿Ð¾Ð´Ñ Ð¾Ð´, показан ÑÑк; пÑоизводÑÑвеноÑо внедÑÑване обÑабоÑва Ñози ÑлÑÑай оÑделно.
Ðзползване на CopyPageFromDocument Ñлед коÑекÑиÑÑа
С вÑвежданеÑо на ReorderPageArrByPagesTree, логиÑеÑкиÑе индекÑи на ÑÑÑаниÑиÑе ÑабоÑÑÑ ÑпоÑед оÑакваниÑÑа. Ðо-виÑокоÑо ниво CopyPageFromDocument пÑиема логиÑеÑки индекÑ, базиÑан на 0, и копиÑа пÑавилнаÑа ÑÑÑаниÑа в ÑÐµÐ»ÐµÐ²Ð¸Ñ Ð´Ð¾ÐºÑменÑ:
var
Source, Dest: THotPDF;
begin
Source := THotPDF.Create(nil);
Dest := THotPDF.Create(nil);
try
Source.LoadFromFile('source.pdf');
Dest.FileName := 'extracted.pdf';
Dest.BeginDoc;
{ Copy logical page 0 (first page the user sees) }
Dest.CopyPageFromDocument(Source, 0, 0);
Dest.EndDoc;
finally
Source.Free;
Dest.Free;
end;
end;
CopyPageFromDocument вÑÑÑеÑно пÑави заÑвка за Ñеда на дÑÑвоÑо на ÑÑÑаниÑиÑе, вмеÑÑо да ÑазÑиÑа на необÑабоÑÐµÐ½Ð¸Ñ Ð¸Ð½Ð´ÐµÐºÑ PageArr, Ñака Ñе Ñе дÑÑжи пÑавилно доÑи пÑи докÑменÑи, пÑи коиÑо ÑизиÑеÑкиÑÑ Ð¸ логиÑеÑкиÑÑ Ñед Ñе ÑазминаваÑ. Ðа пакеÑни опеÑаÑии, InsertPagesFromDocument пÑиема маÑив Ð¾Ñ Ð»Ð¾Ð³Ð¸ÑеÑки индекÑи и ги копиÑа Ñ ÐµÐ´Ð½Ð¾ пÑеминаване.
Ðакво ÑазкÑива Ñова за анализа на PDF
PDF ÑпеÑиÑикаÑиÑÑа е изÑиÑна: логиÑеÑкиÑÑ Ñед на ÑÑÑаниÑиÑе Ñе деÑиниÑа Ð¾Ñ Ð¼Ð°Ñива /Kids на дÑÑвоÑо на ÑÑÑаниÑиÑе, а не Ð¾Ñ Ð½Ð¾Ð¼ÐµÑаÑа на обекÑиÑе или байÑовиÑе оÑмеÑÑÐ²Ð°Ð½Ð¸Ñ (ISO 32000-1 §7.7.3.2). ÐÑеки паÑÑеÑ, койÑо използва ÑазлиÑно подÑеждане каÑо пÑÑк пÑÑ, Ñе даде пÑавилни ÑезÑлÑаÑи пÑи по-голÑмаÑа ÑаÑÑ Ð¾Ñ Ð´Ð¾ÐºÑменÑиÑе, заÑоÑо повеÑеÑо генеÑаÑоÑи пиÑÐ°Ñ ÑÑÑаниÑи в еÑÑеÑÑвен Ñед и пÑиÑвоÑÐ²Ð°Ñ Ð¿Ð¾ÑледоваÑелни номеÑа на обекÑиÑе. ÐÑгÑÑ Ñе кÑие, докаÑо нÑкой не заÑеди PDF, койÑо е бил инкÑеменÑално ÑедакÑиÑан, ÑеоÑганизиÑан Ð¾Ñ Ð´ÑÑг инÑÑÑÑÐ¼ÐµÐ½Ñ Ð¸Ð»Ð¸ генеÑиÑан Ð¾Ñ ÑоÑÑÑеÑ, койÑо е избÑал ÑазлиÑно оÑоÑмление.
ТеÑÑванеÑо Ñамо ÑÑеÑÑ ÑамогенеÑиÑани PDF Ñайлове пÑопÑÑка напÑлно Ñози ÐºÐ»Ð°Ñ Ð¿Ñоблеми. СледоваÑелно коÑекÑиÑÑа за ÑегÑеÑÐ¸Ñ Ð¿Ñи подÑежданеÑо на ÑÑÑаниÑи Ñе нÑждае Ð¾Ñ ÐºÐ¾ÑпÑÑ Ð¾Ñ Ð´Ð¾ÐºÑменÑи Ð¾Ñ ÑазлиÑни изÑоÑниÑи: инкÑеменÑални запиÑи, ÑканиÑани докÑменÑи Ñ Ð²Ð¼ÑкнаÑи заглавни ÑÑÑаниÑи, PDF Ñайлове, пÑоизведени Ð¾Ñ Ð¸Ð½ÑÑÑÑменÑи, коиÑо линеаÑизиÑÐ°Ñ Ð¸Ð»Ð¸ опÑимизиÑÐ°Ñ Ð³ÑаÑа на обекÑиÑе по ÑазлиÑен наÑин. ÐокÑменÑÑÑ, койÑо е задейÑÑвал пÑÑвонаÑÐ°Ð»Ð½Ð¸Ñ Ð±Ñг, ÑÑÑбва да оÑÑане в ÑеÑÑÐ¾Ð²Ð¸Ñ Ð¿Ð°ÐºÐµÑ Ð·Ð° ÑегÑеÑÐ¸Ñ Ð·Ð° поÑÑоÑнно.
СÑÑаниÑаÑа на HotPDF Component покÑива пÑÐ»Ð½Ð¸Ñ API за опеÑаÑии по ÑÑÑаниÑи, вклÑÑиÑелно CopyPageFromDocument, InsertPagesFromDocument и MovePage.