PDF köprüleri URI annotation'larıdır: bir sayfa alanının bir kısmını kaplayan ve tıklandığında görüntüleyiciye bir URL açmasını söyleyen bir dikdörtgen. Annotation ile onun altındaki metin tamamen bağımsız nesnelerdir. HotPDF'in PrintHyperlink metodu her ikisini de tek bir çağrıda paketler; metni çizer ve annotation dikdörtgenini işlenmiş metin metriklerinden hesaplar. Bu kolaylık, üretim kodu yazmadan önce anlaşılmaya değer bir ayrıntıyı gizler. Ayrıca hikâyenin tamamı da bu değildir: AddURILink, kendinizin çizdiği içeriğin üzerine tıklanabilir bir alan yerleştirir ve AddGoToLink belge içi gezinmeyi ele alır — ikisi de aşağıda ele alınmaktadır
PrintHyperlink nasıl çalışır
PrintHyperlink, THPDFPage üzerinde yer alır ve dört argüman alır: X ve Y koordinatları (punto cinsinden, sol alt köşe başlangıç noktası, Y yukarı doğru artar), çizilecek etiket dizesi ve URL hedefi. Dahili olarak geçerli köprü renginde TextOut çağırır, ardından annotation dikdörtgenini hemen geçerli yazı tipi metriklerindeki TextWidth ve TextHeight değerlerinden hesaplar. Bu, yazı tipi ve boyutunun çağrıdan önce ayarlanmış olması gerektiği ve etiketi çizmek ile annotation'ı yerleştirmek arasında değişmemeleri gerektiği anlamına gelir, çünkü ikisi de aynı çağrıda çözümlenir
Varsayılan renk clBlue'dur. SetRGBHyperlinkColor onu yalnızca sonraki çağrılar için değiştirir; halihazırda yazılmış annotation'ları geriye dönük olarak güncellemez. Aynı sayfada farklı bağlantı grupları için farklı renklere ihtiyacınız varsa, her gruptan önce SetRGBHyperlinkColor çağırın ve sonrasında sıfırlayın
İşte iki farklı renkte üç bağlantı yazan minimal bir belge:
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;
Koordinat tuzağı
HotPDF, punto (1/72 inç) cinsinden Y'nin yukarı doğru büyüdüğü sol alt başlangıç noktasını kullanır. Bir A4 sayfası 595 x 842 pt'dir; bir US Letter sayfası ise 612 x 792 pt'dir. Y=750 bir A4 sayfasının üst kısmına yakın oturur ve Y=50 alt kenar boşluğuna yakın olurdu. Ekran grafiklerinden veya HTML'den gelen biri bunun tersini varsayar ve ilk bağlantı satırını doğrudan görünür alanın dışına yerleştirir
PrintHyperlink'in hesapladığı annotation dikdörtgeni aynı koordinat sistemini kullanır. Daha sonra sayfayı döndürür, ölçeklendirir veya X/Y değerlerinizi yeniden hesaplamadan sayfa boyutunu değiştirirseniz, görünür metin ile tıklanabilir dikdörtgen birbirinden ayrılır. Bağlantı, metnin yakınında bir yere tıklamanın URL'yi tetiklemesi anlamında "çalışır", ancak etkin bölge artık okuyucunun gördüğüyle eşleşmez. Yalnızca %100'de geliştirme makinesinde değil, gönderdiğiniz gerçek sayfa boyutu ve yakınlaştırma seviyesinde test edin
Sürüklenmenin garanti olduğu bir durum: PrintHyperlink'i bir A4 sayfasına uygun koordinatlarla çağırıp ardından X/Y değerlerini ayarlamadan özel dar formatlı bir sayfaya geçerseniz, annotation tamamen sayfanın dışında kalabilir. Annotation nesnesi yine de PDF'e yazılır; çoğu görüntüleyici onu sessizce kırpar, bu yüzden bağlantı herhangi bir hata olmadan basitçe kaybolur
Etiket metni ile URL hedefi
Text ve Link argümanları bağımsızdır. Hedef, sorgu parametreleri olan tam nitelikli bir HTTPS URL'si iken "Download invoice PDF" çizebilirsiniz. Bu ayrım bilinçlidir; görünür etiket insan tarafından okunabilir olmalıdır ve URL uzun veya dinamik olarak üretilmiş olabilir
Sorun yaratan şey, etiketin özellikle uzun olduğunda ham URL'nin kendisi olmasıdır. URL görsel olarak iki satıra sarıyorsa ancak annotation dikdörtgeni tek satırlık bir dize için hesaplanmışsa, yalnızca ilk satır tıklanabilir olur. PrintHyperlink çok satırlı akışı ele almaz; etiketi geçerli yazı tipi boyutunda ve sayfa genişliğinde tek bir satıra sığacak kadar kısa tutun, tam URL'yi hedef olarak alan kısa açıklayıcı bir etiket kullanın veya bir sonraki bölümde gösterilen satır başına çözümü uygulayın
Etkin bir internet bağlantısı olmadan arşivlenecek veya dağıtılacak belgeler için, URL'nin kendisinin yalnızca annotation meta verisi olarak değil, belgenin gövdesinde bir yerde basılı biçimde görünmesi gerekip gerekmediğini de düşünün. PDF'i kâğıda yazdıran bir okuyucu, bir URI annotation'ından hiçbir şey elde etmez
Çok satır sınırlamasını aşmak
Bir bağlantı etiketi gerçekten birden fazla satıra yayılmak zorunda kaldığında — birebir basılan uzun bir URL veya baştan sona tıklanabilir olması gereken sarılmış bir cümle — çözüm, onu tek bir bağlantı olarak ele almayı bırakıp satır başına bir bağlantı olarak ele almaktır. Her PrintHyperlink çağrısı, dikdörtgenini çizdiği metinden hesaplar, bu nedenle aynı Link hedefini paylaşan birkaç çağrı, hepsi aynı URL'yi açan, doğru boyutlandırılmış birkaç annotation üretir. Okuyucu farkı anlayamaz; her satır bir tıklamaya yanıt verir
procedure PrintWrappedHyperlink(Page: THPDFPage; X, TopY, LineStep: Single;
const Lines: array of AnsiString; const Link: AnsiString);
var
I: Integer;
begin
for I := 0 to High(Lines) do
Page.PrintHyperlink(X, TopY - I * LineStep, Lines[I], Link);
end;
// Usage: break the label at the positions where your layout wraps it
Pdf.CurrentPage.SetFont('Arial', [], 10);
PrintWrappedHyperlink(Pdf.CurrentPage, 50, 400, 14,
['https://www.loslab.com/en-us/pdf-library/',
'delphi-pdf-component.html'],
'https://www.loslab.com/en-us/pdf-library/delphi-pdf-component.html');
Dizeyi bölmek sizin sorumluluğunuzdadır: her aday satırı test etmek için TextWidth kullanarak, geçerli yazı tipi ve sütun genişliğinde görsel olarak sarılacağı aynı konumlarda bölün. Alternatif, sarılmış metni sade TextOut çağrılarıyla kendiniz çizmek ve ardından her satırın üzerine bir AddURILink dikdörtgeni sermektir — metin zaten kendi kelime sarma mantığınızca üretildiğinde daha iyi bir yol, ki bu da bizi o fonksiyona getiriyor
AddURILink: çizdiğiniz her şeyin üzerine tıklanabilir alanlar
PrintHyperlink bir kolaylık sarmalayıcısıdır: kendi etiketini çizer ve dikdörtgeni o etiketin metriklerinden türetir. AddURILink, doğrudan açığa çıkarılan daha alt seviye yarıdır:
function AddURILink(Rectangle: TRect; const URL: AnsiString;
const Description: AnsiString = ''): THPDFDictionaryObject;
Yalnızca annotation'ı yazar — hiçbir metin çizilmez ve hiçbir renk değişmez. Rectangle, çizim çağrılarınızla aynı koordinat uzayında yorumlanır, bu nedenle TextOut'a veya bir resim çağrısına geçtiğiniz tam X/Y değerlerini yeniden kullanabilirsiniz. Bu, görünür içerik zaten var olduğunda onu doğru araç yapar: bir resim etkin noktası, bir tablo hücresi, daha önce çizilmiş bir metin bloğu veya yukarıdaki çözümdeki gibi sarılmış bir paragrafın bir satırı. Annotation, sıfır genişlikte bir kenarlık taşır, bu yüzden görünür hiçbir şey değişmez; tıklanabilir bölge tam olarak belirttiğiniz dikdörtgendir
Fonksiyon, annotation sözlüğünü bir THPDFDictionaryObject olarak döndürür. Çoğu çağıran sonucu atar, ancak onu tutmak, belge yazılmadan önce annotation'ın girişlerini ayarlamanıza olanak tanır
Yerleşik iki uyumluluk ayrıntısı vardır. PDF/A modlarında, annotation'ın print bayrağı bu standartların gerektirdiği şekilde ayarlanır. PDFUACompliance altında Description parametresi boş olmayan bir dize olmalıdır — bu, yardımcı teknolojinin bağlantı için duyurduğu şey olan annotation'ın /Contents girişi olur — ve çağrı, uyumsuz bir dosyayı sessizce yaymak yerine bir exception fırlatır. PrintHyperlink bu kuraldan öncedir ve hiçbir açıklama eklemez, bu yüzden PDF/UA çıktısı için etiketi TextOut ile çizin ve annotation'ı anlamlı bir açıklamayla birlikte AddURILink ile yerleştirin
Karar kuralı basittir: bağlantı henüz çizmediğiniz kısa bir metin parçasıysa PrintHyperlink'i kullanın; tıklanabilir bölge, kendinizin çizdiği veya ölçtüğü içerik tarafından tanımlanıyorsa AddURILink'i kullanın
AddGoToLink ile belge içi gezinme
Harici URL'ler, bağlantı annotation'larının yaptığının yalnızca yarısıdır. Diğer yarısı belge içindeki gezinmedir — bölümlere atlayan bir içindekiler tablosu, bölümler arası çapraz referanslar. HotPDF bunu AddGoToLink aracılığıyla açığa çıkarır:
procedure AddGoToLink(Rectangle: TRect; TargetPageIndex: Integer;
YPos: Single = -1; const Description: AnsiString = '');
Üç anlam kesin olarak belirtilmeye değerdir, çünkü hiçbiri imzadan tahmin edilemez. TargetPageIndex sıfır tabanlıdır: belgenin ilk sayfası sayfa 0'dır ve CurrentPageNumber ile eşleşir. Çağrıyı yaptığınızda hedef sayfa zaten var olmalıdır; dizin aralık dışındaysa, prosedür bir annotation eklemeden döner — exception yok, bağlantı yok, uyarı yok. İleriye işaret eden bir içindekiler tablosu için önce tüm sayfaları oluşturun, ardından geri dönüp bağlantıları ekleyin
YPos, hedef sayfadaki dikey konumu, çizim çağrılarınızla aynı koordinat uzayında seçer. Varsayılan -1 (herhangi bir negatif değer), boş bir hedef koordinatı yazar ve görüntüleyiciye hedef sayfaya vardığında geçerli dikey konumunu korumasını söyler. Negatif olmayan bir değer geçin ve görüntüleyici, o konum pencerenin üstünde oturacak şekilde kaydırır — bağladığınız başlığın Y koordinatını kullanın. Yakınlaştırma her zaman değişmeden bırakılır. AddURILink'te olduğu gibi, PDFUACompliance altında Description boş olmamalıdır ve bağlantının alternatif metni olur
procedure BuildLinkedTOC(const FileName: string);
const
Chapters: array[0..2] of string =
('Introduction', 'Installation', 'API Reference');
var
Pdf: THotPDF;
I, Y: Integer;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := FileName;
Pdf.BeginDoc; // page 0 becomes the TOC page
// Create the chapter pages first so the link targets exist
for I := 0 to High(Chapters) do
begin
Pdf.AddPage; // pages 1..3
Pdf.CurrentPage.SetFont('Arial', [fsBold], 14);
Pdf.CurrentPage.TextOut(50, 780, 0, Chapters[I]);
end;
// Switch back to page 0 and draw the TOC entries with their links
Pdf.CurrentPageNumber := 0;
Pdf.CurrentPage.SetFont('Arial', [fsBold], 16);
Pdf.CurrentPage.TextOut(50, 760, 0, 'Contents');
Pdf.CurrentPage.SetFont('Arial', [], 11);
Y := 720;
for I := 0 to High(Chapters) do
begin
Pdf.CurrentPage.TextOut(70, Y, 0, Chapters[I]);
Pdf.CurrentPage.AddGoToLink(
Rect(70, Y + 14, 300, Y - 3), // covers the entry with padding
I + 1, // zero-based: chapters are pages 1..3
780, // land with the heading at the top
AnsiString('Go to ' + Chapters[I]));
Y := Y - 25;
end;
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;
Her giriş, tüm satırın işaretçiye yanıt vermesi için metinden daha geniş bir dikdörtgen alır ve her bağlantı, bölüm başlığı (Y=780'de çizilmiş) pencerenin üstünde olacak şekilde konumlanır. Daha sonra bölümlerin önüne bir sayfa eklerseniz, her TargetPageIndex bir kayar; dizinleri sabit kodlamak yerine sayfa oluşturma döngünüzden hesaplayın
Eksiksiz bir belge üretim örneği
Aşağıdaki desen, daha gerçekçi bir senaryo gösterir: bir başlık bölümü, gövde metni ve bir alt bilgi bağlantı satırı ile kısa bir raporu, TEdit alanları olan bir formdan değil, tamamen koddan üretmek:
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'un her metin çağrısı grubundan önce çağrıldığına dikkat edin. Yazı tipi AddPage boyunca kalıcı olmaz ve yeni bir sayfada PrintHyperlink'ten önce ayarlamayı unutursanız, annotation dikdörtgeni sayfanın varsayılan metrikleri her ne ise ona göre hesaplanır ki bu beklediğinizden farklı olabilir
Annotation işleyişinin görüntüleyiciler arasında değiştiği yerler
PDF URI annotation'ları ISO 32000-1 §12.6.4.7'de tanımlanmıştır ve uyumlu her görüntüleyici bunları izlemelidir. Uygulamada, birkaç davranış görüntüleyiciye göre değişir. Adobe Acrobat, güvenilen alan adları listesinde olmayan URL'ler için ilk tıklamada bir güvenlik istemi gösterir; birçok tarayıcı ve hafif okuyucu göstermez. Kilitli ortamlardaki bazı kurumsal PDF görüntüleyiciler, URI annotation'larını politika gereği tamamen devre dışı bırakır, bu yüzden bir tıklama görünür bir hata olmadan hiçbir şey yapmaz. Mobil PDF uygulamaları, bağlantıları uygulamanın web görünümü içinde mi yoksa sistem tarayıcısına devredip mi açacağı konusunda değişkenlik gösterir
Bunların hiçbiri üretim tarafından düzeltebileceğiniz hatalar değildir; bunlar görüntüleyici politika kararlarıdır. Yapabileceğiniz şey, URL'yi belge gövdesinde de görünür kılan bağlantı etiketleri yazmaktır, böylece kısıtlı bir ortamdaki bir okuyucu yine de adresi manuel olarak kopyalayabilir. Annotation kolaylıktır; metin ise yedektir
Bilmeye değer bir başka ayrıntı: PDF URI annotation'ları varsayılan olarak herhangi bir görsel alt çizgi taşımaz. Çoğu görüntüleyicide gördüğünüz alt çizgi, içerik akışındaki bir glif tarafından değil, annotation türüne dayanarak görüntüleyicinin kendisi tarafından çizilir. Etkileşimli olmayan bir işleyiciye yazdırmaya veya PDF'ten resme dönüştürmeye dayanan fiziksel bir alt çizgiye ihtiyacınız varsa, onu metin taban çizgisinin altındaki uygun Y ofsetinde LineTo ve Stroke ile açıkça çizin. Bu ayrı bir çizim işlemidir, PrintHyperlink'in sizin için ele aldığı bir şey değildir
Burada gösterilen köprü API'si, Delphi ve C++Builder için HotPDF Bileşeninin bir parçasıdır