Knowledgebase (2597)
Children categories
A PDF document is organized page by page, and reading, printing and archiving all follow that structure. In practice, you often need to adjust the pages of an existing PDF: add a signature page to a contract, append a summary page at the end of a report, or remove a page that no longer belongs. Doing this with desktop software or a server-side re-layout means exporting and uploading files back and forth. Handling it directly in the browser keeps the document on the user's device and shortens the whole path.
Spire.PDF for JavaScript loads, modifies and saves PDF documents directly in the browser based on WebAssembly, managing input and output files through a virtual file system (VFS), with no backend service required.
This article covers three core features:
For installation and project configuration, refer to Integrating Spire.PDF for JavaScript in a React Project. The following examples assume Spire.PDF is installed and the WebAssembly module has been initialized.
Adding a Page to a PDF
Adding a page means inserting one item into the PdfPageCollection. Use Pages.Insert(index) to insert at a specific position; the index is 0-based, and the pages after the insertion point shift back by one. Here the blank page goes into the second position and the existing pages move onward, which suits adding a page in the middle of a document.
function App() {
const addPageToPdf = async () => {
// Get the Spire.PDF WASM module
const pdfModule = window.wasmModule?.spirepdf;
// Check whether the module is ready
if (!pdfModule) {
alert('Spire.PDF is not ready yet');
return;
}
// Load the PDF file into the VFS
const inputFileName = 'Multipage_Document.pdf';
await window.spire.FetchFileToVFS(inputFileName, "", `${process.env.PUBLIC_URL}/data/`);
// Create a PdfDocument object and load the PDF document
let doc = new pdfModule.PdfDocument();
doc.LoadFromFile(inputFileName);
// Insert a blank page as the second page (the index is 0-based, so index 1 is the second page)
doc.Pages.Insert(1);
// Define the output file name and save the document
const outputFileName = 'Page_Inserted.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 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>Add a Page to PDF</h1>
<button onClick={addPageToPdf}>
Start Adding
</button>
</div>
);
}
export default App;
The PDF document after a blank page is inserted as the second page

Adding a Blank Page at the End of a Document
Spire.PDF for JavaScript also provides the Pages.Add() method, which appends a blank page at the end of a document. It uses A4 as the default page size and 40-point margins on all four sides; when needed, you can set the page size and margins yourself.
function App() {
const appendPageToPdf = async () => {
// Get the Spire.PDF WASM module
const pdfModule = window.wasmModule?.spirepdf;
// Check whether the module is ready
if (!pdfModule) {
alert('Spire.PDF is not ready yet');
return;
}
// Load the PDF file into the VFS
const inputFileName = 'Multipage_Document.pdf';
await window.spire.FetchFileToVFS(inputFileName, "", `${process.env.PUBLIC_URL}/data/`);
// Create a PdfDocument object and load the PDF document
let doc = new pdfModule.PdfDocument();
doc.LoadFromFile(inputFileName);
// Append a blank A4 page with zero margins on all four sides
doc.Pages.Add(pdfModule.PdfPageSize.A4(), new pdfModule.PdfMargins(0.0, 0.0));
// Define the output file name and save the document
const outputFileName = 'Page_Appended.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 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>Append a Blank Page to PDF</h1>
<button onClick={appendPageToPdf}>
Start Adding
</button>
</div>
);
}
export default App;
The PDF document after a blank A4 page is appended at the end

Deleting a Page from a PDF
Deleting a page also works through the page collection: Pages.RemoveAt(index) removes one page by index, starting at 0, and the indexes of the pages after it shift forward by one. Before writing the delete logic, check the current page count with Pages.Count so the index stays in range. Here the second page of the sample document is removed and the remaining pages keep their original order.
function App() {
const deletePageFromPdf = async () => {
// Get the Spire.PDF WASM module
const pdfModule = window.wasmModule?.spirepdf;
// Check whether the module is ready
if (!pdfModule) {
alert('Spire.PDF is not ready yet');
return;
}
// Load the PDF file into the VFS
const inputFileName = 'Multipage_Document.pdf';
await window.spire.FetchFileToVFS(inputFileName, "", `${process.env.PUBLIC_URL}/data/`);
// Create a PdfDocument object and load the PDF document
let doc = new pdfModule.PdfDocument();
doc.LoadFromFile(inputFileName);
// Delete the second page (the index is 0-based, so index 1 is the second page)
doc.Pages.RemoveAt(1);
// Define the output file name and save the document
const outputFileName = 'Page_Deleted.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 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>Delete a Page from PDF</h1>
<button onClick={deletePageFromPdf}>
Start Deleting
</button>
</div>
);
}
export default App;
The PDF document after the second page is deleted

