PDF Library for Delphi обединява два AcroForm документа с изрична политика за полета, споделящи име. MergeDocumentEx приема идентификатора на изходния документ и една от три стратегии: dfsReject отказва обединяването, dfsMerge запазва споделеното име и синхронизира стойностите, а dfsAutoNumber преименува входящите полета детерминирано. Сканирането на имена се случва преди каквито и да е номера на обекти да се изместят, така че отказано обединяване оставя и двата документа напълно използваеми
Всеки, който е сглобявал пакет от PDF заявления, се е сблъскал с това. Три формуляра, всеки с поле, наречено Signature, Date или Total, се обединяват в един файл. В AcroForm, пълното квалифицирано име на поле е идентичността на полето, така че две полета с едно и също име изобщо не са две полета: попълването на едното попълва другото, а подпис, приложен към едното, покрива обхват, който никой не е искал
Защо сблъсъкът на имена се решава преди обединяването?
По-старият MergeDocument конкатенира двата масива с коренни полета на AcroForm и не предлага избор. По-лошото е, че когато резултатът е неизползваем, откриването се случва след като номерата на обекти вече са преномерирани, а дърветата на страници — зашити, което оставя извикващия с документ в състояние, в което нито един от оригиналите не е бил
MergeDocumentEx обръща реда. Той събира имената на полета от най-високо ниво от двата документа, ги сравнява, и прилага стратегията, преди каквото и да е да се премести. Отхвърляне следователно е чиста без-операция: целевият документ е недокоснат, изходният документ е недокоснат, и двата остават отворени и използваеми, което тестът за обединяване проверява, като чете обратно стойност на поле от изходния документ след отказано обединяване
Сравнението използва подреден, чувствителен към регистъра набор от имена, така че цената е пропорционална на комбинирания брой полета, умножен по логаритмичен фактор, а не на произведението на двата броя. Чувствителността към регистъра е правилният избор тук, защото имената на PDF полета са чувствителни към регистъра; сгъването им би обединило полета, които спецификацията третира като различни
Трите стратегии и кога всяка е правилна
dfsReject е стратегията за автоматизирани конвейери, които не трябва да произвеждат двусмислени документи. Обединяването връща нула, а LastErrorCode отчита 705 — специален код, така че дублирани имена да могат да се различат от всеки друг провал при обединяване и да се насочат към конкретна поправка, обикновено преименуване на полета по-нагоре по веригата
dfsMerge запазва споделеното име съзнателно и синхронизира целевата стойност и стойността по подразбиране в изходното поле, така че съвместим визуализатор третира няколкото елемента за въвеждане (widgets) като едно логически именувано поле, което е стандартно поведение на AcroForm за поле с множество widget анотации. Това, което не прави, е да сгъне различни речници на полета в един-единствен обект. Всяко поле пази собствена асоциация със страница, външен вид и действия, защото сгъването им би тихо изхвърлило форматиране и поведение, принадлежащи на входящия документ
dfsAutoNumber преименува входящи дубликати чрез добавяне на числов суфикс, започващ от _2, и вземайки първия свободен. Резултатът е възпроизводим: зависи само от наличните имена, никога от номерата на обектите на полетата, така че обединяването на една и съща двойка документи два пъти дава едни и същи имена и двата пъти. Това свойство има значение, когато код надолу по веригата, FDF импорт или съпоставяне с база данни препраща към полета по име
uses
PDFlibrary;
var
Lib: TPDFlib;
TargetDoc, SourceDoc: Integer;
begin
Lib := TPDFlib.Create;
try
TargetDoc := Lib.SelectedDocument;
Lib.LoadFromFile('application-part1.pdf', '');
SourceDoc := Lib.NewDocument;
Lib.LoadFromFile('application-part2.pdf', '');
Lib.SelectDocument(TargetDoc);
if Lib.MergeDocumentEx(SourceDoc, dfsReject) = 0 then
begin
if Lib.LastErrorCode = 705 then
begin
// И двата документа все още са непокътнати - опитай отново с политика
Log('duplicate field names; retrying with auto-numbering');
Lib.MergeDocumentEx(SourceDoc, dfsAutoNumber);
end;
end;
Lib.SaveToFile('application-complete.pdf');
finally
Lib.Free;
end;
end;
Обърнете внимание на двустъпковия модел в този код, който е възможен само защото отхвърлянето е неразрушително. Опитайте първо строгата политика, инспектирайте грешката, след което решете. При обединяване, което се проваля по средата, резервният вариант би трябвало да започне отначало чрез презареждане на двата файла
Как изглежда обединеният формуляр впоследствие
Под dfsMerge, целево поле, наречено Shared, носещо „Target value“, и изходно поле със същото име произвеждат две полета, и двете наречени Shared, и двете отчитащи целевата стойност, защото целевата стойност и стойността по подразбиране се синхронизират във входящото поле. Това е предвидената семантика за споделено име: едно логическо поле, няколко елемента за въвеждане, една стойност
Под dfsAutoNumber, същият вход произвежда Shared и Shared_2 като отделни полета с независими стойности. Изберете между двете, задавайки един-единствен въпрос: трябва ли попълването на един контрол да попълни другия? За име на подписващ, повтарящо се на всяка част от пакет, да, и dfsMerge е правилно. За обща сума, която означава нещо различно на всеки формуляр, не, и автоматичното номериране е правилно
// След обединяване, изброй какво всъщност си получил
for I := 1 to Lib.FormFieldCount do
Log(Format('%d: %s = %s',
[I, Lib.GetFormFieldTitle(I), Lib.GetFormFieldValue(I)]));
Практически бележки за сглобяване на пакети от формуляри
Успешното обединяване консумира изходния документ: той се премахва от списъка с документи на библиотеката, поради което DocumentCount пада от два на един. Не продължавайте да използвате идентификатора на източника след това. Версията на документа се повишава до по-високата от двете, така че обединяването на PDF 2.0 формуляр в документ 1.7 дава файл 2.0
Редът има значение за имената. Обединяването на A в B и обединяването на B в A произвеждат различни автоматично номерирани резултати, тъй като документът, извършващ обединяването, пази имената си непроменени. Когато пакет има каноничен основен формуляр, направете точно него целта
Полетата за подпис заслужават собствено разглеждане. Подпис, приложен преди обединяване, покрива само редакцията, която е подписал, така че обединяването го обезсилва в практическия смисъл, че файлът се е променил след подписването. Сглобете първо и подпишете сглобения документ, вместо да обединявате подписани части. Когато обединяването е за съдържание на страници, а не за формуляри, по-бързият път, описан в бързо обединяване на PDF с изместване на байтови препратки, е по-добрият инструмент
Накрая, планирайте страната с данните на пакета заедно с обединяването. Ако стойностите на полета пристигат от външна система, решете дали тази система адресира полета по име, преди да изберете автоматично номериране, защото Shared_2 няма да съвпадне със съпоставяне, което очаква Shared. Форматите за импорт и експорт са разгледани в обмен на данни за формуляри FDF, XFDF и XFA, а поведението на скриптовете на ниво поле, което също може да бъде засегнато от преименуване, е разгледано в интерактивни действия на формуляри и JavaScript
Обединяването на формуляри, обменът на данни и подписването работят в една и съща библиотека за Delphi, C++Builder и Free Pascal; пълният списък с функции е на страницата на PDF Library for Delphi