PDF hiperveze su URI anotacije: pravougaonik koji pokriva neki deo stranice i koji, kada se klikne, kaže pregledaču da otvori URL. Anotacija i tekst ispod nje su potpuno nezavisni objekti. HotPDF-ov PrintHyperlink objedinjuje oboje u jedan poziv, crtajući tekst i računajući pravougaonik anotacije na osnovu metrike iscrtanog teksta. Ta pogodnost skriva detalj koji vredi razumeti pre nego što napišete produkcioni kod. To takođe nije cela priča: AddURILink postavlja klikabilnu oblast preko sadržaja koji ste sami nacrtali, a AddGoToLink se bavi internom navigacijom — oboje je obrađeno ispod
Kako radi PrintHyperlink
PrintHyperlink se nalazi na THPDFPage i prima četiri argumenta: X i Y koordinate (u tačkama, koordinatni početak u donjem levom uglu, Y raste naviše), niz oznake za crtanje i ciljni URL. Interno poziva TextOut u trenutnoj boji hiperveze, a zatim odmah računa pravougaonik anotacije na osnovu TextWidth i TextHeight pri trenutnoj metrici fonta. To znači da font i veličina moraju biti podešeni pre poziva, i ne smeju se menjati između crtanja oznake i postavljanja anotacije, jer se oboje razrešava u istom pozivu
Podrazumevana boja je clBlue. SetRGBHyperlinkColor je menja samo za naredne pozive; ne ažurira retroaktivno anotacije koje su već zapisane. Ako vam trebaju različite boje za različite grupe veza na istoj stranici, pozovite SetRGBHyperlinkColor pre svake grupe i vratite je nakon toga
Evo minimalnog dokumenta koji zapisuje tri veze u dve različite boje:
procedure CreateLinkedReport(const FileName: string);
var
Pdf: THotPDF;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := FileName;
Pdf.BeginDoc;
Pdf.CurrentPage.SetFont('Arial', [], 11);
// Podrazumevana plava za informativne veze
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');
// Crvena za akcionu vezu
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); // vraćanje podrazumevane vrednosti
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;
Zamka sa koordinatama
HotPDF koristi koordinatni početak u donjem levom uglu sa Y koje raste naviše, u tačkama (1/72 inča). A4 stranica je 595 x 842 tačaka; US Letter stranica je 612 x 792 tačaka. Y=750 se nalazi blizu vrha A4 stranice, a Y=50 bi bilo blizu donje margine. Svako ko dolazi iz grafike za ekran ili iz HTML-a pretpostavlja suprotno i postavlja prvu liniju veze pravo van vidljive oblasti
Pravougaonik anotacije koji PrintHyperlink računa koristi isti koordinatni sistem. Ako kasnije rotirate stranicu, skalirate je, ili promenite veličinu stranice bez preračunavanja vaših X/Y vrednosti, vidljivi tekst i klikabilni pravougaonik će se razdvojiti. Veza "radi" u smislu da klik negde blizu teksta pokreće URL, ali aktivna zona više ne odgovara onome što čitalac vidi. Testirajte na stvarnoj veličini stranice i nivou zumiranja koji isporučujete, ne samo na razvojnom računaru pri 100%
Jedan slučaj u kom je razdvajanje zagarantovano: ako pozovete PrintHyperlink sa koordinatama odgovarajućim za A4 stranicu, a zatim pređete na prilagođenu stranicu uskog formata bez podešavanja X/Y vrednosti, anotacija može u potpunosti završiti van stranice. Objekat anotacije se i dalje zapisuje u PDF; većina pregledača ga tiho odseca, pa veza jednostavno nestaje bez ikakve greške
Tekst oznake naspram ciljnog URL-a
Argumenti Text i Link su nezavisni. Možete nacrtati "Download invoice PDF" dok je cilj potpuno kvalifikovan HTTPS URL sa parametrima upita. To razdvajanje je namerno; vidljiva oznaka treba da bude čitljiva za čoveka, a URL može biti dug ili dinamički generisan
Problem nastaje kada je oznaka sam sirovi URL, naročito dugačak. Ako se URL vizuelno prelama u dva reda, ali je pravougaonik anotacije izračunat za jednorednu nisku, klikabilan je samo prvi red. PrintHyperlink ne rukuje višerednim tokom teksta; držite oznaku dovoljno kratkom da stane u jedan red pri trenutnoj veličini fonta i širini stranice, koristite kratku opisnu oznaku sa punim URL-om kao ciljem, ili primenite zaobilazno rešenje po redu prikazano u sledećem odeljku
Za dokumente koji će biti arhivirani ili distribuirani bez aktivne internet veze, razmotrite i da li bi sam URL trebalo da se pojavi u odštampanom obliku negde u telu dokumenta, ne samo kao metapodaci anotacije. Čitalac koji štampa PDF na papiru ne dobija ništa od URI anotacije
Zaobilaženje ograničenja sa više redova
Kada oznaka veze zaista mora da se protegne kroz više od jednog reda — dugačak URL ispisan doslovno, ili prelomljena rečenica koja treba da bude klikabilna od početka do kraja — rešenje je prestati je tretirati kao jednu vezu i tretirati je kao jednu vezu po redu. Svaki poziv PrintHyperlink računa svoj pravougaonik na osnovu teksta koji crta, tako da nekoliko poziva koji dele isti cilj Link proizvodi nekoliko ispravno dimenzionisanih anotacija koje sve otvaraju isti URL. Čitalac ne može da primeti razliku; svaki red reaguje na klik
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;
// Upotreba: prelomite oznaku na mestima gde je vaš raspored prelama
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');
Deljenje niske je vaša odgovornost: prelomite je na istim mestima gde bi se vizuelno prelamala pri trenutnom fontu i širini kolone, koristeći TextWidth za testiranje svakog kandidata za red. Alternativa je da sami nacrtate prelomljeni tekst pomoću običnih poziva TextOut, a zatim postavite po jedan pravougaonik AddURILink preko svakog reda — bolji put kada je tekst već proizveden vašom sopstvenom logikom za prelamanje teksta, što nas dovodi do te funkcije
AddURILink: klikabilne oblasti preko bilo čega što ste nacrtali
PrintHyperlink je pogodan omotač: crta sopstvenu oznaku i izvodi pravougaonik iz metrike te oznake. AddURILink je direktno izložena polovina nižeg nivoa:
function AddURILink(Rectangle: TRect; const URL: AnsiString;
const Description: AnsiString = ''): THPDFDictionaryObject;
Ona zapisuje samo anotaciju — ne crta se tekst i ne menja se boja. Rectangle se tumači u istom koordinatnom prostoru kao i vaši pozivi za crtanje, tako da možete ponovo upotrebiti tačne X/Y vrednosti koje ste prosledili TextOut-u ili pozivu za sliku. To je čini pravim alatom kad god vidljivi sadržaj već postoji: aktivna zona na slici, ćelija tabele, blok teksta nacrtan ranije, ili jedan red prelomljenog pasusa kao u zaobilaznom rešenju iznad. Anotacija nosi obrub širine nula, tako da se ništa vidljivo ne menja; klikabilna oblast je tačno pravougaonik koji odredite
Funkcija vraća rečnik anotacije kao THPDFDictionaryObject. Većina pozivalaca odbacuje rezultat, ali njegovo zadržavanje omogućava da prilagodite unose anotacije pre nego što se dokument zapiše
Dva detalja usklađenosti su ugrađena. U PDF/A režimima, zastavica za štampanje anotacije se postavlja onako kako to ti standardi zahtevaju. Pod PDFUACompliance parametar Description mora biti neprazna niska — ona postaje unos /Contents anotacije, što je ono što tehnologija za pomoć osobama sa invaliditetom najavljuje za vezu — i poziv izaziva izuzetak umesto da tiho emituje neusklađen fajl. PrintHyperlink prethodi tom pravilu i ne prilaže opis, pa za PDF/UA izlaz nacrtajte oznaku pomoću TextOut i postavite anotaciju pomoću AddURILink plus smislen opis
Pravilo za odlučivanje je jednostavno: koristite PrintHyperlink kada je veza kratak komad teksta koji još niste nacrtali; koristite AddURILink kada je klikabilna oblast definisana sadržajem koji sami crtate ili merite
Interna navigacija pomoću AddGoToLink
Spoljni URL-ovi su samo polovina onoga što anotacije veza rade. Druga polovina je navigacija unutar dokumenta — sadržaj koji skače na poglavlja, unakrsne reference između odeljaka. HotPDF ovo izlaže kroz AddGoToLink:
procedure AddGoToLink(Rectangle: TRect; TargetPageIndex: Integer;
YPos: Single = -1; const Description: AnsiString = '');
Tri semantike vredi precizno navesti, jer se nijedna ne može pogoditi iz potpisa. TargetPageIndex je indeksiran od nule: prva stranica dokumenta je stranica 0, što odgovara CurrentPageNumber. Ciljna stranica mora već postojati u trenutku poziva; ako je indeks van opsega, procedura se vraća bez dodavanja anotacije — bez izuzetka, bez veze, bez upozorenja. Za sadržaj koji pokazuje unapred, prvo napravite sve stranice, a zatim se vratite i dodajte veze
YPos bira vertikalnu poziciju na ciljnoj stranici, u istom koordinatnom prostoru kao i vaši pozivi za crtanje. Podrazumevana vrednost -1 (bilo koja negativna vrednost) zapisuje nultu odredišnu koordinatu, govoreći pregledaču da zadrži svoju trenutnu vertikalnu poziciju kada sleti na ciljnu stranicu. Prosledite nenegativnu vrednost i pregledač će skrolovati tako da ta pozicija bude na vrhu prozora — koristite Y koordinatu naslova na koji vodite. Zumiranje ostaje uvek nepromenjeno. Kao i kod AddURILink, Description mora biti neprazan pod PDFUACompliance i postaje alternativni tekst veze
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; // stranica 0 postaje stranica sadržaja
// Prvo napravite stranice poglavlja kako bi ciljevi veza postojali
for I := 0 to High(Chapters) do
begin
Pdf.AddPage; // stranice 1..3
Pdf.CurrentPage.SetFont('Arial', [fsBold], 14);
Pdf.CurrentPage.TextOut(50, 780, 0, Chapters[I]);
end;
// Vratite se na stranicu 0 i nacrtajte unose sadržaja sa njihovim vezama
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), // pokriva unos sa razmakom
I + 1, // indeksirano od nule: poglavlja su stranice 1..3
780, // sleti sa naslovom na vrhu
AnsiString('Go to ' + Chapters[I]));
Y := Y - 25;
end;
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;
Svaki unos dobija pravougaonik širi od teksta tako da ceo red reaguje na pokazivač, i svaka veza sleće sa naslovom poglavlja (nacrtanim na Y=780) na vrhu prozora. Ako kasnije umetnete stranicu pre poglavlja, svaki TargetPageIndex se pomera za jedan; računajte indekse iz vaše petlje za pravljenje stranica umesto da ih tvrdo kodirate
Kompletan primer generisanja dokumenta
Šablon ispod pokazuje realističniji scenario: generisanje kratkog izveštaja sa zaglavljem, telom teksta, i redom veza u podnožju, sve iz koda umesto sa forme sa TEdit poljima:
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;
// Zaglavlje
Pdf.CurrentPage.SetFont('Arial', [fsBold], 16);
Pdf.CurrentPage.TextOut(50, 750, 0, WideString(ProductName));
// Rezervisano mesto za pasus tela
Pdf.CurrentPage.SetFont('Arial', [], 11);
Pdf.CurrentPage.TextOut(50, 710, 0, 'See the links below for full documentation.');
// Veze u podnožju
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;
Primetite da se SetFont poziva pre svake grupe poziva za tekst. Font se ne prenosi preko AddPage, i ako zaboravite da ga podesite pre PrintHyperlink-a na novoj stranici, pravougaonik anotacije će biti izračunat prema tome kakva god da je podrazumevana metrika stranice, što se može razlikovati od onoga što očekujete
Gde se rukovanje anotacijama razlikuje među pregledačima
PDF URI anotacije su definisane u ISO 32000-1 §12.6.4.7, i svaki usaglašeni pregledač bi trebalo da ih poštuje. U praksi, nekoliko ponašanja se razlikuje po pregledaču. Adobe Acrobat prikazuje bezbednosni upit pri prvom kliku za URL-ove koji nisu na listi poverenih domena; mnogi pregledači i lagani čitači to ne rade. Neki poslovni PDF pregledači u zaključanim okruženjima potpuno onemogućavaju URI anotacije po politici, tako da klik ne radi ništa, bez vidljive greške. Mobilne PDF aplikacije se razlikuju po tome da li otvaraju veze unutar veb prikaza aplikacije ili prosleđuju sistemskom pregledaču
Nijedno od ovoga nije greška koju možete ispraviti sa strane generisanja; to su odluke politike pregledača. Ono što možete da uradite je da pišete oznake veza koje čine URL vidljivim i u telu dokumenta, tako da čitalac u ograničenom okruženju i dalje može ručno da kopira adresu. Anotacija je pogodnost; tekst je rezervni plan
Još jedan detalj vredan pažnje: PDF URI anotacije podrazumevano ne nose nikakvo vizuelno podvlačenje. Podvlačenje koje vidite u većini pregledača crta sam pregledač na osnovu tipa anotacije, ne glif u toku sadržaja. Ako vam treba fizičko podvlačenje koje preživi štampanje u neinteraktivni renderer ili konverziju PDF-u-sliku, nacrtajte ga eksplicitno pomoću LineTo i Stroke na odgovarajućem Y pomeraju ispod bazne linije teksta. To je odvojena operacija crtanja, nešto što PrintHyperlink ne radi umesto vas
API za hiperveze prikazan ovde je deo HotPDF Delphi Component-a za Delphi i C++Builder