FAQ
How do I set the page size and margins of a new blank page
Reason: Pages.Add and Pages.Insert create blank pages of a regular size with default margins. When a new page has to match a specific paper size or margin, both values need to be passed in.
Solution: The paper size comes from PdfPageSize, such as PdfPageSize.A4() or PdfPageSize.A3(); the margins come from PdfMargins, where the two-argument form new PdfMargins(0.0, 0.0) sets both the vertical and horizontal margins to 0. To control each side separately, pass the named fields:
// A blank A4 page with zero margins on all four sides
doc.Pages.Add(pdfModule.PdfPageSize.A4(), new pdfModule.PdfMargins(0.0, 0.0));
// Set the top, bottom, left and right margins individually
doc.Pages.Add(pdfModule.PdfPageSize.A4(),
new pdfModule.PdfMargins({ left: 40, top: 40, right: 40, bottom: 40 }));
Why does deleting a page report an index out of range
Reason: RemoveAt(index) is 0-based, and its valid range is 0 to Pages.Count - 1. Without checking the page count first, passing a value equal to or greater than Count goes out of range.
Solution: Check the page count with Pages.Count before deleting and keep the index within range. To delete the last page, for example, the index should be Count - 1:
let total = doc.Pages.Count;
if (total > 0) {
// Delete the last page
doc.Pages.RemoveAt(total - 1);
}
What should I watch out for when adding or deleting several pages in a row
Reason: Every insert or delete shifts the indexes of all pages after it. Deleting several pages by fixed indexes from front to back easily removes the wrong ones — once one page is gone, the later indexes recorded earlier have already moved forward.
Solution: Insert, Add and RemoveAt each handle a single page, so repeat the call for batch operations. When deleting several pages, work from back to front so the indexes do not drift:
// Delete from back to front, so the indexes do not shift after each removal
for (let i = doc.Pages.Count - 1; i >= 3; i--) {
doc.Pages.RemoveAt(i);
}
Get a Free License
If you wish to remove the evaluation message from the result document or remove feature limitations, please contact sales to obtain a temporary license valid for 30 days.
Adding shapes to PDF documents is a common requirement in many business scenarios: marking key areas with lines and boxes, adding a prominent border around content outside a table, distinguishing sections with filled color blocks, or overlaying pie and ellipse shapes on drawings and reports. Doing this by hand in a design tool each time is slow and hard to scale. With the drawing capabilities of Spire.PDF for JavaScript, you can write various shapes directly to PDF pages in the browser and let your code handle the annotation and diagramming work automatically.
Spire.PDF for JavaScript is based on WebAssembly and loads, edits, and saves PDF documents directly in the browser, managing input and output files through a virtual file system (VFS) without any backend service. The core object for drawing shapes on a PDF is the page drawing canvas PdfPage.Canvas: it provides methods such as DrawLine, DrawPie, DrawRectangle, and DrawEllipse.
This article covers four core features:
- Draw Lines on a PDF Page
- Draw a Pie on a PDF Page
- Draw a Rectangle on a PDF Page
- Draw an Ellipse on a PDF Page
For installation and project configuration, refer to How to Integrate Spire.PDF for JavaScript in a React Project. The examples below assume Spire.PDF is installed and the WebAssembly module is initialized.
Draw Lines on a PDF Page
When drawing lines you can set the color and thickness, and choose between solid and dashed lines—the dash style is controlled by DashStyle and DashPattern.
function App() {
const drawLines = async () => {
// Get the Spire.PDF WASM module
const pdfModule = window.wasmModule?.spirepdf;
// Check whether the module is ready
if (!pdfModule) {
alert('Spire.PDF is not ready yet');
return;
}
// Create a PDF document and add a blank page
let doc = new pdfModule.PdfDocument();
let page = doc.Pages.Add();
// Save the current graphics state
let state = page.Canvas.Save();
// Create a red pen for drawing lines
let pen = new pdfModule.PdfPen({
pdfRGBColor: new pdfModule.PdfRGBColor({ color: pdfModule.Color.get_Red() }),
width: 2,
});
// Starting coordinates and length of the lines
let x = 30.0;
let y = 50.0;
let width = 300.0;
// Draw a solid line
page.Canvas.DrawLine({ pen: pen, x1: x, y1: y, x2: x + width, y2: y });
// Set the dash style and dash pattern
pen.DashStyle = pdfModule.PdfDashStyle.Dash;
pen.DashPattern = [3.0, 2.0];
// Draw a dashed line
page.Canvas.DrawLine({ pen: pen, x1: x, y1: y + 60.0, x2: x + width, y2: y + 60.0 });
// Restore the graphics state
page.Canvas.Restore({ state: state });
// Define the output file name and save the document
const outputFileName = 'DrawLines_result.pdf';
doc.SaveToFile(outputFileName);
doc.Close();
// Read the generated file from the VFS and trigger a download
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/pdf' });
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>Draw Lines in PDF</h1>
<button onClick={drawLines}>
Draw
</button>
</div>
);
}
export default App;
The result of drawing one solid line and one dashed line on a PDF page

