เปิดเอกสารสองไฟล์ขึ้นมาพร้อมกัน ในหมายเลขหน้าเดียวกัน โดยแต่ละไฟล์อยู่ในพาเนลที่สามารถเลื่อนหน้าจอของตัวเองได้: นั่นคือแกนหลักของโปรแกรมเปรียบเทียบข้อมูล PDFium Component มอบสิ่งนี้ผ่านรูปแบบอ็อบเจ็กต์ที่ตรงไปตรงมา โดยที่ TPdf เป็นเจ้าของไฟล์ และ TPdfView เป็นเจ้าของจอแสดงผล หนึ่งเอกสาร มี TPdf หนึ่งตัว และมี TPdfView หนึ่งตัว หากคุณต้องการสามพาเนล คุณก็จะต้องมีสามคู่ ส่วนที่ยากไม่ใช่การเรียกใช้งาน API; แต่เป็นการคำนวณคณิตศาสตร์สำหรับเลย์เอาต์เมื่อมีการปรับขนาดหน้าต่างและระบบตรรกะสำหรับซิงค์หน้าเมื่อคุณตัดสินใจว่ามุมมองใดควรเป็นตัวกำหนดให้มุมมองใดเลื่อนตาม
เลย์เอาต์ฟอร์ม
VCL ฟอร์มจะรองรับ TScrollBox คอนเทนเนอร์ (containers) สามตัวเคียงข้างกัน โดยแต่ละตัวมี TPdfView อยู่ภายใน และจัดวางแบบ alClient เพื่อให้มันเติมเต็มกล่องพอดี คอมโพเนนต์ TSplitter สองตัววางอยู่ระหว่างกล่องเพื่อให้ผู้ใช้สามารถปรับความกว้างของคอลัมน์ได้ในขณะรันโปรแกรม ทูลบาร์ที่อยู่ด้านบนพาเนลจะมีปุ่มเปิด การควบคุมการซูม และปุ่มสลับการทำงานระหว่างมุมมองแบบสองจอและสามจอ
โหมดสามจอคือเงื่อนไขแบบบูลีนที่ฟอร์มใช้ติดตามอยู่ภายใน เมื่อมีการสลับโหมด คุณจะคำนวณความกว้างใหม่และแสดงหรือซ่อนคอลัมน์ที่สาม แนวทางที่ง่ายที่สุดคือการล้างค่าคุณสมบัติ Align ทั้งหมด ซ่อนตัวแยกหน้าจอ (splitters) จากนั้นกำหนดตำแหน่งสัมบูรณ์:
procedure TFormMain.UpdateLayout;
var
TotalWidth: Integer;
begin
TotalWidth := ClientWidth;
if ThreeViewMode then
begin
ScrollBox3.Visible := True;
ScrollBox1.Left := 0;
ScrollBox1.Width := TotalWidth div 3;
ScrollBox2.Left := ScrollBox1.Width;
ScrollBox2.Width := TotalWidth div 3;
ScrollBox3.Left := ScrollBox2.Left + ScrollBox2.Width;
ScrollBox3.Width := TotalWidth - ScrollBox3.Left;
// Apply the same (ClientHeight - toolbar height) to all three Height values
end
else
begin
ScrollBox3.Visible := False;
ScrollBox1.Left := 0;
ScrollBox1.Width := TotalWidth div 2;
ScrollBox2.Left := ScrollBox1.Width;
ScrollBox2.Width := TotalWidth - ScrollBox2.Left;
end;
end;
การตั้งค่า Align := alNone ในกล่องทั้งสามใบก่อนการคำนวณตัวเลขทางคณิตศาสตร์จะช่วยหลีกเลี่ยงไม่ให้กลไกข้อจำกัดของ VCL มาต่อต้านการกำหนดค่าของคุณ คืนค่าการมองเห็นของตัวแยกหน้าจอกลับมาหลังจากการจัดวางตำแหน่งเสร็จสิ้นแล้ว หากคุณต้องการลากเพื่อปรับขนาดในโหมดสองจอ
ความสูงของแต่ละกล่องเลื่อน (scroll box) คือพื้นที่ไคลเอนต์ลบความสูงของพาเนลทูลบาร์ เนื่องจากทูลบาร์จะถูกจับคู่เทียบท่า (docked) อยู่ที่ด้านบนด้วย alTop ดังนั้นคำสั่ง ClientHeight - PanelButtons.Height จะให้พื้นที่แนวตั้งที่ใช้งานได้ มอบหมายค่านี้ให้กับกล่องทั้งสามในขณะที่มีการเรียก UpdateLayout เดียวกัน เพื่อที่จะไม่เกิดเฟรมที่กล่องใบใดใบหนึ่งสูงกว่ากล่องใบอื่นจนก่อให้เกิดการกะพริบของเลย์เอาต์
การเปิดเอกสาร
แต่ละคู่พาเนลต้องมีกระบวนการ (procedure) เปิดของตัวเอง รูปแบบนั้นสั้นมาก: ปิดการใช้งานคอมโพเนนต์ กำหนดชื่อไฟล์ เปิดการใช้งานอีกครั้ง จากนั้นตรวจสอบค่า Active; หากยังคงเป็น False ให้เรียกขอรหัสผ่านและลองใหม่อีกครั้ง พึงสังเกตว่า TPdfView.Active เป็นตัวควบคุมการเรนเดอร์ แต่ TPdf.Active คือตัวที่เปิดไฟล์ขึ้นมาจริงๆ; พวกมันต่างทำงานเป็นอิสระต่อกัน การตั้งค่า PdfView.Active := True ในขณะที่ TPdf ที่เชื่อมต่ออยู่ยังไม่ได้ทำงานนั้นไม่มีอันตรายใดๆ แต่มันจะไม่แสดงผลใดๆ เช่นกัน
procedure TFormMain.OpenPdfFile(PdfComponent: TPdf;
PdfViewComponent: TPdfView);
var
Password: string;
begin
if not OpenDialog.Execute then
Exit;
PdfComponent.Active := False;
PdfComponent.FileName := OpenDialog.FileName;
PdfComponent.Password := '';
PdfComponent.Active := True;
// Load failures are silent: Active stays False instead of raising.
if not PdfComponent.Active then
begin
// Most likely a password-protected file; give the user one retry.
if InputQuery('Password', 'Enter document password:', Password) then
begin
PdfComponent.Password := Password;
PdfComponent.Active := True;
end;
end;
if not PdfComponent.Active then
begin
ShowMessage('Could not open ' + OpenDialog.FileName +
' (damaged file or wrong password)');
Exit;
end;
PdfViewComponent.PageNumber := 1;
SetActivePdfView(PdfViewComponent);
end;
ตรวจสอบค่า PdfComponent.Active เสมอหลังจากการกำหนดค่า; ไฟล์ที่เสียหายหรือรหัสผ่านที่ไม่ถูกต้องจะทำให้การโหลดล้มเหลวอย่างเงียบๆ โดยไม่แสดงข้อผิดพลาดในเส้นทางเริ่มต้น การตั้งค่า PdfViewComponent.PageNumber := 1 อย่างชัดเจนหลังจากการเปิดที่ประสบความสำเร็จจะช่วยหลีกเลี่ยงหมายเลขหน้าที่ตกค้างมาจากเอกสารก่อนหน้า
กล่องข้อความโต้ตอบที่ส่วนท้ายเป็นความตั้งใจที่ทำขึ้น: คุณต้องการให้ไฟล์ที่เสียหายหรือรูปแบบที่ไม่รองรับโผล่ขึ้นมาทันที แทนที่จะถูกกลืนหายไปแล้วแสดงเป็นเพียงแค่แผงหน้าจอว่างๆ ที่เงียบกริบ ผู้ใช้ที่ไม่เห็นอะไรเลยจะไม่รู้ว่าไฟล์ถูกโหลดขึ้นมาแล้วแต่เป็นแค่หน้ากระดาษเปล่า หรือคอมโพเนนต์ได้ปฏิเสธมันไป การรายงานความล้มเหลวจะช่วยให้ข้อผิดพลาดนั้นมองเห็นได้ตลอด
การติดตามพาเนลที่ทำงาน
เมื่อผู้ใช้คลิกภายในพาเนล พาเนลนั้นจะกลายเป็นพาเนลที่ทำงาน ฟอร์มจะติดตามฟิลด์ FActivePdfView: TPdfView แบบส่วนตัว การตอบรับทางสายตาคือการเปลี่ยนสีขอบของ TScrollBox ที่บรรจุอยู่: ตั้งค่าให้เป็น clHighlight สำหรับพาเนลที่เปิดใช้งานอยู่ และ clWindow สำหรับพาเนลอื่นๆ เชื่อมโยงสิ่งนี้เข้ากับ TPdfView.OnClick แต่ละตัวและกระบวนการเปิดเพื่อให้จุดสนใจติดตามเอกสารที่คุณเพิ่งจะเปิดขึ้นมา
การดำเนินการบางอย่างใช้กับพาเนลที่มองเห็นทั้งหมด แทนที่จะเป็นแค่พาเนลที่ทำงานอยู่เพียงอย่างเดียว ค่าบูลีน FAllViewsMode บนฟอร์มจะทำหน้าที่ขับเคลื่อนเส้นทางนั้น เมื่อเป็นความจริง (true) การเปลี่ยนระดับการซูมและการเลื่อนหน้าจะกระจายออกไปยังทุกพาเนลที่มีเอกสารเปิดใช้งานอยู่:
procedure TFormMain.ApplyZoomToAll(NewZoom: Double);
begin
if PdfView1.Active then PdfView1.Zoom := NewZoom;
if PdfView2.Active then PdfView2.Zoom := NewZoom;
if ThreeViewMode and PdfView3.Active then PdfView3.Zoom := NewZoom;
end;
การเลื่อนหน้าแบบซิงโครไนซ์ (Synchronized Page Navigation)
การเลื่อนหน้าแบบซิงโครไนซ์เป็นเพียงทางเลือกหนึ่งแต่ก็มีประโยชน์สำหรับเวิร์กโฟลว์การแก้ไขเอกสารที่ทั้งสองไฟล์มีช่วงของหน้ากระดาษที่เท่ากัน ตรรกะนี้อยู่ในตัวจัดการเหตุการณ์ (event handler) ที่จะทำงานหลังจากผู้ใช้ทำการเลื่อนหน้าจอมุมมองหนึ่ง เมื่อมุมมองต้นทางเปลี่ยนค่า PageNumber ตัวจัดการจะเผยแพร่หมายเลขนั้นไปยังมุมมองอื่นๆ ภายใต้ข้อจำกัดหนึ่งคือ: มุมมองเป้าหมายต้องมีจำนวนหน้าเท่ากับหรือมากกว่าค่านั้นเป็นอย่างน้อย มิฉะนั้นก็จะข้ามไป
PageNumber ใน TPdfView และ TPdf นั้นทำงานเป็นอิสระต่อกัน TPdf.PageNumber ติดตามว่าหน้าใดที่คอมโพเนนต์เอกสารถือว่าเป็นหน้าปัจจุบัน; TPdfView.PageNumber ติดตามสิ่งที่กำลังแสดงผลอยู่บนหน้าจอ เพื่อจุดประสงค์ด้านการเลื่อนหน้าจอ คุณจะต้องใช้คุณสมบัติของมุมมอง (view property) ไม่ใช่คุณสมบัติเอกสาร (document property)
ช่องกาเครื่องหมาย (checkbox) ที่มีป้ายกำกับทำนองว่า "ซิงค์หน้า" จะช่วยให้ผู้ใช้งานควบคุมการทำงานนี้ได้ เมื่อไม่ถูกกาเลือก แต่ละพาเนลก็จะสามารถเลื่อนหน้าของมันเองได้อย่างเป็นอิสระและตัวจัดการจะหยุดทำงานทันที ความเป็นอิสระนั้นมีความสำคัญต่อกรณีการใช้งานที่เอกสารสองไฟล์มีจำนวนหน้าที่แตกต่างกัน หรือกรณีที่ผู้ใช้ต้องการค้นหาข้อความที่ตรงกันในฉบับแปลซึ่งอาจเริ่มต้นในหน้าที่ไม่ตรงกัน การบังคับซิงค์อยู่ตลอดเวลาจะทำให้เครื่องมือใช้งานยากกว่าการจัดเรียงบนเดสก์ท็อปแบบสองหน้าต่างธรรมดา
สิ่งหนึ่งที่ต้องระวัง: การตั้งค่า PdfView.PageNumber ผ่านทางโปรแกรมในตัวจัดการซิงค์ก็จะก่อให้เกิดเหตุการณ์การเปลี่ยนแปลงในมุมมองนั้นเช่นกัน ป้องกันการเรียกตัวเองซ้ำอย่างไม่สิ้นสุด (infinite recursion) ด้วยแฟล็กแบบบูลีนที่คุณตั้งค่าไว้ก่อนการมอบหมาย (assignment) แล้วรีบล้างออกทันทีหลังจากนั้น แฟล็กดังกล่าวใช้กับเฉพาะฟอร์ม (per-form) ไม่ใช่กับแต่ละมุมมอง (per-view) เนื่องจากมุมมองทั้งสามจะใช้ตัวจัดการแบบเดียวกัน
การซูมแบบต่อพาเนล (Zoom Per Panel)
TPdfView แต่ละตัวมีคุณสมบัติ Zoom ของมันเอง ซึ่งเป็นค่าประเภท Double ในรูปแบบเปอร์เซ็นต์ โดยที่ Zoom := 100 หมายถึงขนาดที่แท้จริง (100%) การตั้งค่าในส่วนนี้จะไปเขียนทับข้อมูลส่วน FitMode ใดๆ ที่กำลังทำงานอยู่ สำหรับปุ่มปรับความกว้างให้พอดีหน้าจอบนพาเนลที่ทำงานอยู่ ให้อ่านการซูมแบบพอดี (fit zoom) จากคำสั่ง PdfView.PageWidthZoom[PdfView.PageNumber] แล้วกำหนดค่าลงไป สำหรับปุ่มปรับให้พอดีหน้า ให้ใช้คำสั่ง PageZoom[PageNumber] ทั้งสองตัวเป็นคุณสมบัติในรูปแบบอาร์เรย์ที่ใช้หมายเลขหน้าแบบ 1-based ในการทำดัชนี ดังนั้นคุณจึงควรป้องกันไม่ให้หมายเลขหน้าเป็นศูนย์ก่อนที่จะทำการเข้าถึง
เมื่อคุณส่งออกหน้าที่แสดงผลปัจจุบันไปยังรูปแบบรูปภาพ คุณจะอ่านค่าการหมุน (rotation) จากมุมมอง แต่เรียก RenderPage ไปที่คอมโพเนนต์ TPdf ไม่ใช่มุมมอง รูปแบบบิตแมปของ TPdf.RenderPage จะรับขนาดพิกเซลที่ชัดเจนบวกกับค่า TRotation และการตั้งค่า TRenderOptions ค่าแปรผันของฟังก์ชัน (function variant) จะส่งกลับคืน TBitmap ที่มีผู้เรียกเป็นเจ้าของซึ่งคุณจะปลดปล่อยมันเองหลังจากทำการบันทึก:
procedure TFormMain.SaveActiveViewAsImage;
var
Pdf: TPdf;
Bmp: TBitmap;
Jpeg: TJpegImage;
begin
if not Assigned(FActivePdfView) or not FActivePdfView.Active then
Exit;
Pdf := FActivePdfView.Pdf;
Pdf.PageNumber := FActivePdfView.PageNumber;
Bmp := Pdf.RenderPage(
0, 0,
Round(Pdf.PageWidth * 2),
Round(Pdf.PageHeight * 2),
FActivePdfView.Rotation, [], clWhite);
try
if SavePictureDialog.Execute then
begin
Jpeg := TJpegImage.Create;
try
Jpeg.Assign(Bmp);
Jpeg.CompressionQuality := 90;
Jpeg.SaveToFile(SavePictureDialog.FileName);
finally
Jpeg.Free;
end;
end;
finally
Bmp.Free;
end;
end;
ตัวคูณ 2x บนความกว้างและความสูงจะให้ผลลัพธ์ที่คมชัดยิ่งขึ้นสำหรับเอกสารที่มีข้อความตัวเล็กๆ try/finally ในช่วงของบิตแมปที่ว่างอยู่นั้นไม่ใช่สิ่งที่เป็นทางเลือก; การกดยกเลิกใน TSaveDialog ก็ยังคงทำงานในบล็อก finally และคุณต้องการให้ปล่อยบิตแมปนั้นออกมาไม่ว่าผู้ใช้จะทำสิ่งใดก็ตาม
ข้อกำหนดของ DLL
PDFium Component ห่อหุ้มไลบรารี pdfium ดั้งเดิมเอาไว้ โปรเซสโฮสต์ 32-บิต ต้องการ pdfium32.dll; โฮสต์ 64-บิต ต้องการ pdfium64.dll รุ่นที่มีเอนจิน V8 JavaScript จะมีการเติมคำต่อท้าย v8 และมีน้ำหนักประมาณ 23-27 MB เมื่อเทียบกับรุ่นมาตรฐานที่มีขนาด 5-6 MB สำหรับโปรแกรมเปิดดูแบบเปรียบเทียบข้อมูลที่ปิดการทำงานของการกรอกแบบฟอร์ม (Pdf.FormFill := False) การใช้รุ่นมาตรฐานที่ไม่มี V8 ก็เพียงพอแล้ว และยังช่วยให้ขนาดไฟล์แจกจ่ายมีขนาดเล็กลงอีกด้วย
วางไฟล์ DLL ไว้ในไดเร็กทอรีเดียวกับไฟล์ปฏิบัติการ (executable) หรือในไดเร็กทอรีใดๆ บนระบบ PATH คอมโพเนนต์จะโหลดไฟล์นี้ตามความต้องการเมื่อมีการใช้งาน TPdf ตัวแรก ดังนั้นการที่ไฟล์ DLL ขาดหายไปจึงมักจะปรากฏให้เห็นในจุดนั้น แทนที่จะเป็นตอนที่แอปพลิเคชันเริ่มต้นการทำงาน หากคุณจัดส่งตัวติดตั้ง (installer) แนวทางที่น่าเชื่อถือที่สุดคือการคัดลอกไฟล์ DLL ลงในโฟลเดอร์แอปพลิเคชันระหว่างขั้นตอนการติดตั้ง แทนที่จะพึ่งพาไดเร็กทอรีของระบบที่ผู้ดูแลระบบอาจเข้ามาทำความสะอาดทิ้งในภายหลัง
รุ่น V8 จะมีประโยชน์เป็นหลักเมื่อคุณต้องการโต้ตอบกับการกระทำของ PDF JavaScript เช่น เพื่อเปิดการทำงานฟิลด์การคำนวณ หรือส่งข้อมูลผ่านแฮนเดิล โปรแกรมดูแบบเปรียบเทียบที่เป็นแบบพาสซีฟไม่มีเหตุผลให้ต้องรัน JavaScript; การตั้งค่า Pdf.FormFill := False ก่อนที่จะตั้งค่า Active := True จะเป็นการข้ามสภาพแวดล้อมการกรอกแบบฟอร์มทั้งหมด ซึ่งนั่นก็หมายความว่าจะไม่มีเอนจิน JS ถูกเปิดขึ้นมาทำงานเลยแม้ว่าจะใช้รุ่นมาตรฐานก็ตาม นั่นคือค่าเริ่มต้นที่ถูกต้องสำหรับโปรแกรมดูแบบอ่านได้อย่างเดียว ไม่ว่าคุณจะส่ง DLL รุ่นใดมาให้ก็ตาม
สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับ PDFium Component และ API ตัวเต็ม คุณสามารถเข้าดูได้ที่หน้าเพจผลิตภัณฑ์ของ Delphi PDFium Component