PDF hiperveze su URI anotacije: pravokutnik koji pokriva neko područje stranice koji, kada se klikne, govori pregledniku da otvori URL. Anotacija i tekst ispod nje potpuno su neovisni objekti. HotPDF-ov PrintHyperlink spaja oboje u jedan poziv, crtajući tekst i računajući pravokutnik anotacije iz metrike iscrtanog teksta. Ta pogodnost skriva detalj koji vrijedi razumjeti prije nego što napišete produkcijski kod
Kako PrintHyperlink radi
PrintHyperlink nalazi se na THPDFPage i prima četiri argumenta: X i Y koordinate (u točkama, ishodiste dolje lijevo, Y se povećava prema gore), niz znakova (label string) koji se iscrtava i ciljani URL. Interno on poziva TextOut u trenutnoj boji hiperveze, a zatim odmah računa pravokutnik anotacije iz TextWidth i TextHeight pri trenutnoj metrici fonta. To znači da se font i veličina moraju postaviti prije poziva i ne smiju se mijenjati između crtanja oznake i postavljanja anotacije, jer se oboje rješava u istom pozivu
Zadana boja je clBlue. SetRGBHyperlinkColor mijenja je samo za naknadne pozive; on ne ažurira retroaktivno već napisane anotacije. Ako su vam potrebne različite boje za različite grupe veza na istoj stranici, pozovite SetRGBHyperlinkColor prije svake grupe i ponovno ga postavite (resetirajte) nakon toga
Ovdje je minimalan dokument koji ispisuje tri veze s dvije 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);
// 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;
Zamka s koordinatama
HotPDF koristi ishodište u donjem lijevom kutu s Y koji raste prema gore, u točkama (1/72 inča). A4 stranica je 595 x 842 točaka (pt); US Letter stranica je 612 x 792 točaka. Y=750 nalazi se blizu vrha A4 stranice, a Y=50 bi bilo blizu donje margine. Svatko tko dolazi iz svijeta grafike za ekrane ili HTML-a pretpostavlja suprotno te postavlja prvu liniju veze ravno izvan vidljivog područja
Pravokutnik anotacije koji PrintHyperlink izračunava koristi isti koordinatni sustav. Ako kasnije rotirate stranicu, promijenite joj veličinu (scale) ili veličinu stranice bez ponovnog izračunavanja vaših X/Y vrijednosti, vidljivi tekst i pravokutnik na koji se može kliknuti odvojit će se jedno od drugog (drift apart). Veza "radi" u smislu da klik negdje blizu teksta pokreće URL, ali aktivna zona (hot zone) više ne odgovara onome što čitatelj vidi. Testirajte na stvarnoj veličini stranice i razini zumiranja koju isporučujete, a ne samo na razvojnom stroju na 100%
Jedan slučaj gdje je odvajanje (drift) zajamčeno: ako pozovete PrintHyperlink s koordinatama primjerenim za A4 stranicu, a zatim se prebacite na prilagođenu stranicu uskog formata bez prilagodbe X/Y vrijednosti, anotacija može završiti potpuno izvan stranice. Objekt anotacije i dalje se zapisuje u PDF; većina preglednika ga tiho odreže (clip), pa veza jednostavno nestane bez ikakve pogreške
Tekst oznake u odnosu na ciljani URL
Argumenti Text i Link su neovisni. Možete nacrtati "Preuzmi PDF računa" dok je cilj potpuno kvalificiran HTTPS URL s parametrima upita (query parameters). To razdvajanje je namjerno; vidljiva oznaka bi trebala biti čitljiva ljudima, a URL može biti dug ili dinamički generiran
Ono što stvara probleme jest kada je oznaka sam sirovi URL, posebno onaj dugački. Ako se URL vizualno prelama preko dvije linije (wraps), ali je pravokutnik anotacije izračunat za jednolinijski niz, samo se na prvu liniju može kliknuti. PrintHyperlink ne upravlja višelinijskim tokom (multi-line flow); neka oznaka bude dovoljno kratka da stane na jednu liniju pri trenutnoj veličini fonta i širini stranice, ili pak upotrijebite kratku opisnu oznaku s punim URL-om kao ciljem
Za dokumente koji će biti arhivirani ili distribuirani bez aktivne internetske veze, također razmotrite treba li se sam URL pojaviti u tiskanom obliku negdje u tijelu dokumenta, a ne samo kao metapodaci anotacije. Čitatelj koji ispisuje PDF na papir ne dobiva ništa od URI anotacije
Zaobilaženje ograničenja višerednog teksta
Višerednu poveznicu treba ispisati redak po redak i za svaki redak zasebno izračunati pravokutnik anotacije jer PrintHyperlink radi s jednom tekstualnom oznakom
AddURILink: klikabilna područja preko svega što ste nacrtali
AddURILink postavlja klikabilno pravokutno područje preko već nacrtanog sadržaja, pa poveznica može pokrivati oblik, sliku ili više redaka bez promjene samog crteža
Interna navigacija pomoću AddGoToLink
AddGoToLink vodi na stranicu i koordinatu unutar istog dokumenta, što je prikladno za sadržaj i interne prečace bez otvaranja vanjskog URL-a
Potpuni primjer generiranja dokumenta
Uzorak u nastavku prikazuje realističniji scenarij: generiranje kratkog izvješća s odjeljkom zaglavlja, tekstom tijela i redom poveznica u podnožju, sve iz koda umjesto s obrasca s TEdit poljima:
Imajte na umu da se SetFont poziva prije svake grupe tekstualnih poziva. Font se ne zadržava preko AddPage, i ako ga zaboravite postaviti prije PrintHyperlink na novoj stranici, pravokutnik anotacije izračunat će se u odnosu na bilo kakve zadane metrike stranice, koje se mogu razlikovati od onoga što očekujete
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');
Gdje se rukovanje anotacijama razlikuje ovisno o pregledniku
PDF URI anotacije definirane su u ISO 32000-1 §12.6.4.7, i svaki usklađeni preglednik trebao bi ih slijediti. U praksi, nekoliko ponašanja razlikuje se ovisno o pregledniku. Adobe Acrobat prikazuje sigurnosni upit (security prompt) pri prvom kliku na URL-ove koji nisu na popisu pouzdanih domena; mnogi web preglednici i lagani (lightweight) čitači to ne čine. Neki poslovni (enterprise) preglednici PDF-a u zaključanim (locked-down) okruženjima u potpunosti onemogućuju URI anotacije prema politici, pa klik ne radi ništa, bez vidljive pogreške. Mobilne PDF aplikacije razlikuju se u tome hoće li otvoriti veze unutar vlastitog web prikaza ili ih proslijediti pregledniku sustava
Ništa od navedenog nisu greške koje možete popraviti sa strane generiranja; to su odluke politika preglednika. Ono što možete učiniti je napisati oznake veza koje URL čine vidljivim i u tijelu dokumenta, tako da čitatelj u ograničenom okruženju još uvijek može ručno kopirati adresu. Anotacija predstavlja pogodnost; tekst je opcija u nuždi (fallback)
Još jedan detalj koji vrijedi znati: PDF URI anotacije po defaultu ne nose vizualno podcrtavanje. Podvlaku koju vidite u većini preglednika crta sam preglednik na temelju vrste anotacije, a ne glifa u struji sadržaja. Ako trebate fizičko podcrtavanje koje preživljava ispis na neinteraktivni mehanizam za iscrtavanje (renderer) ili konverziju iz PDF-a u sliku, iscrtajte ga eksplicitno uz LineTo i Stroke na odgovarajućem Y odmaku ispod osnovne linije teksta. To je zasebna operacija crtanja, a ne nešto što PrintHyperlink radi umjesto vas
Ovdje prikazani hipertekstualni API dio je HotPDF komponente za Delphi i C++Builder
function AddURILink(Rectangle: TRect; const URL: AnsiString;
const Description: AnsiString = ''): THPDFDictionaryObject;
procedure AddGoToLink(Rectangle: TRect; TargetPageIndex: Integer;
YPos: Single = -1; const Description: AnsiString = '');
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;
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;