Draw a Pie on a PDF Page
Pies express proportions: the bounding rectangle sets the position and size, while startAngle and sweepAngle set the opening angle of the sector.
function App() {
const drawPie = async () => {
// Get the Spire.PDF WASM module
const pdfModule = window.wasmModule?.spirepdf;
// Check whether the module is ready
if (!pdfModule) {
alert('Spire.PDF is not ready yet');
return;
}
// Create a PDF document and add a blank page
let doc = new pdfModule.PdfDocument();
let page = doc.Pages.Add();
// Save the current graphics state
let state = page.Canvas.Save();
// Create a dark red pen
let pen = new pdfModule.PdfPen({
pdfRGBColor: new pdfModule.PdfRGBColor({ color: pdfModule.Color.get_DarkRed() }),
width: 2,
});
// Draw the first pie
page.Canvas.DrawPie({ pen: pen, x: 10.0, y: 30.0, width: 130.0, height: 130.0, startAngle: 360.0, sweepAngle: 300.0 });
// Draw the second pie
page.Canvas.DrawPie({ pen: pen, x: 160.0, y: 30.0, width: 130.0, height: 130.0, startAngle: 360.0, sweepAngle: 330.0 });
// Draw the third pie
page.Canvas.DrawPie({ pen: pen, x: 320.0, y: 30.0, width: 130.0, height: 130.0, startAngle: 360.0, sweepAngle: 360.0 });
// Restore the graphics state
page.Canvas.Restore({ state: state });
// Define the output file name and save the document
const outputFileName = 'DrawPie_result.pdf';
doc.SaveToFile(outputFileName);
doc.Close();
// Read the generated file from the VFS and trigger a download
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/pdf' });
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>Draw a Pie in PDF</h1>
<button onClick={drawPie}>
Draw
</button>
</div>
);
}
export default App;
The result of drawing three pies on a PDF page

