Knowledgebase (2597)
Children categories
When charts and diagrams inside a PDF are reused on the web, the usual approach is to render them to PNG first — a bitmap blurs when scaled up, and a different resolution means taking a new screenshot. Web illustrations, vector icons, and print layouts that need to scale to any size really call for SVG.
This article uses Spire.PDF for JavaScript to convert PDF to SVG. It loads and saves PDF documents in the browser on top of WebAssembly, and the whole conversion runs locally: files are read and written through a virtual file system (VFS), with no backend involved. Output has two forms — the whole document merged into one SVG, or one SVG per page.
This article covers two core features:
For installation and project configuration, refer to Integrate Spire.PDF for JavaScript in a React Project. The examples below assume Spire.PDF is already installed and the WebAssembly module is initialized.
Convert the Whole PDF to a Single SVG
Spire.PDF for JavaScript provides the PdfDocument.ConvertOptions.OutputToOneSvg property, which decides whether the whole document is written to one SVG or to one file per page. It defaults to false, so each page becomes its own file; set it to true and the pages are stacked vertically on a single canvas in their original order, then written out by SaveToFile with FileFormat.SVG.
The steps are as follows:
- Get the Spire.PDF WASM module and make sure it is ready.
- Load the sample PDF and the Arial font, create a
PdfDocumentobject, and load the file. - Set
PdfDocument.ConvertOptions.OutputToOneSvgtotrueso all pages go into one SVG. - Use the
PdfDocument.SaveToFile()method to save the result file. - Read the result file from the VFS and trigger a browser download.
The code to convert the whole PDF to a single SVG is as follows:
function App() {
const convertPdfToOneSvg = async () => {
// Get the Spire.PDF WASM module
const pdfModule = window.wasmModule?.spirepdf;
// Check if the module is ready
if (!pdfModule) {
alert('Spire.PDF is not ready yet');
return;
}
// Converting to SVG restores the page text, so put the Arial font into the VFS font directory first
await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
// Load the PDF to convert into the VFS
const inputFileName = 'Chapter_Document.pdf';
await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}/data/`);
// Output file name: the whole document merged into one SVG
const outputFileName = 'Chapter_Document.svg';
// Load the PDF and send all pages into a single SVG
let doc = new pdfModule.PdfDocument();
doc.LoadFromFile(inputFileName);
doc.ConvertOptions.OutputToOneSvg = true;
doc.SaveToFile(outputFileName, pdfModule.FileFormat.SVG);
doc.Close();
// Read the generated file from the VFS and trigger download
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'image/svg+xml' });
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>Convert PDF to SVG</h1>
<button onClick={convertPdfToOneSvg}>
Convert to a single SVG
</button>
</div>
);
}
export default App;
The result is a single SVG file with the five pages joined top to bottom:

Convert Each PDF Page to a Separate SVG
Spire.PDF for JavaScript also provides the PdfToSvgConverter class, which converts a document to SVG page by page. It is a second, equivalent entry point to PdfDocument, and SvgOptions carries OutputToOneSvg as well. This section keeps the default per-page output and downloads the files one by one.
The steps are as follows:
- Get the Spire.PDF WASM module and make sure it is ready.
- Load the sample PDF and the Arial font.
- Create a
PdfToSvgConverterobject, passing the path of the PDF to convert. - Use the
PdfToSvgConverter.Convert()method to convert page by page, one SVG per page. - Read each page's file from the VFS and trigger a download for each.
The code to convert each PDF page to a separate SVG is as follows:
function App() {
const convertPdfPagesToSvg = async () => {
// Get the Spire.PDF WASM module
const pdfModule = window.wasmModule?.spirepdf;
// Check if the module is ready
if (!pdfModule) {
alert('Spire.PDF is not ready yet');
return;
}
// Converting to SVG restores the page text, so put the Arial font into the VFS font directory first
await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
// Load the PDF to convert into the VFS
const inputFileName = 'Chapter_Document.pdf';
await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}/data/`);
// Output file name prefix: pages are written as Chapter_Document_1.svg, Chapter_Document_2.svg, ...
const outputFileName = 'Chapter_Document.svg';
// Convert page by page with the converter
const converter = new pdfModule.PdfToSvgConverter(inputFileName);
converter.Convert(outputFileName);
// Read each page's file from the VFS and trigger a download for each
const fs = window.dotnetRuntime.Module.FS;
const prefix = outputFileName.replace(/\.svg$/i, '_');
const pageFiles = fs.readdir('/').filter((name) => name.startsWith(prefix) && name.endsWith('.svg'));
for (const pageFile of pageFiles) {
const blob = new Blob([fs.readFile(pageFile)], { type: 'image/svg+xml' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = pageFile;
a.click();
URL.revokeObjectURL(url);
}
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>Convert PDF to SVG</h1>
<button onClick={convertPdfPagesToSvg}>
Convert page by page
</button>
</div>
);
}
export default App;
Each page becomes its own SVG file, named with the page number:

