Ðве минÑÑи за копиÑане на ÑÑи ÑÑÑаниÑи Ð¾Ñ PDF Ñайл Ñ Ð¾Ð±Ñо 40 ÑÑÑаниÑи не е пÑоблем за опÑимизиÑане на пÑоизводиÑелноÑÑÑа. Това е Ñигнал, Ñе Ñе използва гÑеÑен пÑÑ Ð² API. ÐогаÑо за пÑÑви пÑÑ Ð²Ð¸Ð´ÑÑ Ñова вÑеме в пÑимеÑен код за копиÑане на ÑÑÑаниÑи Ñ HotPDF компоненÑа, инÑÑинкÑÑÑ Ð¼Ð¸ беÑе да погледна пÑÑво ÑÑÑÑкÑÑÑаÑа на докÑменÑа и Ñлед Ñова кода. Ðказа Ñе, Ñе Ñози Ñед е Ð¾Ñ Ð·Ð½Ð°Ñение.
Ðакво вÑÑÑноÑÑ Ð·Ð°Ð±Ð°Ð²ÑÑе пÑоÑеÑа
ÐÑпÑоÑниÑÑ PDF беÑе ÑеÑеÑенÑен докÑÐ¼ÐµÐ½Ñ Ð¾Ñ 40 ÑÑÑаниÑи Ñ Ð½ÐµÑÑивиално дÑÑво на ÑÑÑаниÑиÑе: множеÑÑво междинни /Pages вÑзли вмеÑÑо един плоÑÑк маÑив. ÐÑигиналниÑÑ Ð¿ÑимеÑен код извикваÑе LoadFromFile, Ñлед коеÑо изгÑаждаÑе нов докÑÐ¼ÐµÐ½Ñ Ñ BeginDoc, вÑÑÑеÑе Ñе в ÑикÑл по избÑаниÑе номеÑа на ÑÑÑаниÑи и пÑи вÑÑка иÑеÑаÑÐ¸Ñ Ð·Ð°ÑеждаÑе изÑ
Ð¾Ð´Ð½Ð¸Ñ Ð´Ð¾ÐºÑÐ¼ÐµÐ½Ñ Ð¾Ñново Ð¾Ñ Ð´Ð¸Ñка, за да вземе ÑÑоÑвеÑнаÑа ÑÑÑаниÑа. Това ознаÑава, Ñе ÑазÑ
одÑÑ Ð·Ð° пÑлен анализ Ñе Ñмножава по бÑÐ¾Ñ Ð½Ð° ÑÑÑаниÑиÑе, коиÑо иÑкаÑе да извлеÑеÑе. Файл Ñ ÑÐ°Ð·Ð¼ÐµÑ 12 MB ÑеÑеÑе диÑка ÑеÑÑ Ð¿ÑÑи за извлиÑане на ÑÑи ÑÑÑаниÑи, заÑоÑо никой не беÑе помиÑлил дали ÑайлÑÑ ÑÑÑбва да оÑÑане оÑвоÑен Ð¼ÐµÐ¶Ð´Ñ Ð¸ÑеÑаÑииÑе.
ÐÑоÑиÑÑ ÑакÑÐ¾Ñ Ð±ÐµÑе невидим в кода: меÑодÑÑ LoadFromFile на HotPDF обÑабоÑва ÑÑлаÑа ÑаблиÑа Ñ Ð¿ÑепÑаÑки (cross-reference table) и декомпÑеÑиÑа вÑеки поÑок Ð¾Ñ Ð¾Ð±ÐµÐºÑи пÑи заÑеждане. Това е пÑавилноÑо поведение за докÑменÑ, койÑо пÑедÑÑои да модиÑиÑиÑаÑе, но е излиÑна ÑабоÑа, ако иÑкаÑе Ñамо бÑÐ¾Ñ Ð½Ð° ÑÑÑаниÑиÑе или подмножеÑÑво Ð¾Ñ ÑÑÑаниÑи. Ðа доÑÑÑп Ñамо за ÑеÑене до ÑÑÑÑкÑÑÑаÑа, DAOpenFileReadOnly избÑгва деÑеÑиализаÑиÑÑа на пÑлноÑо дÑÑво Ð¾Ñ Ð¾Ð±ÐµÐºÑи, коеÑо е важно пÑи компÑеÑиÑани Ñайлове Ñ Ð³Ð¾Ð»ÐµÐ¼Ð¸ ÑеÑÑÑÑи Ð¾Ñ Ð¸Ð·Ð¾Ð±ÑажениÑ.
ÐиÑо едно Ð¾Ñ Ñези неÑа не е бÑг в библиоÑекаÑа. Рв дваÑа ÑлÑÑÐ°Ñ Ð¸Ð·Ð²Ð¸ÐºÐ²Ð°ÑиÑÑ ÐºÐ¾Ð´ избиÑа API, пÑедназнаÑен за една задаÑа, и го използва за дÑÑга.
Ðзползване на InsertPagesFromDocument за извлиÑане на ÑÑÑаниÑи
ÐÑавилниÑÑ Ð¿ÑÑ Ð·Ð° копиÑане на диапазон Ð¾Ñ ÑÑÑаниÑи Ð¾Ñ ÐµÐ´Ð¸Ð½ HotPDF докÑÐ¼ÐµÐ½Ñ Ð² дÑÑг е InsertPagesFromDocument, извикан Ñлед LoadFromFile на изÑоÑника. ÐаÑеждаÑе изÑоÑника веднÑж, заÑеждаÑе или ÑÑздаваÑе ÑÐµÐ»ÐµÐ²Ð¸Ñ Ð´Ð¾ÐºÑÐ¼ÐµÐ½Ñ Ð²ÐµÐ´Ð½Ñж, пÑемеÑÑваÑе ÑÑÑаниÑиÑе и запиÑваÑе. ÐзÑоÑникÑÑ Ð¾ÑÑава в памеÑÑа по вÑеме на вÑиÑки вмÑÐºÐ²Ð°Ð½Ð¸Ñ Ð½Ð° ÑÑÑаниÑи:
procedure ExtractPages(const SourceFile, DestFile: string;
const PageRange: string);
var
Source, Dest: THotPDF;
begin
Source := THotPDF.Create(nil);
Dest := THotPDF.Create(nil);
try
// Load source once: full parse happens here and only here
Source.LoadFromFile(SourceFile);
// Build a minimal destination document
Dest.FileName := DestFile;
Dest.BeginDoc;
// Copy the requested range; '1-3' inserts pages 1 through 3
// starting at position 1 in the destination
Dest.InsertPagesFromDocument(Source, PageRange, 1);
Dest.EndDoc;
finally
Source.Free;
Dest.Free;
end;
end;
ÐаÑамеÑÑÑÑÑ PageRange пÑиема ÑÑÑÐ¸Ñ ÑоÑÐ¼Ð°Ñ ÐºÐ°Ñо пÑимеÑа за команден Ñед: ÑпиÑÑк Ñ ÑазделиÑел запеÑÐ°Ñ Ð¾Ñ Ð½Ð¾Ð¼ÐµÑа на ÑÑÑаниÑи или диапазони, каÑо '1-3' или '1,5,7-9'. ÐндекÑиÑанеÑо на ÑÑÑаниÑиÑе запоÑва Ð¾Ñ 1. InsertPagesFromDocument копиÑа поÑоÑиÑе Ð¾Ñ ÑÑдÑÑжание, ÑеÑниÑиÑе на ÑеÑÑÑÑиÑе и геомеÑÑиÑÑа на ÑÑÑаниÑиÑе, без да докоÑва меÑаданни, оÑмеÑки или пÑикаÑени Ñайлове, оÑвен ако Ñе не Ñа ÑеÑеÑиÑани Ð¾Ñ ÐºÐ¾Ð¿Ð¸ÑаниÑе ÑÑÑаниÑи. Ðа извлиÑане на ÑÑи ÑÑÑаниÑи Ð¾Ñ Ð´Ð¾ÐºÑÐ¼ÐµÐ½Ñ Ñ 40 ÑÑÑаниÑи, Ñова е малÑк ÑабоÑен Ð½Ð°Ð±Ð¾Ñ Ð¾Ñ Ð´Ð°Ð½Ð½Ð¸.
ÐÑемеÑо за изпÑлнение на ÑÑÑÐ¸Ñ 12 MB Ñайл, коеÑо пÑеди оÑнемаÑе две минÑÑи: под 1.5 ÑекÑнди Ñ Ñози модел. Ðо-голÑмаÑа ÑаÑÑ Ð¾Ñ Ñова вÑеме оÑива за еднокÑаÑноÑо извикване на LoadFromFile. СÑÑÑкÑÑÑаÑа на докÑменÑа пÑеÑÑава да бÑде ÑакÑоÑ, Ñлед каÑо ÑаблиÑаÑа Ñ Ð¾Ð±ÐµÐºÑи бÑде обÑабоÑена пÑÑÐ²Ð¸Ñ Ð¿ÑÑ.
ÐогаÑо LoadFromFile е ÑвÑÑде ÑежÑк: Direct File API
Ðко ÑÑÑбва Ñамо да пÑебÑоиÑе ÑÑÑаниÑиÑе, да пÑовеÑиÑе инÑоÑмаÑиÑÑа за докÑменÑа или да копиÑаÑе Ñайл, без да докоÑваÑе ÑÑдÑÑжаниеÑо мÑ, Direct File API избÑгва напÑлно пÑÐ»Ð½Ð¸Ñ Ð°Ð½Ð°Ð»Ð¸Ð·. DAOpenFileReadOnly каÑÑогÑаÑиÑа ÑаблиÑаÑа Ñ Ð¿ÑепÑаÑки, без да декомпÑеÑиÑа поÑоÑиÑе Ð¾Ñ Ð¾Ð±ÐµÐºÑи, Ñака Ñе бÑоÑÑ Ð½Ð° ÑÑÑаниÑиÑе Ñе изÑиÑлÑва за вÑеме O(ÑÐ°Ð·Ð¼ÐµÑ Ð½Ð° xref) вмеÑÑо O(ÑÐ°Ð·Ð¼ÐµÑ Ð½Ð° Ñайла):
procedure InspectPDF(const FileName: string);
var
Pdf: THotPDF;
Handle, PageCount: Integer;
begin
Pdf := THotPDF.Create(nil);
try
Handle := Pdf.DAOpenFileReadOnly(FileName, '');
if Handle <= 0 then
Exit;
try
PageCount := Pdf.DAGetPageCount(Handle);
Writeln('Pages: ', PageCount);
// DACopyFile is a byte-preserving copy, no re-serialization
Pdf.DACopyFile(FileName, 'archive-copy.pdf');
finally
Pdf.DACloseFile(Handle);
end;
finally
Pdf.Free;
end;
end;
ÐÑедÑпÑеждение: DAOpenFileReadOnly пÑиема паÑамеÑÑÑ Ð·Ð° паÑола, но пÑеминава кÑм пÑлен анализ пÑи ÑиÑÑовани Ñайлове, ÑÑй каÑо деÑиÑÑиÑанеÑо изиÑква дÑÑвоÑо Ð¾Ñ Ð¾Ð±ÐµÐºÑи за извлиÑане на ÑеÑника за ÑиÑÑоване. Ðко изÑ
одниÑе ви Ñайлове Ñа ÑиÑÑовани, пÑÑво ги деÑиÑÑиÑайÑе Ñ DecryptFile, за да полÑÑиÑе неÑиÑÑовано копие, и Ñлед Ñова го оÑвоÑеÑе Ñ Direct File API. ФÑнкÑиÑÑа DecryptFile на ниво Ñайл използва диÑекÑен пÑÑ Ð·Ð° пÑезапиÑване Ñ AES-256 за ÑÑандаÑÑно ÑиÑÑоване и е по-бÑÑза Ð¾Ñ LoadFromFile, поÑледван Ð¾Ñ SaveLoadedDocument за големи Ñайлове, ÑÑй каÑо не изгÑажда пÑÐ»Ð½Ð¸Ñ Ð¼Ð¾Ð´ÐµÐ» на обекÑи в памеÑÑа.
ÐÐ°Ð¼ÐµÑ Ð¿Ð¾ вÑеме на обÑабоÑка на големи паÑÑиди
ÐакеÑниÑе задаÑи, коиÑо обÑабоÑÐ²Ð°Ñ Ð´ÐµÑеÑки Ñайлове в ÑикÑл, ÑеÑÑо Ð¸Ð¼Ð°Ñ Ð¼Ð¾Ð´ÐµÐ», койÑо изглежда пÑавилен, но наÑÑÑпва памеÑ: ÑÑздаване на THotPDF вÑÑÑе в ÑикÑла, извикване на LoadFromFile, извÑÑÑване на ÑабоÑаÑа и извикване на Free. Това е ÑÑÑÑкÑÑÑно пÑавилно. ÐÑоблемÑÑ Ð²Ñзниква, когаÑо вÑÑÑеÑнаÑа ÑабоÑа ÑазпÑÐµÐ´ÐµÐ»Ñ Ð²Ñеменни обекÑи, ÑÐ»Ð°Ð²Ñ Ð¸Ð·ÐºÐ»ÑÑÐµÐ½Ð¸Ñ Ð¸ оÑÑÐ°Ð²Ñ Ñези вÑеменни обекÑи акÑивни пÑи вÑзникване на гÑеÑки. ÐениджÑÑÑÑ Ð½Ð° памеÑÑа на Delphi не извÑÑÑва авÑомаÑиÑно ÑплÑÑнÑване, Ñака Ñе ÑÑоÑина ÑеÑа по пÑÑÑ Ð½Ð° гÑеÑкиÑе по вÑеме на пакеÑно изпÑлнение Ð¼Ð¾Ð³Ð°Ñ Ð´Ð° повиÑÐ°Ñ Ð¿Ð°Ð¼ÐµÑÑа доÑÑаÑÑÑно, за да забавÑÑ ÑазпÑеделÑнеÑо на Ð¿Ð°Ð¼ÐµÑ Ð·Ð° вÑиÑко оÑÑанало.
РеÑениеÑо не е Ñложно. ÐÑеки THotPDF и вÑеки междинен TStream или TBitmap, койÑо ÑÑаÑÑва в ÑабоÑаÑа Ñ PDF, ÑÑÑбва да бÑде в блок try/finally, кÑдеÑо Free е поÑледниÑÑ Ð¾Ð¿ÐµÑаÑоÑ. ÐадайÑе локалниÑе ÑказаÑели на nil пÑеди блока try, Ñака Ñе клонÑÑ finally да може безопаÑно да използва if Assigned(x) then x.Free, ако иниÑиализаÑиÑÑа Ñе пÑовали по ÑÑедаÑа. Това е ÑÑандаÑÑнаÑа диÑÑиплина за ÑпÑавление на ÑобÑÑвеноÑÑÑа в Delphi и напÑлно ÑеÑава Ñози ÐºÐ»Ð°Ñ Ð¿Ñоблеми.
ÐÑе едно неÑо, коеÑо ÑÑÑбва да пÑовеÑиÑе в пакеÑни конÑекÑÑи: AddImage ÑегиÑÑÑиÑа изобÑÐ°Ð¶ÐµÐ½Ð¸Ñ Ð² допÑлниÑелен вÑÑÑеÑен ÑпиÑÑк, койÑо ÑÑÑеÑÑвÑва пÑез ÑÐµÐ»Ð¸Ñ Ð¶Ð¸Ð²Ð¾Ñ Ð½Ð° инÑÑанÑиÑÑа на THotPDF. Ðко използваÑе повÑоÑно една и ÑÑÑа инÑÑанÑÐ¸Ñ Ð·Ð° множеÑÑво докÑменÑи ÑÑез поÑледоваÑелно извикване на LoadFromFile, ÑегиÑÑÑаÑииÑе на изобÑÐ°Ð¶ÐµÐ½Ð¸Ñ Ð¾Ñ Ð¿ÑедиÑниÑе докÑменÑи оÑÑÐ°Ð²Ð°Ñ Ð² ÑпиÑÑка. Ðли ÑÑздавайÑе нова инÑÑанÑÐ¸Ñ Ð·Ð° вÑеки докÑменÑ, или изÑиÑÑвайÑе ÑпиÑÑка Ñ Ð¸Ð·Ð¾Ð±ÑÐ°Ð¶ÐµÐ½Ð¸Ñ Ð¼ÐµÐ¶Ð´Ñ Ð´Ð¾ÐºÑменÑиÑе.
ÐзмеÑване пÑеди вÑÑка пÑомÑна
ÐÑеди да пÑиложиÑе нÑкой Ð¾Ñ Ñези модели, напÑавеÑе измеÑване. ÐлаÑÑÑ TStopwatch на Delphi Ð¾Ñ System.Diagnostics обвива QueryPerformanceCounter и е доÑÑаÑÑÑно ÑоÑен за пÑоÑилиÑане на ÑÐ°Ð¹Ð»Ð¾Ð²Ð¸Ñ Ð²Ñ
одно-изÑ
оден пÑоÑÐµÑ (I/O). ÐзмеÑеÑе Ñамо LoadFromFile и вижÑе какÑв пÑоÑÐµÐ½Ñ Ð¾Ñ Ð²ÑемеÑо заема. Ðко е 90% Ð¾Ñ Ð¾Ð±ÑоÑо вÑеме, ÑеÑениеÑо е Direct File API или намалÑване на бÑÐ¾Ñ Ð°Ð½Ð°Ð»Ð¸Ð·Ð¸ на един и ÑÑÑ Ñайл. Ðко е под 20%, ÑÑÑноÑо мÑÑÑо е дÑÑгаде и Ñе ÑокÑÑиÑаÑе вÑÑÑ
Ñ Ð³ÑеÑноÑо неÑо.
ÐвÑминÑÑноÑо извлиÑане, Ñ ÐºÐ¾ÐµÑо запоÑна Ñази ÑÑаÑиÑ, Ñе оказа изÑÑло пÑиÑинено Ð¾Ñ Ð¼Ð¾Ð´ÐµÐ»Ð° на многокÑаÑно заÑеждане. СÑÑÑкÑÑÑаÑа на докÑменÑа не допÑинаÑÑÑе Ñ Ð½Ð¸Ñо â?едно плоÑко дÑÑво на ÑÑÑаниÑиÑе би ÑабоÑило по ÑÑÑÐ¸Ñ Ð½Ð°Ñин. ÐÑеминаванеÑо кÑм еднокÑаÑно извикване на LoadFromFile, поÑледвано Ð¾Ñ ÐµÐ´Ð½Ð¾ извикване на InsertPagesFromDocument, намали вÑемеÑо до 1.3 ÑекÑнди на ÑÑÑÐ¸Ñ Ñ
аÑдÑеÑ, без да Ñе пÑÐ¾Ð¼ÐµÐ½Ñ Ð½Ð¸Ñо дÑÑго.
API за манипÑлиÑане на ÑÑÑаниÑи, показано ÑÑк, е ÑаÑÑ Ð¾Ñ HotPDF компоненÑа за Delphi и C++Builder.