Draw a Rectangle on a PDF Page
A rectangle can be drawn as an outline only, or filled. Besides a solid color (PdfSolidBrush), the fill also supports a linear gradient (PdfLinearGradientBrush) and a radial gradient (PdfRadialGradientBrush).
function App() {
const drawRectangle = async () => {
// Get the Spire.PDF WASM module
const pdfModule = window.wasmModule?.spirepdf;
// Check whether the module is ready
if (!pdfModule) {
alert('Spire.PDF is not ready yet');
return;
}
// Create a PDF document and add a blank page
let doc = new pdfModule.PdfDocument();
let page = doc.Pages.Add();
// Save the current graphics state
let state = page.Canvas.Save();
// Create a black pen
let pen = new pdfModule.PdfPen({
pdfRGBColor: new pdfModule.PdfRGBColor({ color: pdfModule.Color.get_Black() }),
width: 1,
});
// Draw a rectangle outline with the pen
page.Canvas.DrawRectangle({
pen: pen,
rectangle: new pdfModule.RectangleF({
location: new pdfModule.PointF(20.0, 30.0),
size: new pdfModule.SizeF({ width: 150.0, height: 120.0 }),
}),
});
// Create a linear gradient brush
let linearGradientBrush = new pdfModule.PdfLinearGradientBrush({
point1: new pdfModule.PointF(200.0, 30.0),
point2: new pdfModule.PointF(350.0, 150.0),
color1: new pdfModule.PdfRGBColor({ color: pdfModule.Color.get_Green() }),
color2: new pdfModule.PdfRGBColor({ color: pdfModule.Color.get_Red() }),
});
// Draw a filled rectangle with the linear gradient brush
page.Canvas.DrawRectangle({
brush: linearGradientBrush,
rectangle: new pdfModule.RectangleF({
location: new pdfModule.PointF(200.0, 30.0),
size: new pdfModule.SizeF({ width: 150.0, height: 120.0 }),
}),
});
// Create a radial gradient brush
let radialGradientBrush = new pdfModule.PdfRadialGradientBrush({
centreStart: new pdfModule.PointF(380.0, 30.0),
radiusStart: 150.0,
centreEnd: new pdfModule.PointF(530.0, 150.0),
radiusEnd: 150.0,
colorStart: new pdfModule.PdfRGBColor({ color: pdfModule.Color.get_Orange() }),
colorEnd: new pdfModule.PdfRGBColor({ color: pdfModule.Color.get_Blue() }),
});
// Draw a filled rectangle with the radial gradient brush
page.Canvas.DrawRectangle({
brush: radialGradientBrush,
rectangle: new pdfModule.RectangleF({
location: new pdfModule.PointF(380.0, 30.0),
size: new pdfModule.SizeF({ width: 150.0, height: 120.0 }),
}),
});
// Restore the graphics state
page.Canvas.Restore({ state: state });
// Define the output file name and save the document
const outputFileName = 'DrawRectangle_result.pdf';
doc.SaveToFile(outputFileName);
doc.Close();
// Read the generated file from the VFS and trigger a download
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/pdf' });
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>Draw a Rectangle in PDF</h1>
<button onClick={drawRectangle}>
Draw
</button>
</div>
);
}
export default App;
The result of drawing a rectangle outline and gradient-filled rectangles on a PDF page

Draw an Ellipse on a PDF Page
An ellipse likewise supports outlines and fills: use PdfPen for the outline and PdfSolidBrush for the fill, or take one of the preset pens from PdfPens.
function App() {
const drawEllipse = async () => {
// Get the Spire.PDF WASM module
const pdfModule = window.wasmModule?.spirepdf;
// Check whether the module is ready
if (!pdfModule) {
alert('Spire.PDF is not ready yet');
return;
}
// Create a PDF document and add a blank page
let doc = new pdfModule.PdfDocument();
let page = doc.Pages.Add();
// Save the current graphics state
let state = page.Canvas.Save();
// Create a CadetBlue pen
let pen = pdfModule.PdfPens.get_CadetBlue();
// Draw the ellipse outline
page.Canvas.DrawEllipse({ pen: pen, x: 50.0, y: 30.0, width: 120.0, height: 100.0 });
// Create a fill brush
let brush = new pdfModule.PdfSolidBrush({
pdfRGBColor: new pdfModule.PdfRGBColor({ color: pdfModule.Color.get_CadetBlue() }),
});
// Draw the filled ellipse
page.Canvas.DrawEllipse({ brush: brush, x: 180.0, y: 30.0, width: 120.0, height: 100.0 });
// Restore the graphics state
page.Canvas.Restore({ state: state });
// Define the output file name and save the document
const outputFileName = 'DrawEllipse_result.pdf';
doc.SaveToFile(outputFileName);
doc.Close();
// Read the generated file from the VFS and trigger a download
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/pdf' });
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>Draw an Ellipse in PDF</h1>
<button onClick={drawEllipse}>
Draw
</button>
</div>
);
}
export default App;
The result of drawing an ellipse outline and a filled ellipse on a PDF page

