PDF hyperlinks are URI annotations: a rectangle covering some page area that, when clicked, tells the viewer to open a URL. The annotation and the text beneath it are completely independent objects. HotPDF's PrintHyperlink bundles both into one call, drawing the text and computing the annotation rectangle from the rendered text metrics. That convenience hides a detail worth understanding before you write production code.
How PrintHyperlink works
PrintHyperlink lives on THPDFPage and takes four arguments: X and Y coordinates (in points, bottom-left origin, Y increasing upward), the label string to draw, and the URL target. Internally it calls TextOut in the current hyperlink color, then immediately computes the annotation rectangle from TextWidth and TextHeight at the current font metrics. That means font and size have to be set before the call, and they must not change between drawing the label and placing the annotation, because both are resolved in the same call.
The default color is clBlue. SetRGBHyperlinkColor changes it for subsequent calls only; it does not retroactively update annotations already written. If you need different colors for different link groups on the same page, call SetRGBHyperlinkColor before each group and reset it afterwards.
Here is a minimal document that writes three links with two different colors:
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/documentation.html');
// Red for the action link
Pdf.CurrentPage.SetRGBHyperlinkColor(clRed);
Pdf.CurrentPage.PrintHyperlink(50, 660, 'Purchase license', 'https://www.loslab.com/en-us/order/');
Pdf.CurrentPage.SetRGBHyperlinkColor(clBlue); // restore default
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;
The coordinate trap
HotPDF uses a bottom-left origin with Y growing upward, in points (1/72 inch). An A4 page is 595 x 842 pt; a US Letter page is 612 x 792 pt. Y=750 sits near the top of an A4 page, and Y=50 would be near the bottom margin. Anyone coming from screen graphics or HTML assumes the opposite and places the first link line straight off the visible area.
The annotation rectangle that PrintHyperlink computes uses the same coordinate system. If you later rotate the page, scale it, or change the page size without recalculating your X/Y values, the visible text and the clickable rectangle will drift apart. The link "works" in the sense that clicking somewhere near the text triggers the URL, but the hot zone no longer matches what the reader sees. Test on the actual page size and zoom level you ship, not just on the development machine at 100%.
One case where the drift is guaranteed: if you call PrintHyperlink with coordinates appropriate for an A4 page and then switch to a custom narrow-format page without adjusting the X/Y values, the annotation can end up off the page entirely. The annotation object is still written into the PDF; most viewers clip it silently, so the link simply disappears without any error.
Label text versus URL target
The Text and Link arguments are independent. You can draw "Download invoice PDF" while the target is a fully qualified HTTPS URL with query parameters. That separation is deliberate; the visible label should be human-readable and the URL can be long or generated dynamically.
What creates problems is when the label is the raw URL itself, especially a long one. If the URL wraps visually across two lines but the annotation rectangle was computed for a single-line string, only the first line is clickable. PrintHyperlink does not handle multi-line flow; keep the label short enough to fit on one line at the current font size and page width, or use a short descriptive label with the full URL as the target.
For documents that will be archived or distributed without an active internet connection, also consider whether the URL itself should appear in printed form somewhere in the document body, not just as annotation metadata. A reader printing the PDF on paper gets nothing from a URI annotation.
A complete document-generation example
The pattern below shows a more realistic scenario: generating a short report with a header section, body text, and a footer row of links, all from code rather than from a form with TEdit fields:
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;
Notice that SetFont is called before each group of text calls. The font does not persist across AddPage, and if you forget to set it before PrintHyperlink on a new page, the annotation rectangle will be computed against whatever the page's default metrics are, which may differ from what you expect.
Where annotation handling varies across viewers
PDF URI annotations are defined in ISO 32000-1 §12.6.4.7, and every conforming viewer should follow them. In practice, a few behaviors differ by viewer. Adobe Acrobat shows a security prompt on first click for URLs not in the trusted domains list; many browsers and lightweight readers do not. Some enterprise PDF viewers in locked-down environments disable URI annotations entirely by policy, so a click does nothing, with no visible error. Mobile PDF apps vary in whether they open links inside the app's web view or hand off to the system browser.
None of these are bugs you can fix from the generation side; they are viewer policy decisions. What you can do is write link labels that make the URL visible in the document body as well, so a reader in a restricted environment can still copy the address manually. The annotation is the convenience; the text is the fallback.
One further detail worth knowing: PDF URI annotations do not carry any visual underline by default. The underline you see in most viewers is drawn by the viewer itself based on the annotation type, not by a glyph in the content stream. If you need a physical underline that survives printing to a non-interactive renderer or PDF-to-image conversion, draw it explicitly with LineTo and Stroke at the appropriate Y offset below the text baseline. That is a separate drawing operation, not something PrintHyperlink handles for you.
The hyperlink API shown here is part of the HotPDF Component for Delphi and C++Builder.