PDF Library for Delphi ผสานเอกสาร AcroForm สองฉบับด้วยนโยบายที่ชัดเจนสำหรับฟิลด์ที่มีชื่อร่วมกัน MergeDocumentEx รับตัวระบุเอกสารต้นทางและกลยุทธ์หนึ่งในสามแบบ คือ dfsReject ปฏิเสธการผสาน dfsMerge คงชื่อร่วมกันไว้และซิงค์ค่าให้ตรงกัน และ dfsAutoNumber เปลี่ยนชื่อฟิลด์ที่นำเข้ามาอย่างกำหนดผลลัพธ์ได้แน่นอน การสแกนชื่อเกิดขึ้นก่อนที่หมายเลขอ็อบเจกต์ใดๆ จะเปลี่ยนแปลง ดังนั้นการผสานที่ถูกปฏิเสธจะทิ้งเอกสารทั้งสองไว้ใช้งานได้เต็มที่
ใครก็ตามที่เคยประกอบชุดใบสมัคร PDF เคยเจอปัญหานี้มาแล้ว ฟอร์มสามฟอร์ม แต่ละฟอร์มมีฟิลด์ชื่อ Signature หรือ Date หรือ Total ถูกผสานเข้าเป็นไฟล์เดียว ใน AcroForm ชื่อฟิลด์แบบเต็มคือเอกลักษณ์ของฟิลด์นั้น ดังนั้นฟิลด์สองตัวที่มีชื่อเดียวกันจึงไม่ใช่สองฟิลด์เลย การกรอกตัวหนึ่งจะกรอกอีกตัวไปด้วย และลายเซ็นที่ใส่ในตัวหนึ่งจะครอบคลุมขอบเขตที่ไม่มีใครตั้งใจ
เหตุใดการชนกันของชื่อจึงถูกตัดสินก่อนการผสาน
MergeDocument รุ่นเก่าต่อท้ายอาร์เรย์ฟิลด์รากของ AcroForm ทั้งสองเข้าด้วยกันโดยไม่มีทางเลือกใดๆ เลว ร้ายกว่านั้นคือ เมื่อผลลัพธ์ใช้งานไม่ได้ การค้นพบจะเกิดขึ้นหลังจากหมายเลขอ็อบเจกต์ถูกกำหนดใหม่และโครงสร้างต้นไม้หน้าถูกเย็บติดกันแล้ว ซึ่งทิ้งให้ผู้เรียกถือเอกสารที่อยู่ในสถานะที่ไม่มีต้นฉบับใดเคยอยู่มาก่อน
MergeDocumentEx สลับลำดับนี้ มันรวบรวมชื่อฟิลด์ระดับบนสุดจากเอกสารทั้งสองฉบับ เปรียบเทียบกัน และใช้กลยุทธ์ก่อนที่สิ่งใดจะเคลื่อนไหว การปฏิเสธจึงเป็นการไม่ทำอะไรเลยแบบสะอาด เอกสารเป้าหมายไม่ถูกแตะต้อง เอกสารต้นทางไม่ถูกแตะต้อง และทั้งสองยังคงเปิดและใช้งานได้ ซึ่งการทดสอบการผสานยืนยันสิ่งนี้ด้วยการอ่านค่าฟิลด์กลับออกมาจากต้นทางหลังจากการผสานที่ถูกปฏิเสธ
การเปรียบเทียบใช้เซตชื่อแบบเรียงลำดับที่คำนึงถึงตัวพิมพ์เล็กใหญ่ ดังนั้นต้นทุนจึงแปรผันตามจำนวนฟิลด์รวมกันคูณด้วยตัวประกอบลอการิทึม แทนที่จะแปรผันตามผลคูณของจำนวนทั้งสองชุด การคำนึงถึงตัวพิมพ์เล็กใหญ่คือทางเลือกที่ถูกต้องในที่นี้ เพราะชื่อฟิลด์ PDF คำนึงถึงตัวพิมพ์เล็กใหญ่ การพับให้เท่ากันจะผสานฟิลด์ที่สเปกถือว่าแตกต่างกันเข้าด้วยกัน
กลยุทธ์ทั้งสาม และเมื่อใดที่แต่ละอย่างถูกต้อง
dfsReject เป็นกลยุทธ์สำหรับไปป์ไลน์อัตโนมัติที่ต้องไม่สร้างเอกสารที่กำกวม การผสานจะคืนค่าศูนย์ และ LastErrorCode รายงาน 705 ซึ่งเป็นรหัสเฉพาะเพื่อให้ชื่อที่ซ้ำกันสามารถแยกแยะได้จากความล้มเหลวในการผสานแบบอื่นทั้งหมด และส่งต่อไปยังวิธีแก้ไขเฉพาะ ปกติแล้วคือการเปลี่ยนชื่อฟิลด์ตั้งแต่ต้นทาง
dfsMerge คงชื่อร่วมกันไว้อย่างจงใจ และซิงค์ค่าเป้าหมายกับค่าเริ่มต้นเข้าไปในฟิลด์ต้นทาง ดังนั้นตัวแสดงผลที่สอดคล้องตามมาตรฐานจะปฏิบัติต่อวิดเจ็ตหลายตัวเหมือนเป็นฟิลด์เชิงตรรกะเดียวที่มีชื่อเดียวกัน ซึ่งเป็นพฤติกรรม AcroForm มาตรฐานสำหรับฟิลด์ที่มีคำอธิบายประกอบวิดเจ็ตหลายตัว สิ่งที่มันไม่ทำคือการพับดิกชันนารีฟิลด์ที่ต่างกันเข้าเป็นอ็อบเจกต์เดียว แต่ละฟิลด์คงการเชื่อมโยงหน้า รูปลักษณ์ และการกระทำของตัวเองไว้ เพราะการยุบรวมมันจะทิ้งรูปแบบและพฤติกรรมที่เป็นของเอกสารที่นำเข้ามาไปอย่างเงียบๆ
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
// Both documents are still intact - retry with a policy
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 ถูกต้อง สำหรับยอดรวมที่มีความหมายต่างกันในแต่ละฟอร์ม คำตอบคือไม่ และการเปลี่ยนชื่ออัตโนมัติถูกต้อง
// After a merge, enumerate what you actually got
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