Knowledgebase (2595)
Children categories
A table has only a few seconds to be understood, and the work is done not only by the numbers but by the fonts: a title big enough to stand out, a figure that matters found at a glance, a cancelled line spotted immediately. In Excel every one of those differences is carried by a font object belonging to a single cell -- font name, size, colour, bold, italic, underline, strikethrough; changing a cell touches only that cell. One cell can also hold several fonts at once, and switching a single word to another colour or size lifts it out of the sentence around it. Spire.XLS for JavaScript performs these operations in the browser through WebAssembly; the output file is managed by a virtual file system (VFS), with no backend service required.
This article covers two feature points:
For installation and project setup, see Integrating Spire.XLS for JavaScript in a React Project. The examples below assume Spire.XLS is installed and the WebAssembly module has been initialized.
Set the font formatting of a cell
Every cell carries a font object of its own, and all seven properties -- font name, size, colour, bold, italic, underline and strikethrough -- hang off that object. To see what each one does, start a worksheet of its own, write the property's name and a sample of text row by row, and set the matching property on the cell that holds the sample: a change lands on that cell alone, and the description in column A keeps its own look. The steps are:
- Load the font file into the VFS with
window.spire.FetchFileToVFS. - Create a workbook with
new Workbook()and take the first worksheet withworkbook.Worksheets.get(0). - Write the properties out one per row with
sheet.Range.get: the name into column A and the sample text into column B, rows starting at 1 and going up by 2. - Set the matching format on each row's sample through
FontName,Size,Color,IsBold,IsItalic,UnderlineandIsStrikethroughonRange.Style.Font, taking the font colour fromxlsModule.Color.get_Red()and the underline fromxlsModule.FontUnderlineType.Single. - Set both columns to a width of 25 with
sheet.Columns.get(0).ColumnWidthandsheet.Columns.get(1).ColumnWidth. - Save the workbook with
workbook.SaveToFileand dispose of it withworkbook.Disposeto free resources.
The full code example below shows how to set font formatting for cells in React:
function App() {
const setCellFont = async () => {
// get the Spire.XLS WASM module
const xlsModule = window.wasmModule?.spirexls;
// check that the module is ready
if (!xlsModule) {
alert('Spire.Xls is not ready yet');
return;
}
// load the font file into the VFS
await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
// create a new workbook and take the first worksheet
const workbook = new xlsModule.Workbook();
const sheet = workbook.Worksheets.get(0);
// set the font name
let row = 1;
sheet.Range.get(`A${row}`).Value = 'Font name';
sheet.Range.get(`B${row}`).Value = 'Arial';
sheet.Range.get(`B${row}`).Style.Font.FontName = 'Arial';
// set the font size
row += 2;
sheet.Range.get(`A${row}`).Value = 'Font size';
sheet.Range.get(`B${row}`).Value = '15';
sheet.Range.get(`B${row}`).Style.Font.Size = 15;
// set the font colour
row += 2;
sheet.Range.get(`A${row}`).Value = 'Font colour';
sheet.Range.get(`B${row}`).Value = 'Red';
sheet.Range.get(`B${row}`).Style.Font.Color = xlsModule.Color.get_Red();
// bold the text
row += 2;
sheet.Range.get(`A${row}`).Value = 'Bold';
sheet.Range.get(`B${row}`).Value = 'Bold';
sheet.Range.get(`B${row}`).Style.Font.IsBold = true;
// italicise the text
row += 2;
sheet.Range.get(`A${row}`).Value = 'Italic';
sheet.Range.get(`B${row}`).Value = 'Italic';
sheet.Range.get(`B${row}`).Style.Font.IsItalic = true;
// underline the text
row += 2;
sheet.Range.get(`A${row}`).Value = 'Underline';
sheet.Range.get(`B${row}`).Value = 'Underline';
sheet.Range.get(`B${row}`).Style.Font.Underline = xlsModule.FontUnderlineType.Single;
// strike through the text
row += 2;
sheet.Range.get(`A${row}`).Value = 'Strikethrough';
sheet.Range.get(`B${row}`).Value = 'Strikethrough';
sheet.Range.get(`B${row}`).Style.Font.IsStrikethrough = true;
// set the column widths
sheet.Columns.get(0).ColumnWidth = 25;
sheet.Columns.get(1).ColumnWidth = 25;
// save the workbook as an Excel file
const outputFileName = 'CellFont.xlsx';
workbook.SaveToFile({ fileName: outputFileName, version: xlsModule.ExcelVersion.Version2016 });
// dispose of the workbook to free resources
workbook.Dispose();
// read the result file from the VFS and start the download
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>Set Cell Font Formatting</h1>
<button id="cell-font" onClick={setCellFont}>Set the font formatting of a cell</button>
</div>
);
}
export default App;
After running, the effect of setting font formatting for individual cells:

Mix several fonts inside one cell
A cell's rich text object lets one run of text be cut at character positions and each piece given a different font, while the sentence still reads as a whole. The way to do it is to create the font objects first and then assign them range by range with SetFont -- each call overwrites one range, so every part of the sentence has to be covered. Ranges count from 0 and include both ends. The steps are:
- Load the font file into the VFS with
window.spire.FetchFileToVFS. - Create a workbook with
new Workbook()and take the first worksheet withworkbook.Worksheets.get(0). - Create two fonts with
workbook.CreateFont(): one for the whole sentence, and one that emphasises the key phrase throughIsBoldandKnownColor. - Take the cell that holds the sentence with
sheet.Range.get('A1'), and write the whole sentence into its rich text object throughRichText.Text. - Work out the start and end of the text that needs the new font within the sentence with
note.indexOf(key)andkey.length, then assign the fonts to the three parts -- before the key phrase, the key phrase itself and after it -- withRichText.SetFont. - Save the workbook with
workbook.SaveToFileand dispose of it withworkbook.Disposeto free resources.
The full code example below shows how to give part of the text in one cell a different font in React:
function App() {
const setRichTextFonts = async () => {
// get the Spire.XLS WASM module
const xlsModule = window.wasmModule?.spirexls;
// check that the module is ready
if (!xlsModule) {
alert('Spire.Xls is not ready yet');
return;
}
// load the font file into the VFS
await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
// create a new workbook and take the first worksheet
const workbook = new xlsModule.Workbook();
const sheet = workbook.Worksheets.get(0);
// create the first font, for the whole sentence
const font1 = workbook.CreateFont();
font1.FontName = 'Arial';
font1.Size = 12;
// create the second font, for the key phrase
const font2 = workbook.CreateFont();
font2.FontName = 'Arial';
font2.Size = 14;
font2.IsBold = true;
font2.KnownColor = xlsModule.ExcelColors.Red;
// take the cell's rich text object and write the whole sentence
const note = 'This quotation is valid for 30 days; contact your sales representative to extend it.';
const key = '30 days';
const richText = sheet.Range.get('A1').RichText;
richText.Text = note;
// locate the character range the key phrase occupies
const start = note.indexOf(key);
const end = start + key.length - 1;
// set the part before the key phrase to the first font
richText.SetFont(0, start - 1, font1);
// set the key phrase itself to the second font
richText.SetFont(start, end, font2);
// keep the part after the key phrase in the first font
richText.SetFont(end + 1, note.length - 1, font1);
// save the workbook as an Excel file
const outputFileName = 'RichTextFont.xlsx';
workbook.SaveToFile({ fileName: outputFileName, version: xlsModule.ExcelVersion.Version2016 });
// dispose of the workbook to free resources
workbook.Dispose();
// read the result file from the VFS and start the download
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>Mix Several Fonts Inside One Cell</h1>
<button id="rich-text-fonts" onClick={setRichTextFonts}>Give part of the text a different font</button>
</div>
);
}
export default App;
After running, the effect of mixing several fonts inside one cell:

FAQ
What value should SetFont's end position take
Reason: Both position arguments of SetFont count from 0 and include the end position itself, so passing a string's length directly lands one character past the end.
Solution: Pass "length minus one" as the end position. To cover a whole run of text, use the sentence's length minus one:
richText.SetFont(0, note.length - 1, font1);
Get a Free License
Spire.XLS for JavaScript offers a 30-day full-featured free trial license with no functional limitations. Apply here to evaluate before purchasing.
An Excel comment usually carries a paragraph of text, explaining a number or adding a note. When the thing to be explained is itself a picture -- a supplier mark, a product photo, a sketch of a part -- text alone will not do. Spire.XLS for JavaScript can fill a comment's background with a picture, so the image appears beside the cell without a separate floating picture object being placed on the sheet. A comment's background is controlled by its fill object, and switching to a solid colour likewise changes only that one object. Spire.XLS for JavaScript performs these operations in the browser through WebAssembly; input and output files are managed by a virtual file system (VFS), with no backend service required.
This article covers two feature points:
For installation and project setup, see Integrating Spire.XLS for JavaScript in a React Project. The examples below assume Spire.XLS is installed and the WebAssembly module has been initialized.
Insert a picture comment into a cell
A comment is attached to a cell; giving one a picture means first obtaining the comment object on the target cell and then filling it. The picture becomes the comment box's background, replacing the default pale-yellow shading, and it is this picture that appears when the mouse moves over the cell or the file is opened. The picture is scaled to the size of the comment box, and the default box is a flat rectangle, so a square picture is stretched out of shape; after filling, the width and height must therefore be set so that the box matches the picture's proportions. Where the box sits on the sheet is decided by a separate pair of position properties, so it does not have to follow the cell border. The steps are:
- Load the font, the picture and the test data file into the VFS.
- Load the workbook with
workbook.LoadFromFile. - Take the cell the comment will be attached to with
sheet.Range.get('A4'). - Create a comment object on the cell with
range.AddComment(). - Fill the comment background with the picture using
comment.Fill.CustomPicture, passing the picture as a stream built bynew xlsModule.Stream('logo.png')and'None'for thenameargument. - Set
comment.Widthandcomment.Heightso the comment box is square, matching the square picture, which keeps the picture from being stretched. - Place the comment box relative to the top-left corner of the worksheet with
comment.Topandcomment.Left, so that it covers the content below without obscuring its own row. - Set
comment.Visibleto show the comment as soon as the file is opened. - Save the workbook with
workbook.SaveToFile. - Dispose of the workbook with
workbook.Disposeto free resources.
The complete code example below shows how to insert a picture comment into an Excel cell in React:
function App() {
const addPictureComment = async () => {
// get the Spire.XLS WASM module
const xlsModule = window.wasmModule?.spirexls;
// check that the module is ready
if (!xlsModule) {
alert('Spire.Xls is not ready yet');
return;
}
// load the font, the picture and the test data file into the VFS
await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
await window.spire.FetchFileToVFS('logo.png', '', `${process.env.PUBLIC_URL}data/`);
const inputFileName = 'ProductList.xlsx';
await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);
// load the workbook
const workbook = new xlsModule.Workbook();
workbook.LoadFromFile({ fileName: inputFileName });
// take the first worksheet and add a comment to cell A4, filled with the picture
const sheet = workbook.Worksheets.get(0);
const range = sheet.Range.get('A4');
const comment = range.AddComment();
comment.Fill.CustomPicture({ im: new xlsModule.Stream('logo.png'), name: 'None' });
// set the width and height of the comment box
comment.Width = 150;
comment.Height = 150;
// place the comment box relative to the top-left corner of the worksheet
comment.Top = 100;
comment.Left = 400;
// make the comment show as soon as the file is opened
comment.Visible = true;
// save the workbook
const outputFileName = 'PictureComment.xlsx';
workbook.SaveToFile({ fileName: outputFileName, version: xlsModule.ExcelVersion.Version2010 });
// dispose of the workbook to free resources
workbook.Dispose();
// read the result file from the VFS and start the download
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>Insert Comments with Pictures</h1>
<button id="picture-comment" onClick={addPictureComment}>Insert a picture comment into a cell</button>
</div>
);
}
export default App;
After running, the effect of inserting a picture comment into a cell:

Fill a comment with a solid colour
A comment background need not be a picture. When all that is needed is for one comment to stand out from the pale-yellow shading around it, specifying a solid colour is more straightforward than preparing an image. Filling with a picture and filling with a solid colour use the same comment.Fill object; only the fill type changes from picture to solid. The steps are:
- Load the font and the test data file into the VFS.
- Load the workbook with
workbook.LoadFromFile. - Take the cell the comment will be attached to with
sheet.Range.get('A4')and create the comment withrange.AddComment(). - Write the comment text with
comment.Text. - Create a font with
workbook.CreateFont()and set the whole text to it withcomment.RichText.SetFont. - Set
comment.Fill.FillTypeto the solid fill type,xlsModule.ShapeFillType.SolidColor. - Pick the colour with
comment.Fill.ForeColor. - Set
comment.Visibleto show the comment as soon as the file is opened. - Save the workbook with
workbook.SaveToFile. - Dispose of the workbook with
workbook.Disposeto free resources.
The complete code example below shows how to fill an Excel comment with a solid colour in React:
function App() {
const fillCommentColor = async () => {
// get the Spire.XLS WASM module
const xlsModule = window.wasmModule?.spirexls;
// check that the module is ready
if (!xlsModule) {
alert('Spire.Xls is not ready yet');
return;
}
// load the font and the test data file into the VFS
await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
const inputFileName = 'ProductList.xlsx';
await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);
// load the workbook
const workbook = new xlsModule.Workbook();
workbook.LoadFromFile({ fileName: inputFileName });
// take the first worksheet and add a comment to cell A4
const sheet = workbook.Worksheets.get(0);
const range = sheet.Range.get('A4');
// create the font the comment text will use
const font = workbook.CreateFont();
font.FontName = 'Arial';
font.Size = 11;
font.KnownColor = xlsModule.ExcelColors.Orange;
// write the comment text and set the whole text to that font
const commentText = 'This product leads the season';
range.Comment.Text = commentText;
range.Comment.RichText.SetFont(0, commentText.length - 1, font);
// set the comment fill to a solid colour and pick the colour
range.Comment.Fill.FillType = xlsModule.ShapeFillType.SolidColor;
range.Comment.Fill.ForeColor = xlsModule.Color.get_SkyBlue();
range.Comment.Visible = true;
// save the workbook
const outputFileName = 'CommentFillColor.xlsx';
workbook.SaveToFile({ fileName: outputFileName, version: xlsModule.ExcelVersion.Version2010 });
// dispose of the workbook to free resources
workbook.Dispose();
// read the result file from the VFS and start the download
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>Insert Comments with Pictures</h1>
<button id="comment-fill-color" onClick={fillCommentColor}>Fill a comment with a solid colour</button>
</div>
);
}
export default App;
After running, the effect of filling a comment with a solid colour:

FAQ
Why does the picture comment not appear when the result file is opened
Reason: Comments are hidden by default and appear only when the mouse moves over the cell. If the comment was not set to visible before saving, reopening the file shows an ordinary worksheet, and the picture comment does not appear on its own.
Solution: Set comment.Visible to true, and the comment appears as soon as the file is opened:
comment.Visible = true;
Get a Free License
Spire.XLS for JavaScript offers a 30-day full-featured free trial license with no functional limitations. Apply here to evaluate before purchasing.
Hyperlinks in a PDF are meant to save work: one click and you are there, no copying an address, no retyping it. The catch is that they are rarely there when you need them. A brand-new document should come with its download page, a contact address and a product link already in place. An older, already laid-out document should send the product name recurring in its body to the same introduction page. Doing it by hand means dragging a selection and pasting an address at every spot — and on a long document that never ends.
Moving the job into the browser is what Spire.PDF for JavaScript is for: it loads, modifies and saves PDF documents directly on WebAssembly, with PdfTextWebLink drawing visible link text in one step and PdfFileLinkAnnotation attaching a document link. Everything runs locally, reading and writing files through a virtual file system (VFS) with no backend involved.
This article covers two core tasks:
For installation and project setup, see Integrate Spire.PDF for JavaScript in a React project. The example below assumes Spire.PDF is installed and the WebAssembly module is initialized.
Add Hyperlinks to a PDF
When you build a document from scratch, everything on the page is decided by code, and links fall into a few kinds:
- Simple text link: the URL drawn as ordinary text, with no annotation attached; whether it is clickable is up to the viewer — Chrome and others auto-detect the URL and make it clickable — so it is here only for contrast with the explicitly linked ones.
- Hypertext link:
PdfTextWebLinkwithText,Url,FontandBrushset;DrawTextWebLink()draws the underlined link text in one step. - Document link:
PdfFileLinkAnnotation(hotspot rectangle, target file), with the rectangle defined by you.
function App() {
const addLinksToNewPdf = async () => {
// Get the Spire.PDF WASM module
const pdfModule = window.wasmModule?.spirepdf;
// Check that the module is ready
if (!pdfModule) {
alert('Spire.PDF is not ready yet');
return;
}
// Drawing text needs a font; load it into the VFS first
const fontVfs = '/Library/Fonts/ARIAL UNICODE MS.TTF';
await window.spire.FetchFileToVFS('ARIAL UNICODE MS.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
// Create a blank document
const doc = new pdfModule.PdfDocument();
// Add a section and set its margins to 0: coordinates then equal page coordinates
const section = doc.Sections.Add();
section.PageSettings.Size = pdfModule.PdfPageSize.A4();
const margins = new pdfModule.PdfMargins();
margins.Top = 0;
margins.Bottom = 0;
margins.Left = 0;
margins.Right = 0;
section.PageSettings.Margins = margins;
const page = section.Pages.Add();
const font = new pdfModule.PdfTrueTypeFont({ fontFile: fontVfs, size: 13 });
const linkFont = new pdfModule.PdfTrueTypeFont({ fontFile: fontVfs, size: 13, style: pdfModule.PdfFontStyle.Underline });
const format = new pdfModule.PdfStringFormat({ alignment: pdfModule.PdfTextAlignment.Left });
// Draw a plain line of text as a label
const drawLabel = (text, y) => {
page.Canvas.DrawString({
s: text, font: font, brush: pdfModule.PdfBrushes.get_Black(), x: 60, y: y, format: format,
});
};
// Draw a line of underlined text and hand back its rectangle as the hotspot
const drawLinkText = (text, y) => {
const size = linkFont.MeasureString({ text: text });
page.Canvas.DrawString({
s: text, font: linkFont, brush: pdfModule.PdfBrushes.get_Blue(), x: 160, y: y, format: format,
});
return new pdfModule.RectangleF({ x: 160, y: y, width: size.Width, height: size.Height });
};
const url = 'https://www.e-iceblue.com/Introduce/pdf-for-javascript.html';
// 1) Simple text link: just the URL drawn as ordinary text, with no annotation attached (clickability depends on the viewer)
let y = 80;
drawLabel('Simple text link:', y);
drawLinkText(url, y);
// 2) Hypertext link: DrawTextWebLink draws the text and attaches the link in one step
y += 34;
drawLabel('Hypertext link:', y);
const webLink = new pdfModule.PdfTextWebLink();
webLink.Text = 'Visit our site';
webLink.Url = url;
webLink.Font = linkFont;
webLink.Brush = pdfModule.PdfBrushes.get_Blue();
webLink.DrawTextWebLink({ graphics: page.Canvas, location: new pdfModule.PointF(160, y) });
// 3) Email link: same class, with a mailto: URL
y += 34;
drawLabel('Email link:', y);
const mailLink = new pdfModule.PdfTextWebLink();
mailLink.Text = 'Contact support';
mailLink.Url = 'mailto:support@e-iceblue.com';
mailLink.Font = linkFont;
mailLink.Brush = pdfModule.PdfBrushes.get_Blue();
mailLink.DrawTextWebLink({ graphics: page.Canvas, location: new pdfModule.PointF(160, y) });
// 4) Document link: hotspot rectangle + target file
y += 34;
drawLabel('Document link:', y);
const fileName = 'Report.xlsx';
const fileBounds = drawLinkText('Open file', y);
page.Annotations.Add(new pdfModule.PdfFileLinkAnnotation(fileBounds, fileName));
// Save the result document
const outputFileName = 'HyperlinkSample.pdf';
doc.SaveToFile(outputFileName);
doc.Close();
// Read the generated file from the VFS and trigger the download
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/pdf' });
const downloadUrl = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = downloadUrl;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(downloadUrl);
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>Add Hyperlinks</h1>
<button onClick={addLinksToNewPdf}>
Start Creating
</button>
</div>
);
}
export default App;
All four links — simple text, hypertext, email and document — are in place in the new document:

Add Hyperlinks to Text
To add links to an existing document, work the other way round: find the position in the document first, then lay the link over it. PdfTextFinder searches the text of a given page by keyword, and each match carries a bounding rectangle. Build a PdfUriAnnotation from it and add it to the page annotation collection, and the link lands exactly on the original text instead of you doing the measuring.
function App() {
const addHyperlinksToText = async () => {
// Get the Spire.PDF WASM module
const pdfModule = window.wasmModule?.spirepdf;
// Check that the module is ready
if (!pdfModule) {
alert('Spire.PDF is not ready yet');
return;
}
// Load the PDF to be processed into the VFS
const inputFileName = 'ProductManual.pdf';
await window.spire.FetchFileToVFS(inputFileName, "", `${process.env.PUBLIC_URL}/data/`);
// Create a PdfDocument object and load the PDF
let doc = new pdfModule.PdfDocument();
doc.LoadFromFile(inputFileName);
// Take the first page
const page = doc.Pages.get_Item(0);
// Search the page for the keyword; Parameter is case-sensitive by default, so switch to IgnoreCase
const finder = new pdfModule.PdfTextFinder(page);
finder.Options.Parameter = pdfModule.TextFindParameter.IgnoreCase;
const finds = finder.Find('Aurora');
// Create one link annotation for each match
const linkUrl = 'https://www.e-iceblue.com/Introduce/pdf-for-javascript.html';
for (let i = 0; i < finds.length; i++) {
// The bounding rectangle of a match is the link hotspot
const annotation = new pdfModule.PdfUriAnnotation({ rectangle: finds.get(i).Bounds[0] });
annotation.Uri = linkUrl;
// Attach the annotation to the page annotation collection
page.Annotations.Add(annotation);
}
// Save the result document
const outputFileName = 'HyperlinksAdded.pdf';
doc.SaveToFile(outputFileName);
doc.Close();
// Read the generated file from the VFS and trigger the download
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/pdf' });
const downloadUrl = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = downloadUrl;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(downloadUrl);
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>Add Hyperlinks to Text</h1>
<button onClick={addHyperlinksToText}>
Start Adding
</button>
</div>
);
}
export default App;
All three occurrences of Aurora now carry a hyperlink:

FAQ
Find() returns nothing even though the word is right there in the text
Cause: Options.Parameter defaults to TextFindParameter.None, which is case-sensitive, so searching for aurora will not match the capitalised Aurora; and the finder searches only the single page it was constructed with.
Fix: set it to IgnoreCase; to search the whole document, build one finder per page:
finder.Options.Parameter = pdfModule.TextFindParameter.IgnoreCase;
Measured: Aurora appears three times on page 1, and a case-sensitive search returns 0.
The link is there, but nothing shows up when the file is opened
Cause: this kind of link annotation is only a click hotspot — Spire generates no appearance stream (/AP) for it, and neither Chrome (PDFium) nor PyMuPDF (mupdf) draws a border for it; setting Border/Color only writes into the annotation dictionary (/Border [0 0 1], /C [0 0 1]) and stays invisible on the page.
Fix: the link is still clickable. In task 1 the hypertext, email, simple-text and document links all have their text drawn by the code, so they are visible to begin with; for a hotspot laid over existing text in task 2, do not rely on the annotation border — draw an underline at the link position with page.Canvas.DrawString().
Get a Free License
If you want to remove the evaluation message from the result document, or get past the feature limits, contact our sales team for a temporary 30-day license.