Hiperpovezave PDF so pripisi URI: pravokotnik, ki pokriva del površine strani in ob kliku pregledovalniku pove, naj odpre URL. Pripis in besedilo pod njim sta povsem neodvisna objekta. HotPDF-jev PrintHyperlink oboje zapakira v en klic: nariše besedilo in pravokotnik pripisa izračuna iz mer upodobljenega besedila. Ta udobnost skriva podrobnost, ki jo je vredno razumeti, preden napišete produkcijsko kodo. Prav tako to ni celotna zgodba: AddURILink klikljivo področje postavi nad vsebino, ki ste jo narisali sami, AddGoToLink pa poskrbi za notranjo navigacijo — oboje je obravnavano spodaj
Kako deluje PrintHyperlink
PrintHyperlink živi na THPDFPage in vzame štiri argumente: koordinati X in Y (v točkah, z izhodiščem v spodnjem levem kotu in Y, ki raste navzgor), niz oznake za izris in ciljni URL. Interno pokliče TextOut v trenutni barvi hiperpovezav, nato pa takoj izračuna pravokotnik pripisa iz TextWidth in TextHeight pri trenutnih merah pisave. To pomeni, da morata biti pisava in velikost nastavljeni pred klicem in se med izrisom oznake ter postavitvijo pripisa ne smeta spremeniti, saj se oboje razreši v istem klicu
Privzeta barva je clBlue. SetRGBHyperlinkColor jo spremeni le za naslednje klice; že zapisanih pripisov za nazaj ne posodobi. Če za različne skupine povezav na isti strani potrebujete različne barve, SetRGBHyperlinkColor pokličite pred vsako skupino in ga nato ponastavite
Spodaj je minimalen dokument, ki zapiše tri povezave v dveh različnih barvah:
procedure CreateLinkedReport(const FileName: string);
var
Pdf: THotPDF;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := FileName;
Pdf.BeginDoc;
Pdf.CurrentPage.SetFont('Arial', [], 11);
// Privzeta modra za informativne povezave
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');
// Rdeča za akcijsko povezavo
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); // obnovi privzeto
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;
Past s koordinatami
HotPDF uporablja izhodišče v spodnjem levem kotu z Y, ki raste navzgor, v točkah (1/72 palca). Stran A4 meri 595 x 842 pt; stran US Letter meri 612 x 792 pt. Y=750 leži blizu vrha strani A4, Y=50 pa bi bil blizu spodnjega roba. Kdor prihaja iz zaslonske grafike ali HTML, predpostavi nasprotno in prvo vrstico povezav postavi naravnost zunaj vidnega območja
Pravokotnik pripisa, ki ga izračuna PrintHyperlink, uporablja isti koordinatni sistem. Če stran pozneje zasukate, ji spremenite merilo ali velikost, ne da bi preračunali svoje vrednosti X/Y, se bosta vidno besedilo in klikljivi pravokotnik razšla. Povezava "deluje" v tem smislu, da klik nekje blizu besedila sproži URL, vroče območje pa ne ustreza več temu, kar bralec vidi. Preizkusite na dejanski velikosti strani in stopnji povečave, ki ju odpremljate, ne le na razvijalskem stroju pri 100 %
En primer, ko je razhajanje zagotovljeno: če PrintHyperlink pokličete s koordinatami, primernimi za stran A4, nato pa preklopite na ozko stran po meri, ne da bi prilagodili vrednosti X/Y, lahko pripis pristane povsem zunaj strani. Objekt pripisa je v PDF vseeno zapisan; večina pregledovalnikov ga tiho poreže, zato povezava preprosto izgine brez kakršne koli napake
Besedilo oznake proti cilju URL
Argumenta Text in Link sta neodvisna. Izrišete lahko "Prenesi PDF računa", medtem ko je cilj polno kvalificiran URL HTTPS s parametri poizvedbe. Ta ločitev je namerna; vidna oznaka naj bo berljiva za človeka, URL pa je lahko dolg ali ustvarjen dinamično
Težave nastanejo, kadar je oznaka kar sam URL, še posebej dolg. Če se URL vizualno prelomi čez dve vrstici, pravokotnik pripisa pa je bil izračunan za enovrstični niz, je klikljiva le prva vrstica. PrintHyperlink večvrstičnega toka ne obvlada; oznako ohranite dovolj kratko, da se pri trenutni velikosti pisave in širini strani zmesti v eno vrstico, uporabite kratko opisno oznako s polnim URL kot ciljem ali pa uporabite rešitev po vrsticah, prikazano v naslednjem razdelku
Pri dokumentih, ki bodo arhivirani ali razdeljeni brez dejavne internetne povezave, razmislite tudi, ali naj se URL v natisnjeni obliki pojavi nekje v telesu dokumenta in ne le kot metapodatek pripisa. Bralec, ki PDF natisne na papir, od pripisa URI nima ničesar
Kako zaobiti omejitev ene vrstice
Kadar mora oznaka povezave res zajeti več kot eno vrstico — dolg URL, natisnjen dobesedno, ali prelomljen stavek, ki naj bo klikljiv od začetka do konca — je popravek ta, da jo nehate obravnavati kot eno povezavo in jo obravnavate kot eno povezavo na vrstico. Vsak klic PrintHyperlink svoj pravokotnik izračuna iz besedila, ki ga nariše, zato več klicev z istim ciljem Link da več pravilno dimenzioniranih pripisov, ki vsi odprejo isti URL. Bralec razlike ne opazi; vsaka vrstica se odzove 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;
// Raba: oznako prelomite na mestih, kjer jo prelomi vaša postavitev
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 niza je vaša odgovornost: prelomite ga na istih mestih, kjer bi se vizualno prelomil pri trenutni pisavi in širini stolpca, za preizkus vsake kandidatne vrstice pa uporabite TextWidth. Druga možnost je, da prelomljeno besedilo narišete sami z navadnimi klici TextOut in nato čez vsako vrstico položite en pravokotnik AddURILink — to je boljša pot, kadar besedilo že ustvarja vaša lastna logika preloma, kar nas pripelje do te funkcije
AddURILink: klikljiva območja nad čimer koli, kar ste narisali
PrintHyperlink je ovoj za udobje: nariše svojo oznako in pravokotnik izpelje iz mer te oznake. AddURILink je neposredno razkrita nižja polovica:
function AddURILink(Rectangle: TRect; const URL: AnsiString;
const Description: AnsiString = ''): THPDFDictionaryObject;
Zapiše samo pripis — nobeno besedilo ni izrisano in nobena barva se ne spremeni. Rectangle se tolmači v istem koordinatnem prostoru kot vaši risalni klici, zato lahko znova uporabite prav tiste vrednosti X/Y, ki ste jih podali klicu TextOut ali klicu za sliko. Zato je to pravo orodje vselej, kadar vidna vsebina že obstaja: vroča točka na sliki, celica tabele, blok besedila, narisan prej, ali ena vrstica prelomljenega odstavka kot v zgornji rešitvi. Pripis nosi obrobo z ničelno širino, zato se vidno nič ne spremeni; klikljivo območje je natanko pravokotnik, ki ga navedete
Funkcija vrne slovar pripisa kot THPDFDictionaryObject. Večina klicateljev izid zavrže, a če ga obdržite, lahko vnose pripisa prilagodite, preden se dokument zapiše
Vgrajeni sta dve podrobnosti glede skladnosti. V načinih PDF/A je zastavica za tiskanje pripisa nastavljena tako, kot ti standardi zahtevajo. Pod PDFUACompliance mora biti parameter Description neprazen niz — postane vnos /Contents pripisa, torej tisto, kar pomožna tehnologija za povezavo naznani — klic pa sproži izjemo, namesto da bi tiho izpisal neskladno datoteko. PrintHyperlink je starejši od tega pravila in ne pripne nobenega opisa, zato za izhod PDF/UA oznako narišite s TextOut in pripis postavite z AddURILink ter smiselnim opisom
Odločitveno pravilo je preprosto: PrintHyperlink uporabite, kadar je povezava kratek kos besedila, ki ga še niste narisali; AddURILink pa, kadar klikljivo območje določa vsebina, ki jo narišete ali izmerite sami
Notranja navigacija z AddGoToLink
Zunanji URL-ji so le polovica tega, kar pripisi povezav počnejo. Druga polovica je navigacija znotraj dokumenta — kazalo, ki skoči na poglavja, navzkrižni sklici med razdelki. HotPDF to razkriva prek AddGoToLink:
procedure AddGoToLink(Rectangle: TRect; TargetPageIndex: Integer;
YPos: Single = -1; const Description: AnsiString = '');
Tri pomenske podrobnosti je vredno povedati natančno, saj nobene ni mogoče uganiti iz podpisa. TargetPageIndex se šteje od nič: prva stran dokumenta je stran 0, kar se ujema s CurrentPageNumber. Ciljna stran mora ob klicu že obstajati; če je indeks zunaj obsega, se postopek vrne, ne da bi dodal pripis — brez izjeme, brez povezave, brez opozorila. Za kazalo, ki kaže naprej, najprej ustvarite vse strani, nato pa se vrnite in dodajte povezave
YPos izbere navpični položaj na ciljni strani, v istem koordinatnem prostoru kot vaši risalni klici. Privzeta vrednost -1 (katera koli negativna vrednost) zapiše ničelno ciljno koordinato in pregledovalniku pove, naj ob pristanku na ciljni strani ohrani trenutni navpični položaj. Podajte nenegativno vrednost in pregledovalnik se pomakne tako, da bo ta položaj na vrhu okna — uporabite koordinato Y naslova, na katerega se povezujete. Povečava ostane vedno nespremenjena. Tako kot pri AddURILink mora biti Description pod PDFUACompliance neprazen in postane nadomestno besedilo povezave
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; // stran 0 postane stran s kazalom
// Najprej ustvarite strani poglavij, da cilji povezav obstajajo
for I := 0 to High(Chapters) do
begin
Pdf.AddPage; // strani 1..3
Pdf.CurrentPage.SetFont('Arial', [fsBold], 14);
Pdf.CurrentPage.TextOut(50, 780, 0, Chapters[I]);
end;
// Vrnite se na stran 0 in izrišite vnose kazala z njihovimi povezavami
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), // pokrije vnos z odmikom
I + 1, // šteto od nič: poglavja so strani 1..3
780, // pristani z naslovom na vrhu
AnsiString('Go to ' + Chapters[I]));
Y := Y - 25;
end;
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;
Vsak vnos dobi pravokotnik, širši od besedila, tako da se na kazalec odzove cela vrstica, vsaka povezava pa pristane z naslovom poglavja (narisanim pri Y=780) na vrhu okna. Če pozneje vstavite stran pred poglavja, se vsak TargetPageIndex premakne za ena; indekse računajte iz svoje zanke za ustvarjanje strani, namesto da bi jih zapisali trdo
Celovit primer generiranja dokumenta
Spodnji vzorec kaže bolj resničen scenarij: generiranje kratkega poročila z razdelkom glave, besedilom telesa in nožno vrstico povezav, vse iz kode in ne iz obrazca s polji 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;
// Glava
Pdf.CurrentPage.SetFont('Arial', [fsBold], 16);
Pdf.CurrentPage.TextOut(50, 750, 0, WideString(ProductName));
// Nadomestek za odstavek telesa
Pdf.CurrentPage.SetFont('Arial', [], 11);
Pdf.CurrentPage.TextOut(50, 710, 0, 'See the links below for full documentation.');
// Povezave v nogi
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;
Opazite, da je SetFont poklican pred vsako skupino besedilnih klicev. Pisava ne obstane čez AddPage, in če jo pozabite nastaviti pred PrintHyperlink na novi strani, bo pravokotnik pripisa izračunan glede na privzete mere te strani, ki se lahko razlikujejo od tega, kar pričakujete
Kje se obravnava pripisov med pregledovalniki razlikuje
Pripisi URI v PDF so definirani v ISO 32000-1 §12.6.4.7 in vsak skladen pregledovalnik bi jim moral slediti. V praksi se nekaj vedenj med pregledovalniki razlikuje. Adobe Acrobat ob prvem kliku pokaže varnostni poziv za URL-je, ki niso na seznamu zaupanja vrednih domen; mnogi brskalniki in lahki bralniki tega ne počnejo. Nekateri poslovni pregledovalniki PDF v zaklenjenih okoljih pripise URI po pravilniku povsem onemogočijo, zato klik ne stori nič, brez vidne napake. Mobilne aplikacije za PDF se razlikujejo po tem, ali povezave odprejo v spletnem pogledu znotraj aplikacije ali jih predajo sistemskemu brskalniku
Nič od tega ni napaka, ki bi jo lahko popravili na strani generiranja; gre za pravilniške odločitve pregledovalnikov. Kar lahko storite, je, da napišete oznake povezav, ki URL naredijo viden tudi v telesu dokumenta, tako da lahko bralec v omejenem okolju naslov še vedno prepiše ročno. Pripis je udobje; besedilo je zasilna rešitev
Še ena podrobnost, ki jo je vredno poznati: pripisi URI v PDF privzeto ne nosijo nobenega vidnega podčrtaja. Podčrtaj, ki ga vidite v večini pregledovalnikov, nariše pregledovalnik sam glede na vrsto pripisa in ne kak znak v toku vsebine. Če potrebujete fizičen podčrtaj, ki preživi tiskanje v neinteraktivni upodabljalnik ali pretvorbo PDF v sliko, ga izrišite izrecno z LineTo in Stroke pri ustreznem odmiku Y pod osnovnico besedila. To je ločena risalna operacija in ne nekaj, kar bi PrintHyperlink opravil namesto vas
API za hiperpovezave, prikazan tukaj, je del komponente HotPDF Delphi za Delphi in C++Builder