FAQ
Why does the drawn shape appear at the edge of the page or outside the visible area
Cause: The coordinates used by DrawLine, DrawPie, DrawRectangle, and DrawEllipse have their origin at the bottom-left corner of the page, with the x-axis pointing right and the y-axis pointing up, in units of points. If you copy screen coordinates directly (where the origin is at the top-left), the drawn shape will end up in the opposite position or outside the page.
Solution: Convert the coordinates using the bottom-left corner of the page as the origin. You can read the page size first and then lay out the shape, for example by using PdfPage.Size to get the page width and height and calculating the shape's position from them:
// Get the page size and calculate coordinates with the bottom-left corner as the origin
let size = page.Size;
let x = size.Width / 4;
let y = size.Height / 3;
page.Canvas.DrawRectangle({ pen: pen, x: x, y: y, width: 200, height: 120 });
Why does the existing content on the page shift after drawing
Cause: Drawing modifies the canvas's current transform and graphics state. If you change the coordinate system with ScaleTransform, TranslateTransform, and similar methods before drawing, or fail to restore the state afterward, subsequent content will be affected.
Solution: Use Canvas.Save and Canvas.Restore in pairs, wrapping the drawing operations between them, to make sure the canvas state is restored to its previous level once drawing is complete:
// Save the state before drawing
let state = page.Canvas.Save();
// ... perform drawing ...
// Restore the state after drawing
page.Canvas.Restore({ state: state });
Why can't I see the drawn shape in the saved PDF
Cause: Shapes are drawn onto the canvas object. If you do not call doc.SaveToFile to write the document back to a file after drawing, or if the output file and the file read for download are not the same name, you will still see the original content.
Solution: Make sure you call doc.SaveToFile(outputFileName) to save after drawing, and read and download from the virtual file system using the same outputFileName:
// Save the document to the specified file name
doc.SaveToFile(outputFileName);
doc.Close();
// Read from the VFS using the same file name to trigger a download
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
Get a Free License
If you want to remove the evaluation message from the result documents or get rid of the feature limitations, please contact our sales team to obtain a temporary license valid for 30 days.
A bookmark in a PDF records the document's outline structure, and a bookmark can hold child bookmarks of its own, nesting level by level into a tree. Reading that information has plenty of practical uses — exporting it as a table of contents, generating site navigation from bookmark titles, or locating a specific page for further processing. All of it calls for a program that can walk the whole bookmark tree and pull out the content of each node.
Spire.PDF for JavaScript processes PDF documents directly in the browser based on WebAssembly, managing input and output files through a virtual file system (VFS), with no backend service required. The core entry point for extracting bookmarks is the PdfDocument.Bookmarks property, which returns a PdfBookmarkCollection; each PdfBookmark object in it exposes its own child bookmark collection, so together they form the complete bookmark tree. Every bookmark node provides properties such as Title and DisplayStyle for reading its appearance, and Destination.Page combined with PdfPageCollection.IndexOf gives you the page number the bookmark points to.
This article covers two core features:
For installation and project configuration, refer to Integrating Spire.PDF for JavaScript in a React Project. The following examples assume Spire.PDF is installed and the WebAssembly module has been initialized.
Extracting All PDF Bookmarks
PdfDocument.Bookmarks returns only the top-level bookmark collection, while bookmarks themselves can contain child bookmarks. To read out every bookmark in the document you need a recursive function that walks the PdfBookmarkCollection level by level, reads the Title (bookmark title) and DisplayStyle (text style) of each node, and records them with indentation by level, producing a complete outline list in the end.
function App() {
const extractAllBookmarks = async () => {
// Get the Spire.PDF WASM module
const pdfModule = window.wasmModule?.spirepdf;
// Check whether the module is ready
if (!pdfModule) {
alert('Spire.PDF is not ready yet');
return;
}
// Load the PDF file to be processed into the VFS
const inputFileName = 'Sample.pdf';
await window.spire.FetchFileToVFS(inputFileName, "", `${process.env.PUBLIC_URL}/data/`);
// Create a PdfDocument object and load the PDF document
let doc = new pdfModule.PdfDocument();
doc.LoadFromFile(inputFileName);
// String that holds the extracted result
let content = 'All bookmarks in the PDF document:\r\n';
// Recursively traverse the bookmark collection, recording each title and text style with indentation by level
const collectBookmarks = (bookmarks, indent) => {
for (let i = 0; i < bookmarks.Count; i++) {
let bookmark = bookmarks.get_Item(i);
// Record the title and text style of the current bookmark
content += indent + bookmark.Title + ' (' + bookmark.DisplayStyle.toString() + ')\r\n';
// If there are child bookmarks, process them recursively with more indentation
if (bookmark.Count > 0) {
collectBookmarks(bookmark, indent + ' ');
}
}
};
// Start extracting from the top-level bookmarks
collectBookmarks(doc.Bookmarks, '');
// Write the extracted result to a file and trigger the download
const outputFileName = 'AllBookmarks.txt';
window.dotnetRuntime.Module.FS.writeFile(outputFileName, content);
doc.Close();
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'text/plain' });
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>Extract All PDF Bookmarks</h1>
<button onClick={extractAllBookmarks}>
Start Extracting
</button>
</div>
);
}
export default App;
The list of all bookmark titles and text styles extracted recursively

