ไฮเปอร์ลิงก์ใน PDF คือคำอธิบายประกอบแบบ URI (URI annotations): ซึ่งเป็นพื้นที่สี่เหลี่ยมบนหน้ากระดาษที่เมื่อคลิก จะสั่งให้โปรแกรมอ่านเปิด URL ที่กำหนด คำอธิบายประกอบและข้อความที่อยู่ด้านล่างนั้นเป็นออบเจกต์ที่แยกจากกันอย่างสิ้นเชิง PrintHyperlink ของ HotPDF รวมทั้งสองอย่างเข้าไว้ด้วยกันในการเรียกเพียงครั้งเดียว โดยวาดข้อความและคำนวณพื้นที่สี่เหลี่ยมของคำอธิบายประกอบจากขนาดตัวอักษรที่เรนเดอร์ ความสะดวกนี้ซ่อนรายละเอียดที่คุ้มค่าแก่การทำความเข้าใจก่อนที่คุณจะเขียนโค้ดสำหรับระบบจริง
PrintHyperlink ทำงานอย่างไร
PrintHyperlink อยู่ใน THPDFPage และรับอาร์กิวเมนต์สี่ตัว: พิกัด X และ Y (ในหน่วยพอยต์ จุดกำเนิดซ้ายล่าง Y เพิ่มขึ้นไปด้านบน) สตริงป้ายกำกับที่จะวาด และ URL เป้าหมาย ภายในระบบจะเรียก TextOut ด้วยสีไฮเปอร์ลิงก์ปัจจุบัน จากนั้นจะคำนวณพื้นที่สี่เหลี่ยมของคำอธิบายประกอบจาก TextWidth และ TextHeight ตามขนาดฟอนต์ปัจจุบันในทันที ซึ่งหมายความว่าต้องตั้งค่าฟอนต์และขนาดก่อนการเรียกใช้ และต้องไม่มีการเปลี่ยนแปลงระหว่างการวาดป้ายกำกับและการวางคำอธิบายประกอบ เนื่องจากทั้งสองอย่างนี้ถูกจัดการพร้อมกันในการเรียกครั้งเดียว
สีเริ่มต้นคือ clBlue การเรียก SetRGBHyperlinkColor จะเปลี่ยนสีสำหรับการเรียกครั้งต่อไปเท่านั้น จะไม่มีผลย้อนหลังกับคำอธิบายประกอบที่เขียนไปแล้ว หากคุณต้องการสีที่แตกต่างกันสำหรับกลุ่มลิงก์ต่าง ๆ ในหน้าเดียวกัน ให้เรียก SetRGBHyperlinkColor ก่อนแต่ละกลุ่มแล้วตั้งค่ากลับหลังจากนั้น
นี่คือเอกสารขนาดเล็กที่สุดที่เขียนลิงก์สามรายการด้วยสองสีที่แตกต่างกัน:
procedure CreateLinkedReport(const FileName: string);
var
Pdf: THotPDF;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := FileName;
Pdf.BeginDoc;
Pdf.CurrentPage.SetFont('Arial', [], 11);
// Default blue for informational links
Pdf.CurrentPage.TextOut(50, 750, 0, 'Reference links:');
Pdf.CurrentPage.PrintHyperlink(50, 720, 'Product page', 'https://www.loslab.com/en-us/pdf-library/delphi-pdf-component.html');
Pdf.CurrentPage.PrintHyperlink(50, 695, 'Online manual', 'https://www.loslab.com/en-us/pdf-library/delphi-pdf-component.html');
// Red for the action link
Pdf.CurrentPage.SetRGBHyperlinkColor(clRed);
Pdf.CurrentPage.PrintHyperlink(50, 660, 'Purchase license', 'https://www.loslab.com/en-us/buy-hotpdf-fastspring.html');
Pdf.CurrentPage.SetRGBHyperlinkColor(clBlue); // restore default
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;
กับดักพิกัด
HotPDF ใช้จุดกำเนิดซ้ายล่างโดยที่ Y เพิ่มขึ้นไปด้านบน ในหน่วยพอยต์ (1/72 นิ้ว) หน้า A4 มีขนาด 595 x 842 pt; หน้า US Letter มีขนาด 612 x 792 pt ค่า Y=750 จะอยู่ใกล้ขอบบนของหน้า A4 และ Y=50 จะอยู่ใกล้ขอบล่าง ผู้ที่คุ้นเคยกับกราฟิกบนหน้าจอหรือ HTML มักจะคิดตรงกันข้ามและเผลอวางลิงก์บรรทัดแรกหลุดออกจากพื้นที่แสดงผล
พื้นที่สี่เหลี่ยมคำอธิบายประกอบที่ PrintHyperlink คำนวณจะใช้ระบบพิกัดเดียวกัน หากคุณหมุนหน้ากระดาษ ย่อขยาย หรือเปลี่ยนขนาดหน้ากระดาษในภายหลังโดยไม่คำนวณค่า X/Y ใหม่ ข้อความที่มองเห็นและพื้นที่สี่เหลี่ยมที่คลิกได้จะเคลื่อนออกจากกัน ลิงก์ยังคง "ทำงาน" ได้ในแง่ที่ว่าการคลิกบริเวณใกล้เคียงกับข้อความจะกระตุ้น URL แต่จุดที่คลิกได้จริงจะไม่ตรงกับที่ผู้อ่านเห็นอีกต่อไป ควรทดสอบกับขนาดหน้ากระดาษและระดับการซูมจริงที่คุณตั้งใจจะเผยแพร่ ไม่ใช่แค่บนเครื่องพัฒนาที่ซูม 100% เท่านั้น
กรณีหนึ่งที่การเคลื่อนออกจากกันจะเกิดขึ้นแน่นอน: หากคุณเรียก PrintHyperlink ด้วยพิกัดที่เหมาะสมกับหน้า A4 แล้วเปลี่ยนไปใช้หน้าฟอร์แมตแคบแบบกำหนดเองโดยไม่ปรับพิกัด X/Y คำอธิบายประกอบอาจหลุดออกนอกหน้ากระดาษไปเลย ออบเจกต์คำอธิบายประกอบจะยังคงถูกเขียนลงใน PDF เพียงแต่โปรแกรมอ่านส่วนใหญ่จะตัดทิ้งอย่างเงียบ ๆ ทำให้ลิงก์หายไปโดยไม่มีข้อผิดพลาดใด ๆ แจ้งเตือน
ข้อความป้ายกำกับกับ URL เป้าหมาย
อาร์กิวเมนต์ Text และ Link เป็นอิสระจากกัน คุณสามารถวาดคำว่า "Download invoice PDF" ในขณะที่เป้าหมายเป็น URL HTTPS แบบเต็มรูปแบบที่มีพารามิเตอร์คิวรี การแยกกันนี้เป็นความตั้งใจ ป้ายกำกับที่มองเห็นควรเป็นภาษาที่คนอ่านเข้าใจได้ และ URL สามารถยาวหรือสร้างขึ้นแบบไดนามิกได้
สิ่งที่จะสร้างปัญหาคือเมื่อป้ายกำกับเป็น URL ดิบเสียเอง โดยเฉพาะเมื่อมันยาวมาก หาก URL ม้วนตัวเป็นสองบรรทัดในแง่ของการแสดงผล แต่พื้นที่สี่เหลี่ยมคำอธิบายประกอบถูกคำนวณสำหรับสตริงบรรทัดเดียว จะมีเพียงบรรทัดแรกเท่านั้นที่คลิกได้ PrintHyperlink ไม่รองรับการไหลของข้อความหลายบรรทัด ให้รักษาป้ายกำกับให้สั้นพอที่จะอยู่ในบรรทัดเดียวที่ขนาดฟอนต์และความกว้างหน้ากระดาษปัจจุบัน หรือใช้ป้ายกำกับสั้น ๆ ที่อธิบายความหมายโดยมี URL เต็มเป็นเป้าหมาย
สำหรับเอกสารที่จะถูกจัดเก็บหรือแจกจ่ายโดยไม่มีการเชื่อมต่ออินเทอร์เน็ตที่เปิดใช้งานอยู่ ให้พิจารณาด้วยว่า URL เองควรปรากฏในรูปแบบการพิมพ์ที่ใดที่หนึ่งในเนื้อหาเอกสารด้วย ไม่ใช่แค่เป็นข้อมูลเมตาของคำอธิบายประกอบ ผู้อ่านที่พิมพ์ PDF ออกมาบนกระดาษจะไม่ได้รับประโยชน์ใด ๆ จากคำอธิบายประกอบ URI เลย
การแก้ข้อจำกัดของข้อความหลายบรรทัด
PrintHyperlink เหมาะกับป้ายกำกับบรรทัดเดียว หากข้อความต้องห่อบรรทัด ให้ใช้ TextOut วาดแต่ละบรรทัดเอง แล้วสร้างพื้นที่คลิกที่ครอบคลุมทุกบรรทัดด้วย AddURILink การแยกข้อความที่มองเห็นออกจากคำอธิบายประกอบทำให้การจัดวางและพื้นที่กดไม่ขึ้นต่อการคำนวณความกว้างของสตริงเพียงบรรทัดเดียว
AddURILink: พื้นที่คลิกเหนือทุกสิ่งที่วาด
AddURILink ใช้สร้างคำอธิบายประกอบ URI จากสี่เหลี่ยมพิกัดโดยไม่สนใจว่าด้านล่างเป็นข้อความ รูปภาพ หรือรูปทรงเวกเตอร์ใด จึงเหมาะกับปุ่มที่วาดเองและกับป้ายกำกับหลายบรรทัด พื้นที่นี้เป็นข้อมูลโต้ตอบแยกจาก content stream และจะไม่เปลี่ยนสิ่งที่ผู้อ่านเห็นบนหน้า
การนำทางภายในด้วย AddGoToLink
สำหรับการเชื่อมโยงไปยังหน้าอื่นในเอกสารเดียวกัน ให้ใช้ AddGoToLink แทน URI ภายนอก เมธอดนี้ผูกสี่เหลี่ยมกับปลายทางภายใน จึงไม่ต้องพึ่งเครือข่ายและไม่เปิดคำเตือนความปลอดภัยแบบลิงก์ไปเว็บ แต่ยังต้องคำนวณพิกัดหน้าและดัชนีปลายทางให้ตรงกับเอกสารที่สร้างจริง
ตัวอย่างการสร้างเอกสารฉบับสมบูรณ์
รูปแบบด้านล่างแสดงสถานการณ์ที่สมจริงยิ่งขึ้น: การสร้างรายงานขนาดสั้นที่มีส่วนหัว ข้อความเนื้อหา และลิงก์ที่ส่วนท้ายกระดาษ ทั้งหมดทำจากโค้ดแทนที่จะมาจากฟอร์มที่มีฟิลด์ TEdit:
procedure GenerateProductSheet(
const FileName, ProductName, ProductURL, SupportURL: string);
var
Pdf: THotPDF;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := FileName;
Pdf.Compression := cmFlateDecode;
Pdf.BeginDoc;
// Header
Pdf.CurrentPage.SetFont('Arial', [fsBold], 16);
Pdf.CurrentPage.TextOut(50, 750, 0, WideString(ProductName));
// Body paragraph placeholder
Pdf.CurrentPage.SetFont('Arial', [], 11);
Pdf.CurrentPage.TextOut(50, 710, 0, 'See the links below for full documentation.');
// Footer links
Pdf.CurrentPage.SetFont('Arial', [], 10);
Pdf.CurrentPage.TextOut(50, 80, 0, 'Links:');
Pdf.CurrentPage.PrintHyperlink(50, 60, 'Product page', ProductURL);
Pdf.CurrentPage.PrintHyperlink(200, 60, 'Support', SupportURL);
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;
สังเกตว่ามีการเรียก SetFont ก่อนการเรียกข้อความแต่ละกลุ่ม ฟอนต์จะไม่ถูกจดจำเมื่อเรียก AddPage และถ้าคุณลืมตั้งค่าฟอนต์ก่อนเรียก PrintHyperlink บนหน้าใหม่ พื้นที่สี่เหลี่ยมของคำอธิบายประกอบจะถูกคำนวณตามขนาดค่าเริ่มต้นของหน้ากระดาษ ซึ่งอาจแตกต่างจากที่คุณคาดไว้
ความแตกต่างของการจัดการคำอธิบายประกอบในโปรแกรมอ่านต่าง ๆ
คำอธิบายประกอบ URI ของ PDF ถูกกำหนดไว้ใน ISO 32000-1 §12.6.4.7 และโปรแกรมอ่านที่ได้มาตรฐานทุกตัวควรปฏิบัติตาม ในความเป็นจริง มีพฤติกรรมบางอย่างที่แตกต่างกันไปตามโปรแกรมอ่าน Adobe Acrobat จะแสดงคำเตือนความปลอดภัยเมื่อคลิกครั้งแรกสำหรับ URL ที่ไม่อยู่ในรายการโดเมนที่เชื่อถือได้ ในขณะที่เบราว์เซอร์และโปรแกรมอ่านขนาดเล็กส่วนใหญ่จะไม่แสดง โปรแกรมอ่าน PDF ระดับองค์กรในสภาพแวดล้อมที่ถูกจำกัดอย่างเข้มงวดจะปิดใช้งานคำอธิบายประกอบ URI ทั้งหมดตามนโยบาย ดังนั้นการคลิกจึงไม่เกิดผลอะไร และไม่มีข้อผิดพลาดแสดงให้เห็น แอป PDF บนมือถือมีความแตกต่างกันตรงที่อาจเปิดลิงก์ใน web view ภายในแอป หรือส่งต่อไปยังเบราว์เซอร์ของระบบ
สิ่งเหล่านี้ไม่ใช่ข้อบกพร่องที่คุณสามารถแก้ไขได้จากฝั่งการสร้างไฟล์ แต่เป็นการตัดสินใจด้านนโยบายของโปรแกรมอ่าน สิ่งที่คุณทำได้คือเขียนป้ายกำกับลิงก์ที่ทำให้ URL ปรากฏในเนื้อหาเอกสารด้วย เพื่อให้ผู้อ่านในสภาพแวดล้อมที่ถูกจำกัดยังสามารถคัดลอกที่อยู่ได้ด้วยตนเอง คำอธิบายประกอบเป็นเพียงความสะดวก ข้อความเป็นตัวเลือกสำรอง
รายละเอียดอีกประการที่ควรทราบ: คำอธิบายประกอบ URI ของ PDF จะไม่มีขีดเส้นใต้ที่มองเห็นได้โดยค่าเริ่มต้น ขีดเส้นใต้ที่คุณเห็นในโปรแกรมอ่านส่วนใหญ่ถูกวาดโดยตัวโปรแกรมอ่านเองโดยอิงจากประเภทของคำอธิบายประกอบ ไม่ใช่กลีฟใน content stream หากคุณต้องการขีดเส้นใต้ที่จับต้องได้ซึ่งคงอยู่เมื่อพิมพ์ไปยังตัวเรนเดอร์ที่ไม่ตอบสนอง หรือการแปลง PDF เป็นรูปภาพ ให้วาดมันอย่างชัดเจนด้วย LineTo และ Stroke ที่ระยะออฟเซต Y ที่เหมาะสมใต้เส้นฐานข้อความ นั่นคือการวาดแยกต่างหาก ไม่ใช่สิ่งที่ PrintHyperlink จัดการให้คุณ
API ไฮเปอร์ลิงก์ที่แสดงในที่นี้เป็นส่วนหนึ่งของ HotPDF Component สำหรับ Delphi และ C++Builder