Set the SVG Size
The converter also exposes the SvgOptions.ScaleX and ScaleY properties, which scale the output size by a factor; both default to 1. Setting them to 2 doubles the size. The SVG content is vector, so — unlike a bitmap — it stays sharp when scaled.
// Scale the output size up by 2x
const converter = new pdfModule.PdfToSvgConverter('Chapter_Document.pdf');
converter.SvgOptions.ScaleX = 2;
converter.SvgOptions.ScaleY = 2;
converter.Convert('Chapter_Document.svg');
Frequently Asked Questions
Why do I get a bunch of xxx_1.svg, xxx_2.svg files instead of one .svg
Cause: When PdfDocument.SaveToFile converts to SVG it writes page by page, and ConvertOptions.OutputToOneSvg defaults to false, so the file name becomes "prefix_page.svg". A single-page PDF hides the difference; with more pages it shows up.
Solution: To get a single file, turn the property on before saving:
doc.ConvertOptions.OutputToOneSvg = true;
doc.SaveToFile('Chapter_Document.svg', pdfModule.FileFormat.SVG);
With PdfToSvgConverter, the matching switch is converter.SvgOptions.OutputToOneSvg — the two entry points use an equivalent switch and produce identical output.
How do I fix the error Cannot found font(Arial) installed on the system.
Cause: Converting to SVG restores page text as <text> elements, and the converter looks the font up by name in /Library/Fonts/ of the virtual file system. If that directory is empty, it throws this error.
Solution: Put the font into the VFS before LoadFromFile:
await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
Arial and Arial Unicode MS are two different files; supplying only the latter still raises the same error.
Is the text in the converted SVG still selectable
Cause: <text> and stroked paths are different things. The converter keeps page text as <text> elements and turns only graphics and lines into vector paths (<path>), so the text stays selectable and searchable.
Solution: No extra settings. Open the generated SVG in an editor and you will see <text> nodes; zoom it in a browser and neither the text nor the lines blur.
What if I only want to convert a few pages
Cause: SaveToFile also has an overload that takes a page range, working on the same per-page files that are saved in the VFS.
Solution: Pass the start and end page indexes; the index is 0-based and inclusive:
// Write only pages 1 and 2, producing first_two_pages_1.svg and first_two_pages_2.svg
doc.SaveToFile('first_two_pages.svg', 0, 1, pdfModule.FileFormat.SVG);
Get a Free License
If you would like to remove the evaluation message from the result document or lift feature restrictions, please contact sales for a 30-day temporary license.
Einleitung
Dies ist ein Test
A single column often packs several pieces of information into one cell: a contact's name, phone number and city written on one line, an order code joining the country code, the date and a serial number into one string. Such data can neither be sorted nor filtered by any one of those parts. Splitting text into columns takes a column of text apart by a given rule and drops the pieces into the columns to its right, leaving the original data where it is. Spire.XLS for JavaScript performs these operations in the browser through WebAssembly; the 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.
Split on a separator
When every entry on a line is separated from the next by the same character, splitting on that character moves them into the columns to the right. Each kind of information then has a column of its own, ready for sorting, checking or grouping, while the original data stays where it is.
The split reads the text out of the first column, cuts it apart, and writes the pieces back into the columns on the right:
| Syntax | Result |
|---|---|
sheet.Range.get({ row, column: 1 }).Text |
Reads the raw text of the first column on that row |
.split(' ') |
Cuts the text apart on the space |
sheet.Range.get({ row, column: index + 2 }).Text = piece |
Writes the piece at index into the index + 1 column to its right |
sheet.AllocatedRange.AutoFitColumns() |
Sizes every column to its content |
The steps are:
- Load the font file and the test data file into the VFS with
window.spire.FetchFileToVFS. - Create a workbook with
new Workbook(), load the input file withworkbook.LoadFromFile, and take the target worksheet withworkbook.Worksheets.get(0). - Take the last data row with
sheet.LastRowand work row by row from row 2, skipping the header. - Read the raw text of the first column with
sheet.Range.get({ row, column: 1 }).Textand split it on the separator withsplit(' '). - Walk the pieces and write them back from the second column on with
sheet.Range.get({ row, column: index + 2 }).Text. - Size every column to its content with
sheet.AllocatedRange.AutoFitColumns(). - Save the workbook with
workbook.SaveToFileand dispose of it withworkbook.Disposeto free resources.
The full code example below shows how to split an Excel column on a separator in React:
function App() {
const splitByDelimiter = 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 = 'SplitTextIntoColumns.xlsx';
await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);
// load the workbook and take the first worksheet
const workbook = new xlsModule.Workbook();
workbook.LoadFromFile({ fileName: inputFileName });
const sheet = workbook.Worksheets.get(0);
// row by row, take the text in column A, split it on the space and write the pieces into the columns on the right
const lastRow = sheet.LastRow;
for (let row = 2; row <= lastRow; row++) {
const pieces = sheet.Range.get({ row, column: 1 }).Text.split(' ');
pieces.forEach((piece, index) => {
sheet.Range.get({ row, column: index + 2 }).Text = piece;
});
}
// size every column to its content
sheet.AllocatedRange.AutoFitColumns();
// save the workbook
const outputFileName = 'SplitByDelimiter.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>Split Excel Data into Columns</h1>
<button id="split-by-delimiter" onClick={splitByDelimiter}>Split by a Separator</button>
</div>
);
}
export default App;
The worksheet after splitting on a separator:

Split by fixed width
Other text is not divided by any character at all: every entry has a fixed length and they run end to end, as a code assembled from fields of known width. Such text is cut by the number of characters in each entry, carrying on from where the previous one ended.
The cut advances through the source text by the length of each entry and writes the pieces back into the columns on the right:
| Syntax | Result |
|---|---|
sheet.Range.get({ row, column: 1 }).Text |
Reads the raw code of the first column on that row |
code.substring(start, start + width) |
Cuts out width characters starting at start |
start += width |
Moves the cutting position past that entry, ready for the next |
sheet.Range.get({ row, column: index + 2 }).Text = piece |
Writes the piece at index into the index + 1 column to its right |
sheet.AllocatedRange.AutoFitColumns() |
Sizes every column to its content |
The steps are:
- Load the font file and the test data file into the VFS with
window.spire.FetchFileToVFS. - Create a workbook with
new Workbook(), load the input file withworkbook.LoadFromFile, and take the worksheet holding the text withworkbook.Worksheets.get(2). - Note down the number of characters in each entry in an array; the lengths should add up to that of the source text.
- Take the last data row with
sheet.LastRowand work row by row from row 2, skipping the header. - Cut one entry out with
code.substring(start, start + width), move the starting position on by that length, and write it back from the second column on withsheet.Range.get({ row, column: index + 2 }).Text. - Size every column to its content with
sheet.AllocatedRange.AutoFitColumns(). - Save the workbook with
workbook.SaveToFileand dispose of it withworkbook.Disposeto free resources.
The full code example below shows how to split an Excel column by fixed width in React:
function App() {
const splitByFixedWidth = 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 = 'SplitTextIntoColumns.xlsx';
await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);
// load the workbook and take the third worksheet
const workbook = new xlsModule.Workbook();
workbook.LoadFromFile({ fileName: inputFileName });
const sheet = workbook.Worksheets.get(2);
// row by row, take the code in column A and cut it at the fixed lengths into the columns on the right
const widths = [2, 4, 2, 2, 3];
const lastRow = sheet.LastRow;
for (let row = 2; row <= lastRow; row++) {
const code = sheet.Range.get({ row, column: 1 }).Text;
let start = 0;
widths.forEach((width, index) => {
sheet.Range.get({ row, column: index + 2 }).Text = code.substring(start, start + width);
start += width;
});
}
// size every column to its content
sheet.AllocatedRange.AutoFitColumns();
// save the workbook
const outputFileName = 'SplitByFixedWidth.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>Split Excel Data into Columns</h1>
<button id="split-by-fixed-width" onClick={splitByFixedWidth}>Split by Fixed Width</button>
</div>
);
}
export default App;
The worksheet after splitting by fixed width:

FAQ
The column uses more than one kind of separator, how can it be split in one pass
Reason: split(' ') cuts on a single character at a time; when a text mixes commas, slashes and the like, each separator has to be handled on its own, so the code keeps piling up and one of them is easily left out.
Solution: Put the separators you expect into a character class, and a single split covers them all -- the characters inside the brackets are alternatives, so any one of them counts as a split point. The sample workbook's address sheet separates the levels of an address with a comma or a slash, and this form splits it into three columns at once:
// a comma and a slash both count as split points, taken apart in one pass
const pieces = sheet.Range.get({ row, column: 1 }).Text.split(/[,/]/);
An empty column turns up in the middle of the split result
Reason: split(' ') cuts strictly on a single space, so two adjacent spaces in the raw text cut out an empty string between them, and that empty string lands on the sheet as a blank column.
Solution: Strip the leading and trailing spaces with trim() first, then split on /\s+/ -- a run of consecutive spaces counts as one split point, and no empty string is cut out in between:
// strip the surrounding spaces, then split on a run of whitespace so no empty piece is cut out
const pieces = sheet.Range.get({ row, column: 1 }).Text.trim().split(/\s+/);
Does splitting into columns overwrite what the columns on the right already hold
Cause: The pieces are written cell by cell with sheet.Range.get({ row, column: index + 2 }).Text = piece, so whatever the target column held on that row is replaced by the entry written into it. The text in the first column is untouched; on the right, the columns the pieces land in are overwritten, and any column beyond them keeps its old data.
Solution: To keep what is already on the right, write the result further out into empty columns, or into a separate worksheet instead; sheet.LastColumn tells you whether the right-hand side already holds anything before you write.
What happens when the entry lengths do not add up to the length of the source text
Cause: substring(start, start + width) only takes what the source text actually holds. When the lengths add up to less than the source text, the characters left at the end of it go into no column at all; when they add up to more, the overrun is cut to an empty string and leaves a blank column.
Solution: Check that the lengths add up to the length of the source text before cutting, and every entry lands in a column of its own.
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.