Getting the Page Number of a Bookmark
Besides its title, a bookmark also carries a jump destination. Through PdfBookmark.Destination.Page you can obtain the PdfPage object the bookmark points to, and then use PdfPageCollection.IndexOf to get its index within the document. Because the index starts from 0, adding 1 gives the page number shown in a reader. This is commonly used to export a bookmark list as a "title — page number" table of contents.
function App() {
const getBookmarkPageNumber = async () => {
// Get the Spire.PDF WASM module
const pdfModule = window.wasmModule?.spirepdf;
// Check whether the module is ready
if (!pdfModule) {
alert('Spire.PDF is not ready yet');
return;
}
// Load the PDF file to be processed into the VFS
const inputFileName = 'Sample.pdf';
await window.spire.FetchFileToVFS(inputFileName, "", `${process.env.PUBLIC_URL}/data/`);
// Create a PdfDocument object and load the PDF document
let doc = new pdfModule.PdfDocument();
doc.LoadFromFile(inputFileName);
// Read the page number each top-level bookmark points to, one by one
let content = 'Bookmarks and their page numbers:\r\n';
for (let i = 0; i < doc.Bookmarks.Count; i++) {
let bookmark = doc.Bookmarks.get_Item(i);
// Destination.Page gives the page the bookmark points to; IndexOf returns its 0-based index
let pageNumber = doc.Pages.IndexOf(bookmark.Destination.Page) + 1;
content += bookmark.Title + ' — Page ' + pageNumber + '\r\n';
}
// Write the extracted result to a file and trigger the download
const outputFileName = 'BookmarkPageNumber.txt';
window.dotnetRuntime.Module.FS.writeFile(outputFileName, content);
doc.Close();
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'text/plain' });
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>Get the Page Number of a Bookmark</h1>
<button onClick={getBookmarkPageNumber}>
Start Extracting
</button>
</div>
);
}
export default App;
The title of each bookmark and the page number it points to

FAQ
Why is the number of extracted bookmarks smaller than the outline shown in a reader
Reason: doc.Bookmarks returns only the top-level bookmark collection, and its Count counts only the nodes at that level. Child bookmarks nested under a chapter are reached through the node's own collection and are otherwise not counted.
Solution: Traverse the whole bookmark tree recursively and add up the nodes at every level:
function countBookmarks(bookmarks) {
let total = 0;
for (let i = 0; i < bookmarks.Count; i++) {
total += 1;
// Recursively add the child bookmarks
total += countBookmarks(bookmarks.get_Item(i));
}
return total;
}
const total = countBookmarks(doc.Bookmarks);
Why is the extracted DisplayStyle always Regular
Reason: PdfBookmark.DisplayStyle returns the text style a bookmark is displayed with in the outline panel. Only when the bookmark itself is explicitly set to a style such as Bold or Italic will the value read back differ from the default Regular. It reflects the bookmark's appearance setting, not the font used by the bookmark title in the page content.
Solution: Record the enum value as it is; if you only need to tell whether it is bold or italic, compare it against the PdfTextStyle values one by one:
let style = 'Regular';
if (bookmark.DisplayStyle === pdfModule.PdfTextStyle.Bold) {
style = 'Bold';
} else if (bookmark.DisplayStyle === pdfModule.PdfTextStyle.Italic) {
style = 'Italic';
}
Why does the page number from Destination.Page differ from the one shown in a reader
Reason: PdfPageCollection.IndexOf returns the page's index within the collection, counting from 0, while a reader shows page numbers from 1, so using the index directly is off by one.
Solution: Add 1 to the index to match what the reader shows:
// The index is 0-based, so add 1 to get the page number shown in a reader
let pageNumber = doc.Pages.IndexOf(bookmark.Destination.Page) + 1;
In addition, if a bookmark points to a page that no longer exists (for example, the target page was deleted), Destination may be empty, so check for null before reading it:
if (bookmark.Destination && bookmark.Destination.Page) {
let pageNumber = doc.Pages.IndexOf(bookmark.Destination.Page) + 1;
}
Get a Free License
If you wish to remove the evaluation message from the result document or remove feature limitations, please contact sales to obtain a temporary license valid for 30 days.