.NET (1330)
Children categories

Converting Excel to XML in C# can mean three different things, and the code you write depends on which one you need. You may want to save an entire workbook as an XML spreadsheet, export a few columns into a custom XML structure for another system, or serialize tabular data with .NET's built-in DataTable.WriteXml(). Each produces a different document, and they are not interchangeable.
This tutorial shows how to convert Excel to XML in C# using Spire.XLS for .NET, a standalone library that reads and writes Excel files without Microsoft Excel or Office Interop installed. You will learn to export a whole workbook as XML Spreadsheet 2003, build a custom XML schema from selected cells, and serialize a DataTable, then handle the data type details that matter in production.
Quick Navigation
- Which XML Format Does Excel to XML in C# Produce?
- Set Up a .NET Excel-to-XML Project
- Convert an Excel Workbook to XML in C#
- Export Excel to XML in C# with a Custom Schema
- Convert Excel Data to XML with DataTable.WriteXml()
- XLS, XLSX, and Data Handling
- Frequently Asked Questions
- Summary
Which XML Format Does Excel to XML in C# Produce?
Before writing code, decide which XML the receiving system expects. Three formats come up in Excel work, and mixing them up is the most common mistake in this task.
| XML type | What it is | When to use it |
|---|---|---|
| XML Spreadsheet 2003 (SpreadsheetML) | One XML file describing a workbook, with Workbook, Worksheet, Row, and Cell elements |
When a downstream tool accepts an Excel 2003 XML spreadsheet |
| Office Open XML (OOXML) | The .xlsx format. It is a ZIP package containing separate XML parts for the workbook, worksheets, styles, and shared strings |
When you need to read or modify the internal parts of an XLSX file |
| Custom XML | A document whose element names and hierarchy you define | When an API, database import, or partner system requires a fixed schema |
An XML file whose root is <Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"> is XML Spreadsheet 2003. An XLSX file is a ZIP container holding several XML parts rather than one XML document. Because both are described as "XML" in Excel documentation, the two are easy to confuse.
If you are covering the OOXML package format instead, see convert Excel to Open XML in C#. That is a separate topic from this article. If you do not need code at all, the no-code options are covered in other ways to convert Excel to XML.
Set Up a .NET Excel-to-XML Project
Install Spire.XLS for .NET
dotnet add package Spire.XLS
Import the namespace
using Spire.Xls;
The examples below read SalesOrders.xlsx, which has an Orders sheet with columns OrderID, Customer, Region, Product, Quantity, UnitPrice, Amount, OrderDate, and Status, plus a Summary sheet. Both .xls and .xlsx files load through the same LoadFromFile() call.
The sample workbook used in the examples:

Convert an Excel Workbook to XML in C#
To save a whole workbook as an XML spreadsheet, load the file and call Workbook.SaveAsXml().
using Spire.Xls;
using (Workbook workbook = new Workbook())
{
// Load the Excel workbook.
workbook.LoadFromFile("SalesOrders.xlsx");
// Save the workbook as an XML spreadsheet.
workbook.SaveAsXml("output.xml");
}
SaveToFile("output.xml", FileFormat.XML) produces the same file. In a test with Spire.XLS 16.10.3, the two calls returned documents of identical size and identical MD5 hash, so use whichever reads better in your code.
The output is XML Spreadsheet 2003. An OOXML package is a separate format. A real XML Spreadsheet file starts like this:
<?xml version="1.0"?>
<?mso-application progid="Excel.Sheet"?>
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"
xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
xmlns:x="urn:schemas-microsoft-com:office:excel">
<ss:Styles>...</ss:Styles>
<ss:Worksheet ss:Name="Orders">
<ss:Table ss:DefaultColumnWidth="48.75">
<Row ss:Index="1">
<Cell ss:Index="1"><Data ss:Type="String">OrderID</Data></Cell>
<Cell ss:Index="2"><Data ss:Type="String">Customer</Data></Cell>
<Cell ss:Index="3"><Data ss:Type="String">Region</Data></Cell>
<Cell ss:Index="4"><Data ss:Type="String">Product</Data></Cell>
</Row>
...
The exported orders-spreadsheet.xml opened in a text editor:

To read the file back, use LoadFromXml(). LoadFromXml() reads XML Spreadsheet files, while LoadFromFile() handles XLS and XLSX workbooks.
using (Workbook workbook = new Workbook())
{
// Load an XML Spreadsheet 2003 file.
workbook.LoadFromXml("output.xml");
// Workbook.Worksheets is now available.
}
The export includes the workbook's styles and cell formatting along with the cell data, so the XML document describes the full spreadsheet rather than a bare data table.
Export Excel to XML in C# with a Custom Schema
A workbook export is convenient, but most integrations need their own element names. Converting an Excel spreadsheet to XML with a custom schema usually starts with one range and a fixed set of element names. The sample workbook has these values in the Orders sheet:
| OrderID | Amount | OrderDate |
|---|---|---|
| SO-1001 | 3897.00 | 2026-10-01 |
| SO-1002 | 1645.00 | 2026-10-02 |
| SO-1003 | 2598.00 | 2026-10-03 |
and the target system expects this structure:
<?xml version="1.0" encoding="utf-8"?>
<orders>
<order>
<id>SO-1001</id>
<amount>3897.00</amount>
<orderDate>2026-10-01</orderDate>
</order>
<order>
<id>SO-1002</id>
<amount>1645.00</amount>
<orderDate>2026-10-02</orderDate>
</order>
<order>
<id>SO-1003</id>
<amount>2598.00</amount>
<orderDate>2026-10-03</orderDate>
</order>
</orders>
Export the range to a DataTable, then write the XML yourself with XmlWriter.
using System;
using System.Data;
using System.Globalization;
using System.Text;
using System.Xml;
using Spire.Xls;
using (Workbook workbook = new Workbook())
{
workbook.LoadFromFile("SalesOrders.xlsx");
Worksheet sheet = workbook.Worksheets["Orders"];
var options = new ExportTableOptions
{
ExportColumnNames = true,
KeepDataType = true,
KeepDataFormat = false,
ComputedFormulaValue = true
};
// AllocatedRange covers only the cells actually used.
DataTable table = sheet.ExportDataTable(sheet.AllocatedRange, options);
var settings = new XmlWriterSettings
{
Indent = true,
Encoding = new UTF8Encoding(false)
};
using (XmlWriter writer = XmlWriter.Create("orders-custom.xml", settings))
{
writer.WriteStartDocument();
writer.WriteStartElement("orders");
foreach (DataRow row in table.Rows)
{
writer.WriteStartElement("order");
writer.WriteElementString("id",
Convert.ToString(row["OrderID"], CultureInfo.InvariantCulture));
string amount = row.IsNull("Amount")
? string.Empty
: Convert.ToDecimal(row["Amount"], CultureInfo.InvariantCulture)
.ToString("F2", CultureInfo.InvariantCulture);
writer.WriteElementString("amount", amount);
string orderDate = row.IsNull("OrderDate")
? string.Empty
: Convert.ToDateTime(row["OrderDate"], CultureInfo.InvariantCulture)
.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);
writer.WriteElementString("orderDate", orderDate);
writer.WriteEndElement();
}
writer.WriteEndElement();
writer.WriteEndDocument();
}
}
AllocatedRange covers only the cells in use, so the exported records match the actual data. A fixed range such as sheet.Range["A1:C100"] also includes the empty rows below the data; if you use a fixed range, skip rows where every value is null or empty.
Two options control the output. KeepDataType = true with KeepDataFormat = false keeps Amount as a decimal and OrderDate as a DateTime. With KeepDataFormat = true, the export uses the displayed text instead, such as 2026/10/29. XmlWriter also escapes &, <, and > in cell values, so the output stays well-formed.
If the source columns are numbers stored as text, or dates stored as plain strings, convert them explicitly before writing. Under invariant culture, Convert.ToDecimal expects 1250.50 rather than 1,250.50.
This approach writes the selected data only. When you also need merged cells, column widths, and fonts, use the workbook export in the previous section.
The custom output written to orders-custom.xml:

Convert Excel Data to XML with DataTable.WriteXml()
When the target accepts a standard table-shaped XML document, DataTable.WriteXml() removes the manual element writing.
using System.Data;
using Spire.Xls;
using (Workbook workbook = new Workbook())
{
workbook.LoadFromFile("SalesOrders.xlsx");
Worksheet sheet = workbook.Worksheets[0];
var options = new ExportTableOptions
{
ExportColumnNames = true,
KeepDataType = true,
KeepDataFormat = false
};
DataTable table = sheet.ExportDataTable(sheet.AllocatedRange, options);
table.TableName = "Orders";
table.WriteXml("orders.xml", XmlWriteMode.IgnoreSchema);
}
The output root is <DocumentElement>, with one <Orders> element per row:
<?xml version="1.0" standalone="yes"?>
<DocumentElement>
<Orders>
<OrderID>SO-1001</OrderID>
<Customer>Acme Corp</Customer>
<Region>North</Region>
...
<Amount>3897</Amount>
<OrderDate>2026-10-01T00:00:00+08:00</OrderDate>
<Status>Shipped</Status>
</Orders>
</DocumentElement>
The DataTable output, with one <Orders> element per row:

Date values are written with the local offset, so the same workbook produces a different string on a server in another time zone. Normalize the values first if the consumer expects UTC.
If the receiving system requires a different root, wrap the table in a DataSet. Rename the table first, or you get <Orders><Orders> instead of one element per row:
table.TableName = "Order";
var dataSet = new DataSet("Orders");
dataSet.Tables.Add(table);
dataSet.WriteXml("orders-dataset.xml", XmlWriteMode.IgnoreSchema);
The output now uses the root and record names you chose:
<?xml version="1.0" standalone="yes"?>
<Orders>
<Order>
<OrderID>SO-1001</OrderID>
...
XmlWriteMode.WriteSchema adds an XSD schema block to the document. Use it when the consumer reads the schema.
For more on the export side, see converting Excel data to a DataTable in C#.
XLS, XLSX, and Data Handling
XLS vs XLSX input
Both formats load with LoadFromFile(), and the output format is independent of the input extension. An .xlsx file exported with SaveAsXml() becomes one XML spreadsheet document; the package parts inside the original file are a separate format. Decide the output format first, then pick the method.
Dates and numbers
Excel stores dates as numbers and displays them through a format. When you export through a DataTable, choose between the typed value and the displayed text. For cross-system exchange, keep the typed value and format it at write time, as the earlier yyyy-MM-dd example does.
Formulas
ComputedFormulaValue = true writes the calculated result. Set it to false when the consumer needs the formula expression itself.
Headers and empty cells
ExportColumnNames = true turns the first row into column names. Keep titles, notes, and blank rows out of the selected range. Decide up front whether a missing value becomes an omitted element, an empty element, or an explicit null marker, and choose the form your consumer expects.
Multiple worksheets
A workbook export writes every worksheet. A range export writes one sheet. To produce custom XML from several sheets, loop over the collection and write each sheet into its own parent element. The snippet reuses the settings and options objects defined earlier.
using (XmlWriter writer = XmlWriter.Create("workbook-custom.xml", settings))
{
writer.WriteStartDocument();
writer.WriteStartElement("workbook");
foreach (Worksheet sheet in workbook.Worksheets)
{
DataTable table = sheet.ExportDataTable(sheet.AllocatedRange, options);
writer.WriteStartElement("sheet");
writer.WriteAttributeString("name", sheet.Name);
foreach (DataRow row in table.Rows)
{
writer.WriteStartElement("row");
foreach (DataColumn column in table.Columns)
{
writer.WriteStartElement("field");
writer.WriteAttributeString("name", column.ColumnName);
writer.WriteString(Convert.ToString(row[column], CultureInfo.InvariantCulture));
writer.WriteEndElement();
}
writer.WriteEndElement();
}
writer.WriteEndElement();
}
writer.WriteEndElement();
writer.WriteEndDocument();
}
The column name goes into an attribute rather than an element name on purpose. XML element names cannot contain spaces or start with a digit, so headers such as Order ID or 2026 Total need a safe name; the attribute form accepts any header as written.
To include only populated sheets, check table.Rows.Count before writing the <sheet> element.
The multi-sheet output, with each sheet and column name kept:

Large files
For very large sheets, read rows in chunks, or write directly with XmlWriter while iterating cells.
If you need the reverse direction, see convert XML to Excel and PDF in C#. For CSV input instead of Excel, see convert CSV to XML in C#.
Frequently Asked Questions
How do I convert XLSX to XML in C#?
Load the file with Workbook.LoadFromFile() and call SaveAsXml() to produce an XML Spreadsheet 2003 document. If you need custom element names instead, export the range to a DataTable and write it with XmlWriter or DataTable.WriteXml().
Does SaveAsXml() produce Office Open XML?
SaveAsXml() produces XML Spreadsheet 2003. In the Spire.XLS 16.10.3 test, the output used the urn:schemas-microsoft-com:office:spreadsheet namespace and a Workbook root, which identifies XML Spreadsheet 2003. Office Open XML is the .xlsx package format and is handled as a separate format.
How can I export only one worksheet to XML?
Export the range from that worksheet with Worksheet.ExportDataTable(), then serialize it. A workbook-level SaveAsXml() call always covers the whole workbook.
Can I control the element names?
Yes, with XmlWriter. You define the root, the record element, field names, nesting, and value formatting. DataTable.WriteXml() is faster to write but fixes the structure to the table.
Can I export XLS files the same way?
Yes. .xls and .xlsx both load with LoadFromFile(), and the export steps are the same for an XLS to XML conversion as for an XLSX to XML conversion.
Summary
Converting Excel to XML in C# means choosing which XML the receiving system needs before choosing the API. With Spire.XLS for .NET, SaveAsXml() produces an XML Spreadsheet 2003 document for the whole workbook, XmlWriter produces whatever schema your integration requires, and DataTable.WriteXml() produces a standard table-shaped document with one element per row.
The differences between the three outputs are practical, not cosmetic. XML Spreadsheet 2003 keeps workbook structure and formatting, custom XML keeps only the fields you write, and the DataTable route keeps the table shape but fixes the root and record names. SpreadsheetML and the XML parts inside an XLSX package are different formats, so confirm which one the consumer expects.
Keep data types intact by setting KeepDataType = true and KeepDataFormat = false, select the range with AllocatedRange instead of a fixed block, and decide how missing values should appear before the first file reaches the consumer.
If you need to test Spire.XLS for .NET in a real business environment, you can apply for a temporary license.

Transposing data in Excel (i.e., swapping rows and columns) is a common requirement in data processing, reporting, and ETL workflows. Within desktop Microsoft Excel, you can copy a range and use the Transpose paste option. For .NET‑based applications, you may need to perform this transpose task programmatically without opening Microsoft Excel.
This tutorial walks you through two practical ways to transpose rows and columns in C# using Spire.XLS for .NET , from the built-in copy with transpose option to the native TRANSPOSE array formula.
- Method 1: Transpose Data via Static Copy
- Method 2: Transpose via the Dynamic TRANSPOSE Array Formula
- Excel Transpose Data Method Comparison
- Conclusion: Choosing the Right Method
- Frequently Asked Questions
Prerequisites
- A .NET project (.NET Framework / .NET Core / .NET 5+)
- Install Spire.XLS for .NET via NuGet Package Manager
Install-Package Spire.XLS
Note: Spire.XLS works on Windows, macOS, and Linux environments for cross-platform Excel processing.
Method 1: Transpose Data via Static Copy
Starting with Spire.XLS 16.1.4, the library introduced native support for transposing rows and columns when copying a cell range. It creates a disconnected snapshot of transposed data, preserving cell formatting.
using Spire.Xls;
class Program
{
static void Main()
{
// 1. Create a new workbook
Workbook workbook = new Workbook();
// 2. Load an existing Excel file
workbook.LoadFromFile(@"F:\SalesData.xlsx");
// 3. Get the first worksheet
Worksheet sheet = workbook.Worksheets[0];
// 4. Define the source range (e.g., A1:D5)
CellRange sourceRange = sheet.Range["A1:D5"];
// 5. Define the destination cell (top-left corner)
CellRange destCell = sheet.Range["F7"];
// 6. Set transposition options
CopyRangeOptions options = CopyRangeOptions.Transpose | CopyRangeOptions.All;
// 7. Copy the range with transposition
sourceRange.Copy(destCell, options);
// 8. Save the result
workbook.SaveToFile(@"Transposed.xlsx", ExcelVersion.Version2016);
workbook.Dispose();
}
}
How It Works
CopyRangeOptions.Transpose: Inverts rows and columns for the copied cell range.CopyRangeOptions.All: Copies cell values, formulas, fill styles, borders and font settings. You can combine multiple flags with the bitwise OR (|) operator.- You only specify the top‑left destination cell, and Spire.XLS will automatically calculate the full output range dimensions.
Result Preview:
The source range A1:D5 is transposed to F7:J10. The original rows become columns and the original columns become rows.

Copying Without Styles
If you only need the raw data and want to avoid copying cell formatting, omit CopyRangeOptions.All:
CopyRangeOptions options = CopyRangeOptions.Transpose;
Copying to a Different Worksheet
You can also transpose data into another worksheet:
Worksheet sourceSheet = workbook.Worksheets[0];
Worksheet destSheet = workbook.Worksheets.Add("TransposedData");
CellRange sourceRange = sourceSheet.Range["A1:D5"];
CellRange destCell = destSheet.Range["A1"];
CopyRangeOptions options = CopyRangeOptions.Transpose | CopyRangeOptions.All;
sourceRange.Copy(destCell, options);
Method 2: Transpose via the Dynamic TRANSPOSE Array Formula
Spire.XLS 16.9.2 added support for the Excel TRANSPOSE function. This method is useful when you want the transposed output to remain dynamically linked to the source range—if the source data changes, the transposed data updates automatically (depending on Excel's calculation settings).
using Spire.Xls;
class Program
{
static void Main()
{
// Load an existing Excel file
Workbook workbook = new Workbook();
workbook.LoadFromFile(@"F:\SalesData.xlsx");
// Get the first worksheet
Worksheet sheet = workbook.Worksheets[0];
// Source range: A1:D5 (5 rows × 4 columns)
// Transposed output: F7:J10 (4 rows × 5 columns)
sheet.Range["F7:J10"].FormulaArray = "=TRANSPOSE(A1:D5)";
// Save the result
workbook.SaveToFile("TransposedFormula.xlsx", ExcelVersion.Version2016);
workbook.Dispose();
}
}
Important Notes
- The output range must be sized correctly: if the source is m rows × n columns, the destination must be n rows × m columns. In the example above,
A1:D5is 5×4, soF7:J10is 4×5. - The formula is entered as an array formula using FormulaArray, not the regular Formula property.
- This approach preserves the dynamic link only if the recipient opens the file in Excel with automatic calculation enabled.
- Source‑sheet cell formatting is not copied to formula‑driven output cells.
Result Preview:

Excel Transpose Data Method Comparison
| Feature | Method 1: Static Copy with Transpose | Method 2: TRANSPOSE Array Formula |
|---|---|---|
| Data type | Static copy (values, formats) | Dynamic array formula |
| Auto-update | No | Yes (when source changes) |
| Preserves formatting | Yes | Formula only |
| Output cells | Plain cell values | Embedded formula |
| Best for | One-time transposition, formatting preservation | Editable templates, dynamic dashboards |
Conclusion: Choosing the Right Method
Converting rows to columns or columns to rows in C# is a common requirement that Spire.XLS for .NET handles elegantly. The best method depends on whether you need a static snapshot or a dynamic, formula-driven result.
Pick Method 1 if:
- You need a snapshot of transposed data
- You want to retain cell styles, borders, fills, and formatting
- End users only view the result and won’t edit the source
- You don’t want formulas in the final Excel file
Pick Method 2 if:
- You’re building editable Excel templates for users
- The transposed block needs to refresh when source data changes
- You prefer minimal code and don’t need to copy formatting
Always validate exported Excel outputs against real‑world test data before production deployment. These reusable patterns help streamline row‑and‑column transformation within your .NET applications.
Frequently Asked Questions
Q: Can I transpose a single row or column?
A: Yes. Set the source range to a single row, such as A1:J1, or a single column, such as A1:A10. The output becomes a column or row, respectively.
Q: Does CopyRangeOptions.Transpose work with merged cells?
A: Merged cells can behave unpredictably when transposed. The CopyRangeOptions.Transpose method may preserve them in some cases, but it is safer to unmerge cells before transposing or test the result.
Q: After static transpose, can I still modify the transposed cells independently?
A: Yes. The static transpose output creates independent cell copies. These cells hold plain values and formatting and have no link to the original source cells. Any edits to the transposed region will not affect the source data, and vice versa.
Q: Can I transpose multiple ranges at once?
A: Not in a single built-in call. You can transpose each range separately and place the outputs in different destination areas, or combine the data into one range before transposing.
How to Flatten a PDF in C#: Form Fields, Annotations & Layers
2026-10-12 06:19:50 Written by Allen Yang
Flattening a PDF converts interactive objects into fixed page content. A filled-in form field loses its input control and keeps its value as text, a reviewer's comment becomes part of the page appearance, and optional-content layers are removed by rebuilding each page from its currently visible content. This makes the document less dependent on the interactive features of individual PDF viewers, although the page content itself can still be edited in a PDF editor.
This guide flattens a PDF in C# with Spire.PDF for .NET: the whole form at once, selected fields only, every annotation on every page, and layers by rebuilding each page through SVG. It closes with a verification step, because a flattened file looks very much like the original — the difference is structural, not visual.
Quick Navigation
- Install Spire.PDF for .NET
- What Flattening a PDF Does
- Flatten the Entire PDF Form
- Flatten Selected PDF Form Fields
- Flatten PDF Annotations and Comments
- Flatten PDF Layers Through SVG
- Flatten a PDF Before Printing
- Verify the Flattened PDF
- Putting It Together
- FAQ
- Conclusion
Install Spire.PDF for .NET
Spire.PDF for .NET is available as a NuGet package. Install it using the .NET CLI:
dotnet add package Spire.PDF
These examples were built with Spire.PDF for .NET 12.9.18.
You can also download Spire.PDF for .NET and add it to your project manually.
Three namespaces matter for flattening:
| Namespace | What lives there |
|---|---|
Spire.Pdf |
PdfDocument, PdfPageBase, FileFormat |
Spire.Pdf.Widget, Spire.Pdf.Fields |
PdfFormWidget and PdfField — the form side |
Spire.Pdf.Interactive.Annotations |
PdfAnnotation, PdfAnnotationCollection — the annotation side, including Flatten() |
What Flattening a PDF Does
Flattening presses the appearance of an interactive object into the page content and removes the object. The goal is to preserve the visible appearance while removing the interactivity — which is why this guide counts the objects left in the saved file instead of comparing screenshots.
Each object type needs its own call:
| Object | Flattened with |
|---|---|
| Form fields (text boxes, check boxes, list boxes, combo boxes) | PdfForm.IsFlatten for the whole form, PdfField.Flatten for one field |
| Annotations (comments, highlights, stamps, free text) | PdfAnnotation.Flatten() on each annotation |
| Layers (optional content groups) | PDF → SVG → PDF conversion, which rebuilds every page from what is visible |
Flatten the Entire PDF Form
Set Form.IsFlatten to true and every field in the document is converted in one pass:
using Spire.Pdf;
PdfDocument pdf = new PdfDocument();
pdf.LoadFromFile("FillableForm.pdf");
// Every field and its current value becomes page content.
pdf.Form.IsFlatten = true;
pdf.SaveToFile("FlattenedForm.pdf");
pdf.Close();
On the registration form used here — five fields, three of them filled — the field count drops from 5 to 0. The values stay on the page as selectable text.

Flatten Selected PDF Form Fields
Locking only the fields that are already final is the more common requirement: a completed section becomes read-only while the rest of the form stays fillable. Fields are reached through the FieldsWidget collection of a PdfFormWidget, and Flatten is a boolean property on each one:
using Spire.Pdf;
using Spire.Pdf.Fields;
using Spire.Pdf.Widget;
PdfDocument pdf = new PdfDocument();
pdf.LoadFromFile("FillableForm.pdf");
PdfFormWidget formWidget = pdf.Form as PdfFormWidget;
foreach (PdfField field in formWidget.FieldsWidget)
{
// Flatten the fields that are final, leave the rest fillable.
if (field.Name == "full_name" || field.Name == "email" || field.Name == "department")
{
field.Flatten = true;
}
}
pdf.SaveToFile("PartiallyFlattenedForm.pdf");
pdf.Close();
On the sample form this flattens three fields and leaves start_date and manager editable — the count goes from 5 to 2. Filter on the field names your form defines — read them from FieldsWidget so the comparison matches exactly.

Flattening a field vs. removing a field. Flattening keeps a field's visible appearance — including the value typed into it — and removes only the interactivity. Deleting a form field removes the object and its appearance together. If deletion is the actual goal, see Remove/Delete Form Fields from PDF in C# instead.
Flatten PDF Annotations and Comments
Annotations are the objects reviewers leave on top of a page: text comments, highlights, stamps and free text boxes. In Spire.PDF for .NET they are flattened through the interactive annotation model: create a PdfAnnotationCollection for each page and call Flatten() on each annotation.
using Spire.Pdf;
using Spire.Pdf.Interactive.Annotations;
PdfDocument pdf = new PdfDocument();
pdf.LoadFromFile("ReviewedDocument.pdf");
foreach (PdfPageBase page in pdf.Pages)
{
if (page.Annotations.Count == 0) continue;
PdfAnnotationCollection annotations = PdfAnnotationCollection.Create(page);
// Flatten() removes the annotation from the collection, so walk it backwards.
for (int i = annotations.Count - 1; i >= 0; i--)
{
annotations[i].Flatten();
}
}
pdf.SaveToFile("FlattenedAnnotations.pdf");
pdf.Close();
The reviewed contract page in this guide carries three annotations — a free text note, a rubber stamp and a yellow highlight over a clause. After the loop the annotation count goes from 3 to 0, and all three marks are still drawn on the page: the note keeps its box and colour, the stamp keeps its border, the highlight stays behind the text. Only the interactive objects are gone. The highlight survives because its quad points are read from the saved page rather than from a page still in memory.

Two details in that loop are worth noting. Walk the collection backwards: Flatten() removes each annotation as it is flattened, so a back-to-front loop processes every item exactly once. Flatten() is declared on Spire.Pdf.Interactive.Annotations.PdfAnnotation, the model the annotation collection is created from.
Flatten PDF Layers Through SVG
Optional-content layers are removed by rebuilding each page from its currently visible content: convert each page to SVG and build the PDF back from it. The round trip redraws every page, which drops the layer structure while keeping text as text and graphics as vectors — so the result still selects, searches and scales, unlike a page rendered to an image.
A document with layers converts one page at a time:
using System;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using Spire.Pdf;
// Work in a fresh temp folder for each run.
string workDir = Path.Combine(Path.GetTempPath(),
"flatten_svg_" + Guid.NewGuid().ToString("N").Substring(0, 8));
Directory.CreateDirectory(workDir);
try
{
// Step 1 - save every page as its own SVG file. A multi-page document gives
// page_1.svg, page_2.svg, ...; a single-page document gives page.svg.
PdfDocument source = new PdfDocument();
source.LoadFromFile("LayeredDocument.pdf");
int pageCount = source.Pages.Count;
source.SaveToFile(Path.Combine(workDir, "page.svg"), FileFormat.SVG);
source.Close();
// Sort the files by page number; a single-page document produces page.svg.
string[] svgFiles = Directory.GetFiles(workDir, "*.svg")
.OrderBy(f =>
{
Match m = Regex.Match(Path.GetFileNameWithoutExtension(f), @"\d+$");
return m.Success ? int.Parse(m.Value) : 0;
})
.ToArray();
// One SVG per page — the merge below uses that count.
if (svgFiles.Length != pageCount)
{
throw new IOException(
$"SVG conversion produced {svgFiles.Length} files for {pageCount} pages.");
}
// Step 2 - load each SVG back and save it as a one-page PDF.
string[] pageFiles = new string[svgFiles.Length];
for (int i = 0; i < svgFiles.Length; i++)
{
PdfDocument page = new PdfDocument();
page.LoadFromSvg(svgFiles[i]);
pageFiles[i] = Path.Combine(workDir, "page_" + (i + 1) + ".pdf");
page.SaveToFile(pageFiles[i], FileFormat.PDF);
page.Close();
}
// Step 3 - join the one-page PDFs into a single document, in page order.
PdfDocument.MergeFiles(pageFiles, "FlattenedLayers.pdf");
}
finally
{
Directory.Delete(workDir, true);
}
The file names depend on the page count. A two-page layered document saved to page.svg produces page_1.svg and page_2.svg, while a one-page document produces a single page.svg with no number. On the sample document the round trip keeps both pages in order at the original page size, keeps the text that was drawn on each layer, and the layer count goes from 4 to 0. The same workflow on a single-page document returns one page.

Text and vector graphics survive the round trip, so the rebuilt pages still select, search and scale like the original, and the content that was drawn on each layer stays on the page.
For the two conversion steps on their own, see Convert PDF to SVG in C#, VB.NET and Convert SVG to PDF in C#, VB.NET.
Flatten a PDF Before Printing
Flatten a PDF for printing when the file is going to a printer, a print shop or a viewer you do not control. Once the fields, annotations and layers are ordinary page content, what gets printed is decided by the file rather than by the reader's interactive features.
No separate code is needed. Use the form calls from the form sections when the document has fields, the annotation loop when it has comments, and the SVG workflow when layer visibility could change what lands on paper — then run the output through the checks below before you send it.
Verify the Flattened PDF
A flattened file looks like the original, so confirm the result by counting objects in the saved file rather than by looking at a render:
using Spire.Pdf;
using Spire.Pdf.Fields;
using Spire.Pdf.Widget;
PdfDocument result = new PdfDocument();
result.LoadFromFile("FlattenedForm.pdf");
int fields = 0;
PdfFormWidget form = result.Form as PdfFormWidget;
if (form != null)
{
foreach (PdfField field in form.FieldsWidget) fields++;
}
int annotations = 0;
foreach (PdfPageBase page in result.Pages)
{
annotations += page.Annotations.Count;
}
Console.WriteLine($"pages={result.Pages.Count} fields={fields} "
+ $"annotations={annotations} layers={result.Layers.Count}");
result.Close();
Count fields through PdfFormWidget.FieldsWidget. On the sample files this reports:
| File | Pages | Fields | Annotations | Layers |
|---|---|---|---|---|
FillableForm.pdf (input) |
1 | 5 | 0 | 0 |
FlattenedForm.pdf |
1 | 0 | 0 | 0 |
PartiallyFlattenedForm.pdf |
1 | 2 | 0 | 0 |
ReviewedDocument.pdf (input) |
1 | 0 | 3 | 0 |
FlattenedAnnotations.pdf |
1 | 0 | 0 | 0 |
LayeredDocument.pdf (input) |
2 | 0 | 0 | 4 |
FlattenedLayers.pdf |
2 | 0 | 0 | 0 |
Then check the visual result: open the file in a reader and confirm nothing can be clicked or toggled, compare page count and page size with the original, and select or extract text to confirm it is still real text.
Putting It Together
The snippets above solve one concern each. Here they are as a single method that flattens the form fields and the annotations of any document in one pass:
using Spire.Pdf;
using Spire.Pdf.Interactive.Annotations;
/// <summary>
/// Flatten the form fields and annotations of a PDF in one pass.
/// </summary>
public static void FlattenDocument(string inputFile, string outputFile)
{
PdfDocument pdf = new PdfDocument();
pdf.LoadFromFile(inputFile);
// 1. Form fields: one flag covers every field in the document.
if (pdf.Form != null)
{
pdf.Form.IsFlatten = true;
}
// 2. Annotations: Flatten() removes each one, so walk backwards. Skip pages
// that carry no annotations.
foreach (PdfPageBase page in pdf.Pages)
{
if (page.Annotations.Count == 0) continue;
PdfAnnotationCollection annotations = PdfAnnotationCollection.Create(page);
for (int i = annotations.Count - 1; i >= 0; i--)
{
annotations[i].Flatten();
}
}
pdf.SaveToFile(outputFile);
pdf.Close();
}
The guard keeps the loop on pages that carry annotations, so the same method also works for documents that only have form fields. With it, the method takes the five-field form to 0 fields and the reviewed contract from 3 annotations to 0. Layered documents go through the SVG workflow from the previous section instead.
FAQ
What is the difference between flattening and removing form fields?
Flattening keeps the field's visible appearance, including any entered value, and removes only the interactivity. Removing a form field deletes the field object and its appearance together.
Can I flatten only some form fields?
Yes. Cast pdf.Form to PdfFormWidget, walk FieldsWidget, and set Flatten = true on the fields you name. The rest stay fillable.
Does flattening keep the text selectable and searchable?
Yes. A flattened text field keeps its value in the page's text layer, and the SVG round trip keeps the text it redraws, so the values stay selectable, copyable and searchable.
Is flattening reversible?
Flattening writes the fields and annotations into the page, so keep the source PDF if you may need to edit them later.
Do I need to flatten before printing?
Not always, but it removes the dependency on the reader. Flattening can make the output more consistent across PDF viewers, because the visible content no longer depends on interactive fields, annotations or layer visibility settings.
Conclusion
Flattening a PDF in C# is one call per object type:
pdf.Form.IsFlatten = trueflattens every field in the document;PdfField.Flattenflattens the ones you name.PdfAnnotationCollection.Create(page)plusFlatten()on each item, walked backwards, flattens every comment, highlight and stamp on the page.- Layers are flattened by saving each page as SVG, loading it back and merging the pages with
PdfDocument.MergeFiles.
The page keeps its appearance either way, so verify by counting the objects left in the saved file — not by comparing screenshots.
If you need to test Spire.PDF for .NET in a real business environment, you can apply for a temporary license.

Your application receives a PDF invoice. You need the invoice number, vendor name, and line items — not as text on a page, but as structured JSON your API can consume. That is the real problem behind PDF to JSON conversion.
Unlike CSV or XML, a PDF file has no inherent data structure — no fields, no rows, no schema. Extracting usable JSON requires different approaches depending on what the document actually contains: plain text with key-value patterns, tables with rows and columns, fillable form fields, or scanned images that need OCR.
This article covers all four scenarios with runnable C# code using Spire.PDF for .NET. We build a real invoice-to-JSON converter, handle common table extraction problems like merged cells and missing headers, and package everything into a reusable PdfToJsonConverter class you can drop into any .NET project.
Quick Navigation
- What "PDF to JSON" Actually Means
- Install Spire.PDF for .NET
- Convert PDF Text to JSON in C#
- Convert PDF Tables to JSON in C#
- Convert PDF Form Fields to JSON
- Invoice PDF to JSON: A Real-World Example
- Convert Multiple PDFs to JSON in Batch
- Build a PDF to JSON Converter in C#
- Convert OCR Output to JSON in C#
- Performance Considerations
- FAQ
1. What "PDF to JSON" Actually Means
There is no built-in "PDF to JSON" conversion in the way you might convert a CSV to JSON. A PDF has no JSON structure. What developers actually need is: extract content from a PDF, then shape that content into a JSON format that matches their use case.
Depending on the PDF type and business requirement, the target JSON falls into one of three categories.
Raw Text JSON
Pull all text from each page and wrap it in a JSON envelope. Works for search indexing, RAG pipelines, and document archival.
{
"sourceFile": "Contract.pdf",
"pages": [
{ "pageNumber": 1, "text": "SERVICE AGREEMENT\nBetween Contoso Ltd and..." }
]
}
Key-Value JSON
Many PDFs follow a Label: Value pattern — employee records, registration forms, simple invoices. The goal here is to parse those pairs into a flat JSON object:
{
"name": "John Smith",
"email": "john@contoso.com",
"department": "Engineering",
"employeeId": "EMP-2026-0142"
}
Structured Business JSON
Real business documents have nested data: an invoice has a header, line items, tax breakdowns, and payment terms. The JSON output needs to mirror that structure:
{
"invoiceNumber": "INV-2026-0042",
"vendor": "Contoso Ltd",
"date": "2026-06-15",
"lineItems": [
{ "description": "Widget A", "quantity": 150, "unitPrice": 24.50, "total": 3675.00 }
],
"subtotal": 3675.00,
"tax": 294.00,
"total": 3969.00
}
This distinction matters. When you search for "convert PDF to JSON," you need to decide which output format your application requires. The rest of this article shows how to build each one using Spire.PDF in C#.
2. Install Spire.PDF for .NET
Install via NuGet Package Manager Console:
Install-Package Spire.PDF
Or add to your .csproj:
<PackageReference Include="Spire.PDF" Version="*" />
Include these namespaces in your project:
using Spire.Pdf;
using Spire.Pdf.Texts;
using Spire.Pdf.Utilities;
using Spire.Pdf.Fields;
using Spire.Pdf.Widget;
using System.Text.Json;
using System.Text.Json.Serialization;
Spire.PDF supports .NET Framework, .NET Core, and .NET 6/7/8/9+.
3. Convert PDF Text to JSON in C#
The most common starting point: extract text from a PDF and produce JSON output.
Extract Text from PDF
using Spire.Pdf;
using Spire.Pdf.Texts;
using System.Collections.Generic;
using (PdfDocument pdf = new PdfDocument())
{
pdf.LoadFromFile("EmployeeRecord.pdf");
var pages = new List<Dictionary<string, string>>();
for (int i = 0; i < pdf.Pages.Count; i++)
{
PdfPageBase page = pdf.Pages[i];
PdfTextExtractOptions options = new PdfTextExtractOptions();
options.IsExtractAllText = true;
PdfTextExtractor extractor = new PdfTextExtractor(page);
string pageText = extractor.ExtractText(options);
pages.Add(new Dictionary<string, string>
{
{ "pageNumber", (i + 1).ToString() },
{ "text", pageText.Trim() }
});
}
}
Parse Key-Value Pairs into JSON
If your PDF follows a Label: Value pattern, parse the extracted text into structured fields:
using System.Text.Json;
var parsedFields = new Dictionary<string, string>();
foreach (var page in pages)
{
string[] lines = page["text"].Split('\n');
foreach (string line in lines)
{
int colonIndex = line.IndexOf(':');
if (colonIndex > 0)
{
string key = line.Substring(0, colonIndex).Trim();
string value = line.Substring(colonIndex + 1).Trim();
parsedFields[key] = value;
}
}
}
var jsonOptions = new JsonSerializerOptions
{
WriteIndented = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
string jsonOutput = JsonSerializer.Serialize(parsedFields, jsonOptions);
File.WriteAllText("EmployeeRecord.json", jsonOutput);
Key API Calls
PdfDocument.LoadFromFile()— opens the PDF filePdfTextExtractor.ExtractText()— extracts text content from a pagePdfTextExtractOptions.IsExtractAllText— preserves whitespace and formatting
Output
The following example shows the structured JSON generated from the extracted employee record.
{
"name": "John Smith",
"email": "john.smith@contoso.com",
"department": "Engineering",
"employeeId": "EMP-2026-0142",
"startDate": "2024-03-15"
}
The following screenshot shows the actual JSON file generated after running the example.

This approach works well for forms, records, and documents with consistent key-value layouts. For unstructured text, skip the parsing step and serialize the raw pages directly.
If you need a deeper look at PDF text extraction, see our dedicated guide on extracting text from PDFs in C# using Spire.PDF for .NET.
4. Convert PDF Tables to JSON in C#
The previous section focused on extracting plain text from PDFs. While that works well for paragraphs and simple records, many business documents organize their most valuable information in tables, such as invoice line items, sales reports, and financial statements. To preserve rows, columns, and relationships between cells, table data must be extracted differently before it can be converted into structured JSON.
Why Table Extraction Is Different from Text Extraction
Text extraction returns a flat stream of characters in reading order. Although a table may appear perfectly organized on the page, the extracted text often loses its row-and-column structure, making it difficult to identify which values belong together.
To preserve the table layout, you need a dedicated table extraction engine. PdfTableExtractor analyzes the page layout, detects table boundaries, and returns PdfTable objects that you can iterate row by row and cell by cell. Instead of producing a flat string such as:
Widget A 150 $24.50 $3,675.00
it enables you to generate structured JSON like:
{
"Product": "Widget A",
"Quantity": "150",
"Unit Price": "$24.50",
"Total": "$3,675.00"
}
The following example demonstrates how to extract tables from a PDF and serialize them into JSON.
Extract Tables from PDF
using Spire.Pdf;
using Spire.Pdf.Utilities;
using System.Collections.Generic;
using (PdfDocument pdf = new PdfDocument())
{
pdf.LoadFromFile("SalesReport.pdf");
PdfTableExtractor tableExtractor = new PdfTableExtractor(pdf);
var allTables = new List<List<List<string>>>();
for (int pageIndex = 0; pageIndex < pdf.Pages.Count; pageIndex++)
{
PdfTable[] tables = tableExtractor.ExtractTable(pageIndex);
if (tables != null && tables.Length > 0)
{
foreach (PdfTable table in tables)
{
int rowCount = table.GetRowCount();
int colCount = table.GetColumnCount();
var tableData = new List<List<string>>();
for (int row = 0; row < rowCount; row++)
{
var rowData = new List<string>();
for (int col = 0; col < colCount; col++)
{
rowData.Add(table.GetText(row, col).Trim());
}
tableData.Add(rowData);
}
allTables.Add(tableData);
}
}
}
}
Serialize Table Data to JSON
var jsonTables = new List<object>();
foreach (var tableData in allTables)
{
if (tableData.Count < 2) continue;
var headers = tableData[0];
var rows = new List<Dictionary<string, string>>();
for (int i = 1; i < tableData.Count; i++)
{
var rowObj = new Dictionary<string, string>();
for (int j = 0; j < headers.Count && j < tableData[i].Count; j++)
{
rowObj[headers[j]] = tableData[i][j];
}
rows.Add(rowObj);
}
jsonTables.Add(new
{
tableIndex = allTables.IndexOf(tableData) + 1,
headers = headers,
data = rows
});
}
string tableJson = JsonSerializer.Serialize(new
{
sourceFile = "SalesReport.pdf",
tables = jsonTables
}, new JsonSerializerOptions { WriteIndented = true });
File.WriteAllText("SalesReport_Tables.json", tableJson);
Key API Calls
PdfTableExtractor(PdfDocument)— initializes the table extraction enginePdfTableExtractor.ExtractTable(pageIndex)— detects and extracts tables from a pagePdfTable.GetRowCount()/GetColumnCount()— returns table dimensionsPdfTable.GetText(row, col)— reads cell content
Sample JSON Output
The resulting JSON preserves the original table structure by organizing each row into key-value pairs based on the detected column headers.
{
"sourceFile": "SalesReport.pdf",
"tables": [
{
"tableIndex": 1,
"headers": ["Product", "Quantity", "Unit Price", "Total"],
"data": [
{ "Product": "Widget A", "Quantity": "150", "Unit Price": "$24.50", "Total": "$3,675.00" },
{ "Product": "Widget B", "Quantity": "80", "Unit Price": "$39.90", "Total": "$3,192.00" }
]
}
]
}
The following screenshot shows the actual JSON file generated after running the example.

This approach works well for invoices, reports, and other PDFs with well-defined table structures. For documents containing merged cells, missing headers, or multi-page tables, additional post-processing may be required.
If you need a deeper look at PDF table extraction, see our dedicated guide on extracting tables from PDFs in C# using Spire.PDF for .NET.
Common Table Extraction Problems
Real-world PDF tables are messy. Here are the three problems you will hit most often, and how to handle them.
Problem 1: Missing Headers
Many invoices and reports have tables without explicit header rows. The data starts immediately:
Apple 10 $2.99 $29.90
Orange 5 $1.50 $7.50
When the first row is data rather than headers, assign column names manually based on your known schema:
// Define headers when the PDF table has no header row
string[] defaultHeaders = { "Product", "Quantity", "UnitPrice", "Total" };
var rows = new List<Dictionary<string, string>>();
for (int i = 0; i < tableData.Count; i++) // Start from 0, not 1
{
var rowObj = new Dictionary<string, string>();
for (int j = 0; j < defaultHeaders.Length && j < tableData[i].Count; j++)
{
rowObj[defaultHeaders[j]] = tableData[i][j];
}
rows.Add(rowObj);
}
Problem 2: Merged Cells
Tables in financial reports often have merged cells for grouping:
Quarter Revenue Expenses
Q1 $120,000 $95,000
$115,000 $88,000
Q2 $140,000 $102,000
The extractor returns empty strings for merged cells. Fill them forward from the last non-empty value:
// Fill merged cells with the previous row's value
for (int col = 0; col < headers.Count; col++)
{
string lastValue = "";
for (int row = 1; row < tableData.Count; row++)
{
if (col < tableData[row].Count && !string.IsNullOrWhiteSpace(tableData[row][col]))
{
lastValue = tableData[row][col];
}
else if (col < tableData[row].Count)
{
tableData[row][col] = lastValue;
}
}
}
Problem 3: Multi-Page Tables
Enterprise reports often have a single table spanning multiple pages, with the header row repeated on each page. Handle this by deduplicating headers during serialization:
var combinedRows = new List<Dictionary<string, string>>();
string[] expectedHeaders = null;
for (int pageIndex = 0; pageIndex < pdf.Pages.Count; pageIndex++)
{
PdfTable[] tables = tableExtractor.ExtractTable(pageIndex);
if (tables == null) continue;
foreach (PdfTable table in tables)
{
for (int r = 0; r < table.GetRowCount(); r++)
{
var cells = new List<string>();
for (int c = 0; c < table.GetColumnCount(); c++)
{
cells.Add(table.GetText(r, c).Trim());
}
// First row of first page becomes the headers
if (expectedHeaders == null && r == 0)
{
expectedHeaders = cells.ToArray();
continue;
}
// Skip repeated header rows on subsequent pages
if (r == 0 && cells.SequenceEqual(expectedHeaders))
continue;
var rowDict = new Dictionary<string, string>();
for (int c = 0; c < expectedHeaders.Length && c < cells.Count; c++)
{
rowDict[expectedHeaders[c]] = cells[c];
}
combinedRows.Add(rowDict);
}
}
}
5. Convert PDF Form Fields to JSON
Unlike plain text or tables, fillable PDF forms already store data as named fields. Applications, surveys, and registration forms contain field names and values that can be mapped directly to JSON key-value pairs, making form data one of the easiest types of PDF content to serialize.
Read and Export Form Fields
using Spire.Pdf;
using Spire.Pdf.Fields;
using Spire.Pdf.Widget;
using System.Collections.Generic;
using (PdfDocument pdf = new PdfDocument())
{
pdf.LoadFromFile("RegistrationForm.pdf");
PdfFormWidget formWidget = pdf.Form as PdfFormWidget;
var formData = new Dictionary<string, object>();
if (formWidget != null)
{
for (int i = 0; i < formWidget.FieldsWidget.List.Count; i++)
{
PdfField field = formWidget.FieldsWidget.List[i] as PdfField;
if (field is PdfTextBoxFieldWidget textBox)
formData[textBox.Name] = textBox.Text;
else if (field is PdfCheckBoxWidgetFieldWidget checkBox)
formData[checkBox.Name] = checkBox.Checked;
else if (field is PdfRadioButtonListFieldWidget radioButton)
formData[radioButton.Name] = radioButton.Value;
else if (field is PdfComboBoxWidgetFieldWidget comboBox)
formData[comboBox.Name] = comboBox.SelectedValue;
else if (field is PdfListBoxWidgetFieldWidget listBox)
{
var selectedItems = new List<string>();
foreach (PdfListWidgetItem item in listBox.Values)
selectedItems.Add(item.Value);
formData[listBox.Name] = selectedItems;
}
}
}
var formOutput = new
{
sourceFile = "RegistrationForm.pdf",
fieldCount = formData.Count,
fields = formData
};
string json = JsonSerializer.Serialize(formOutput, new JsonSerializerOptions
{
WriteIndented = true
});
File.WriteAllText("RegistrationForm_Data.json", json);
}
Key API Calls
PdfFormWidget— provides access to the document's interactive formPdfTextBoxFieldWidget.Text— reads text input valuesPdfCheckBoxWidgetFieldWidget.Checked— reads checkbox statePdfRadioButtonListFieldWidget.Value— reads selected radio buttonPdfComboBoxWidgetFieldWidget.SelectedValue— reads combo box selection
Output
The following example shows how the extracted form fields are represented as structured JSON.
{
"sourceFile": "RegistrationForm.pdf",
"fieldCount": 6,
"fields": {
"FullName": "John Smith",
"Email": "john.smith@contoso.com",
"Department": "Sales",
"AgreeTerms": true,
"SubscriptionPlan": "Enterprise",
"Skills": ["C#", "SQL", "Azure"]
}
}
The following screenshot shows the actual JSON file generated after exporting the form data.

This approach works well for interactive PDF forms that contain structured fields such as text boxes, check boxes, radio buttons, and drop-down lists. Because each field already has a unique name, the extracted data can be serialized directly into JSON without additional parsing.
If you need a deeper look at importing and exporting PDF form field data in C#, see our dedicated guide on working with PDF form fields using Spire.PDF for .NET.
6. Invoice PDF to JSON: A Real-World Example
Invoice processing is one of the most common business use cases for PDF to JSON conversion. Instead of presenting a full parser implementation, this section demonstrates how the extraction techniques from Sections 3 and 4 come together to solve a real problem.
Target JSON Structure
Before writing any extraction code, define your target schema. For a typical invoice, the JSON output might look like this:
{
"invoiceNumber": "INV-2026-0042",
"date": "2026-06-15",
"vendor": "Contoso Ltd",
"paymentTerms": "Net 30",
"lineItems": [
{ "description": "Widget A", "quantity": 150, "unitPrice": 24.50, "total": 3675.00 },
{ "description": "Widget B", "quantity": 80, "unitPrice": 39.90, "total": 3192.00 }
],
"subtotal": 8367.00,
"tax": 669.36,
"total": 9036.36
}
Extraction Pattern
Use text extraction (Section 3) to parse header fields via regex, and table extraction (Section 4) to pull line items:
// Parse header fields from extracted text using regex
invoice["invoiceNumber"] = Regex.Match(fullText, @"Invoice Number:\s*(\S+)").Groups[1].Value;
invoice["date"] = Regex.Match(fullText, @"Date:\s*(\S+)").Groups[1].Value;
invoice["vendor"] = Regex.Match(fullText, @"Vendor:\s*(.+)").Groups[1].Value;
// Extract line items from table data (Section 4 pattern)
for (int r = 1; r < table.GetRowCount(); r++)
{
lineItems.Add(new
{
description = table.GetText(r, 0).Trim(),
quantity = int.Parse(table.GetText(r, 1).Trim()),
unitPrice = ParseCurrency(table.GetText(r, 2)),
total = ParseCurrency(table.GetText(r, 3))
});
}
The implementation combines the text extraction introduced in Section 3 with the table extraction introduced in Section 4. Regex is used only for simple field matching — the core PDF processing relies entirely on Spire.PDF APIs.
Handling Different Invoice Layouts
In production, you rarely deal with a single invoice format:
- Fixed template + regex — works when you control the source or process invoices from a known vendor
- Template matching — maintain a set of regex patterns, one per vendor
- AI-assisted extraction — for unknown or highly variable layouts, combine OCR output with an LLM
Regex-based parsing is fast and reliable for known formats. For a production-ready implementation, extend the PdfToJsonConverter class from Section 8 to build a dedicated invoice parser that reuses the same extraction patterns.
7. Convert Multiple PDFs to JSON in Batch
Production workflows process hundreds or thousands of PDFs at once. This batch processor handles errors gracefully and logs results:
using Spire.Pdf;
using Spire.Pdf.Texts;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
string inputDir = @"C:\PDFs\Invoices";
string outputDir = @"C:\Output\JSON";
Directory.CreateDirectory(outputDir);
string[] pdfFiles = Directory.GetFiles(inputDir, "*.pdf");
var results = new List<object>();
foreach (string pdfPath in pdfFiles)
{
string fileName = Path.GetFileNameWithoutExtension(pdfPath);
string outputPath = Path.Combine(outputDir, $"{fileName}.json");
try
{
using (PdfDocument pdf = new PdfDocument())
{
pdf.LoadFromFile(pdfPath);
var pageTexts = new List<string>();
for (int i = 0; i < pdf.Pages.Count; i++)
{
var extractor = new PdfTextExtractor(pdf.Pages[i]);
var options = new PdfTextExtractOptions { IsExtractAllText = true };
pageTexts.Add(extractor.ExtractText(options).Trim());
}
var doc = new
{
sourceFile = Path.GetFileName(pdfPath),
pageCount = pdf.Pages.Count,
processedAt = DateTime.UtcNow,
content = pageTexts
};
File.WriteAllText(outputPath, JsonSerializer.Serialize(doc,
new JsonSerializerOptions { WriteIndented = true }));
results.Add(new { file = fileName, status = "success" });
}
}
catch (Exception ex)
{
results.Add(new { file = fileName, status = "error", error = ex.Message });
}
}
File.WriteAllText(Path.Combine(outputDir, "_log.json"),
JsonSerializer.Serialize(results, new JsonSerializerOptions { WriteIndented = true }));
Swap the text-only extraction with the invoice JSON extraction pattern from Section 6 if your batch consists of invoices, or with the PdfToJsonConverter class from Section 8 for general-purpose conversion.
8. Build a PDF to JSON Converter in C#
For production applications, encapsulate all extraction logic into a single class. The PdfToJsonConverter below combines text, table, and form field extraction into one reusable PDF to JSON converter:
using Spire.Pdf;
using Spire.Pdf.Texts;
using Spire.Pdf.Utilities;
using Spire.Pdf.Fields;
using Spire.Pdf.Widget;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
public class PdfToJsonConverter
{
private readonly JsonSerializerOptions _jsonOptions = new()
{
WriteIndented = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull
};
public string ConvertToJson(string pdfPath)
{
using (PdfDocument pdf = new PdfDocument())
{
pdf.LoadFromFile(pdfPath);
var result = new
{
sourceFile = Path.GetFileName(pdfPath),
processedAt = DateTime.UtcNow,
text = ExtractText(pdf),
tables = ExtractTables(pdf),
formFields = ExtractFormFields(pdf)
};
return JsonSerializer.Serialize(result, _jsonOptions);
}
}
public void ConvertAndSave(string pdfPath, string outputPath)
{
File.WriteAllText(outputPath, ConvertToJson(pdfPath));
}
// Reuses the text extraction technique from Section 3 (PdfTextExtractor + PdfTextExtractOptions)
private List<PageText> ExtractText(PdfDocument pdf) { return new List<PageText>(); }
// Reuses the table extraction technique from Section 4 (PdfTableExtractor + ExtractTable)
private List<TableData> ExtractTables(PdfDocument pdf) { return new List<TableData>(); }
// Reuses the form field extraction technique from Section 5 (PdfFormWidget + field type checking)
private Dictionary<string, object> ExtractFormFields(PdfDocument pdf) { return new Dictionary<string, object>(); }
}
public class PageText
{
public int PageNumber { get; set; }
public string Text { get; set; }
}
public class TableData
{
public int PageNumber { get; set; }
public int RowCount { get; set; }
public List<List<string>> Rows { get; set; }
}
Usage
var converter = new PdfToJsonConverter();
// Single file
converter.ConvertAndSave("InvoiceReport.pdf", "InvoiceReport.json");
// Use inside an ASP.NET controller
[HttpPost("api/pdf-to-json")]
public IActionResult ConvertPdf(IFormFile file)
{
var tempPath = Path.GetTempFileName();
file.CopyTo(new FileStream(tempPath, FileMode.Create));
var converter = new PdfToJsonConverter();
string json = converter.ConvertToJson(tempPath);
return Content(json, "application/json");
}
The helper methods (ExtractText, ExtractTables, ExtractFormFields) reuse the extraction techniques introduced in Sections 3–5. Refer to those sections for the full implementations.
Best Practices for Production Pipelines
When building PDF to JSON conversion into a production system:
- Define your JSON schema first. Map each PDF element to a target field before writing extraction code.
- Validate extracted data. Currency strings, dates, and IDs should be parsed and verified before serialization.
- Handle missing values. Use
JsonIgnoreCondition.WhenWritingNullto omit null fields from output. - Include metadata. Always record source file name, page numbers, and extraction timestamp for auditing.
- Clean text artifacts. Trim whitespace, normalize line breaks, and handle encoding issues in extracted strings.
9. Convert OCR Output to JSON in C#
Scanned PDFs contain images rather than selectable text, so they must be processed with an OCR engine before they can be converted to JSON. Spire.PDF handles PDF rendering and page processing, while text recognition should be performed by an OCR solution such as Tesseract or Azure AI Vision.
For a complete walkthrough, see How to Extract Text from Scanned PDFs in C#.
Once OCR returns the recognized text, you can parse it using the same techniques shown earlier in this article.
Parse OCR Text into JSON
string recognizedText = ocrEngine.Recognize(imagePath);
// Parse recognized text using the same helper methods demonstrated in previous examples.
var parsedData = ParseRecognizedText(recognizedText);
string json = JsonSerializer.Serialize(parsedData, new JsonSerializerOptions
{
WriteIndented = true
});
Best Practices
- Scan documents at 300 DPI or higher for better OCR accuracy.
- Validate important fields such as invoice numbers, dates, and currency values before serialization.
- Reuse the parsing patterns introduced earlier in this article to build consistent JSON structures.
10. Performance Considerations
PDF to JSON conversion works fine for a single 5-page document. In production, you are processing hundreds of files with hundreds of pages each. These are the issues you will actually hit.
Large PDFs (100+ Pages)
Avoid loading all page text into a List<string> before serialization. Process and write each page incrementally:
using (var stream = File.Create("output.json"))
using (var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Indented = true }))
{
writer.WriteStartObject();
writer.WriteString("sourceFile", Path.GetFileName(pdfPath));
writer.WriteStartArray("pages");
for (int i = 0; i < pdf.Pages.Count; i++)
{
var extractor = new PdfTextExtractor(pdf.Pages[i]);
var options = new PdfTextExtractOptions { IsExtractAllText = true };
string text = extractor.ExtractText(options).Trim();
writer.WriteStartObject();
writer.WriteNumber("pageNumber", i + 1);
writer.WriteString("text", text);
writer.WriteEndObject();
}
writer.WriteEndArray();
writer.WriteEndObject();
}
Utf8JsonWriter writes directly to the stream instead of building a string in memory. For a 500-page document, this can cut peak memory usage by 60-70% compared to JsonSerializer.Serialize().
Memory Usage
PdfDocument holds parsed page trees, fonts, and image references in memory. Two rules:
- Always wrap
PdfDocumentinusing— it releases unmanaged resources on dispose - Process one document at a time — do not keep multiple
PdfDocumentinstances open simultaneously unless you have the RAM for it
For batch jobs processing 1000+ files, the using pattern inside the loop ensures each document is fully released before the next one loads.
Parallel Processing
Batch conversion is CPU-bound and parallelizes well:
var pdfFiles = Directory.GetFiles(inputDir, "*.pdf");
Parallel.ForEach(pdfFiles,
new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount },
pdfPath =>
{
string outputPath = Path.Combine(outputDir,
Path.GetFileNameWithoutExtension(pdfPath) + ".json");
var converter = new PdfToJsonConverter();
converter.ConvertAndSave(pdfPath, outputPath);
});
Each thread creates its own PdfToJsonConverter and PdfDocument instance. PdfDocument is not thread-safe — never share a single instance across threads.
When to Use Streaming JSON
Use Utf8JsonWriter over JsonSerializer.Serialize() when:
- Output JSON exceeds 50 MB
- You are processing PDFs with 200+ pages
- Running in a memory-constrained environment (container with 512 MB limit)
For smaller documents, JsonSerializer is simpler and the memory difference is negligible.
11. FAQ
Can I convert PDF to JSON in C# for free?
Spire.PDF for .NET offers a free evaluation version with a page limit. For production use, you can apply for a 30-day free license or purchase a commercial license. The System.Text.Json serializer is built into .NET and free.
Can scanned PDFs be converted to JSON?
Yes, but you need an external OCR engine. Spire.PDF renders PDF pages as images via SaveAsImage(), which you then pass to Tesseract, Azure Computer Vision, or Amazon Textract for text recognition. The recognized text is then parsed and serialized to JSON. See Section 9 for the integration pattern.
Can I convert PDF tables to JSON automatically?
Yes. PdfTableExtractor automatically detects table structures on each page without manual configuration. It handles both properly structured tables (created in Word or Excel) and visual tables (text aligned to look like rows and columns). For multi-page tables or tables without headers, see the handling patterns in Section 4.
Can I batch convert multiple PDFs to JSON?
Yes. Iterate through a directory using Directory.GetFiles(), process each PDF with Spire.PDF extraction APIs, and save individual JSON files. Include error handling so one failed file does not stop the batch. See Section 7 for a complete example.
How can I convert large PDF files to JSON in C#?
Process the PDF page-by-page rather than loading all content into memory at once. For very large files (100+ pages), use Utf8JsonWriter to write JSON incrementally to a stream instead of building the entire output in memory. See Section 10 for the streaming JSON pattern and parallel processing approach.
Can I convert PDF to JSON using an API?
Yes. You can wrap the PdfToJsonConverter class from this article in an ASP.NET Web API endpoint. Accept a PDF upload, run the extraction, and return the JSON response. Spire.PDF works in any .NET hosting environment — ASP.NET Core, Azure Functions, AWS Lambda, or a self-hosted console app. See the ASP.NET controller example in Section 8.
Conclusion
PDF to JSON is not a single operation. Depending on your document, you are solving one of three different problems: wrapping raw text in a JSON envelope, parsing key-value patterns into flat objects, or building structured business JSON from text and table extraction.
This article covered all three, plus the complications that break naive implementations: tables without headers, merged cells, multi-page tables, fillable form fields, varying invoice layouts, batch processing, memory management for large documents, and OCR integration boundaries.
The PdfToJsonConverter class is a starting point you can adapt to your document types. The invoice extraction pattern shown in Section 6 demonstrates how to combine these techniques for real business documents. Both use Spire.PDF for .NET, which handles all PDF reading locally without external dependencies.
To get started:
- Install via NuGet:
Install-Package Spire.PDF - Apply for a 30-day free license to evaluate without page limits
- Explore the Spire.PDF documentation for additional extraction scenarios

Formatting plays an important role in making Excel spreadsheets easier to read, analyze, and present. Whether you are generating reports, invoices, financial statements, or dashboards, raw data often needs proper styling before it can be shared with end users.
In C#, Excel formatting tasks may include changing fonts, applying colors, aligning content, formatting numbers and dates, adding borders, creating tables, and configuring page layouts. Performing these tasks manually can be time-consuming, especially when dealing with large volumes of spreadsheets.
Spire.XLS for .NET provides a comprehensive set of APIs for creating, editing, formatting, and converting Excel documents without requiring Microsoft Excel to be installed. In this article, you will learn how to apply various types of formatting to Excel files in C# using Spire.XLS for .NET.
Table of Contents:
- Prepare Your C# Project for Excel Formatting
- Part 1. Format Cell Appearance
- Part 2. Format Cell Values
- Part 3. Format Ranges and Layout
- Part 4. Apply Advanced Formatting
- Part 5. Format Excel Tables and Worksheets
- Part 6. Create a Professional Report Example
- Conclusion
- FAQs
Prepare Your C# Project for Excel Formatting
Spire.XLS for .NET is a powerful Excel library that enables developers to work with XLS, XLSX, XLSM, CSV, and other spreadsheet formats programmatically. Besides formatting operations, it also supports formula calculation, chart creation, pivot tables, worksheet management, printing, and file conversion.
To install Spire.XLS for .NET, run the following NuGet command:
Install-Package Spire.XLS
Before applying formatting, load an existing Excel workbook (or create a new one) and access the worksheet you want to modify. Once all formatting operations are complete, save the result to a new Excel file using the SaveToFile() method.
using Spire.Xls;
using System.Drawing;
// Create a workbook object
Workbook workbook = new Workbook();
// Load an existing Excel file
workbook.LoadFromFile("input.xlsx");
// Get a specific worksheet
Worksheet sheet = workbook.Worksheets[0];
// Apply formatting
...
// Save the result
workbook.SaveToFile("output.xlsx", ExcelVersion.Version2016);
Note: The following examples assume that the workbook has already been loaded and the worksheet object has been obtained.
Part 1. Format Cell Appearance
Cell appearance settings control how data looks inside a worksheet. Proper formatting can significantly improve readability and help users quickly identify important information.
Format Cell Fonts
Font formatting allows you to customize the visual style of cell content. Common options include font family, font size, bold, italic, underline, and font color. These settings are frequently used for report titles, section headers, and highlighted values.
CellStyle style = workbook.Styles.Add("FontStyle");
style.Font.FontName = "Calibri";
style.Font.Size = 14;
style.Font.IsBold = true;
style.Font.IsItalic = true;
style.Font.Underline = FontUnderlineType.Single;
style.Font.Color = Color.Blue;
sheet.Range["A1"].Text = "Formatted Text";
sheet.Range["A1"].Style = style;
Set Cell Background Colors
Background colors help distinguish different sections of a worksheet and draw attention to key cells. For example, you may use a colored header row or highlight summary data with a contrasting fill color.
sheet.Range["A2"].Text = "Background Color";
sheet.Range["A2"].Style.Color = Color.LightSkyBlue;
Align Cell Content
Excel provides horizontal alignment, vertical alignment, indentation, and text rotation options. Proper alignment improves the overall layout of a worksheet and makes tabular data easier to scan.
sheet.Range["B2"].Text = "Centered Text";
CellStyle style = sheet.Range["B2"].Style;
style.HorizontalAlignment = HorizontalAlignType.Center;
style.VerticalAlignment = VerticalAlignType.Center;
style.Rotation = 45;
sheet.SetRowHeight(2, 40);
sheet.SetColumnWidth(2, 20);
Add Cell Borders
Borders are useful for separating rows and columns and defining table structures. Depending on the scenario, you can apply borders to individual cells or entire ranges and customize their styles and colors.
CellRange range = sheet.Range["A4:D6"];
range.Text = "Border";
range.Style.Borders[BordersLineType.EdgeTop].LineStyle = LineStyleType.Thin;
range.Style.Borders[BordersLineType.EdgeBottom].LineStyle = LineStyleType.Thin;
range.Style.Borders[BordersLineType.EdgeLeft].LineStyle = LineStyleType.Thin;
range.Style.Borders[BordersLineType.EdgeRight].LineStyle = LineStyleType.Thin;
range.Style.Borders[BordersLineType.EdgeTop].Color = Color.Black;
Wrap Text
When cell content exceeds the available column width, wrapping text allows multiple lines to be displayed within the same cell, preventing important information from being truncated.
sheet.Range["A8"].Text = "This is a very long sentence that will automatically wrap within the cell.";
sheet.Range["A8"].Style.WrapText = true;
sheet.SetColumnWidth(1, 20);
sheet.SetRowHeight(8, 60);
Part 2. Format Cell Values
Value formatting changes how data is displayed without modifying the underlying values. This is particularly important for business and financial spreadsheets.
Format Numbers
Numeric formats can control decimal places, thousands separators, scientific notation, and other display rules. Choosing the right format improves accuracy and readability.
sheet.Range["A10"].NumberValue = 1234567.891;
sheet.Range["A10"].NumberFormat = "#,##0.00";
Format Currency
Currency formatting automatically displays monetary symbols and decimal precision according to your requirements. This is commonly used in invoices, budgets, and financial reports.
sheet.Range["A11"].NumberValue = 5999.95;
sheet.Range["A11"].NumberFormat = "$#,##0.00";
Format Dates
Date formatting allows the same date value to be displayed in different styles, such as short dates, long dates, or custom patterns. Consistent date formats make reports easier to interpret.
sheet.Range["A12"].DateTimeValue = DateTime.Now;
sheet.Range["A12"].NumberFormat = "yyyy-MM-dd";
Part 3. Format Ranges and Layout
Instead of formatting cells one by one, you can apply styles to larger worksheet areas to improve efficiency and maintain consistency.
Format Ranges
A range may contain multiple rows and columns. Applying formatting to a range ensures that all cells share the same appearance and reduces repetitive code.
CellRange range = sheet.Range["A14:D18"];
range.Style.Color = Color.LightYellow;
range.Style.Font.IsBold = true;
range.Style.HorizontalAlignment = HorizontalAlignType.Center;
Merge Cells
Merged cells are often used for report titles and section headers. After merging, the content can be centered and styled to create a more professional appearance.
sheet.Range["A20:D20"].Merge();
sheet.Range["A20"].Text = "Monthly Sales Report";
sheet.Range["A20"].Style.Font.Size = 18;
sheet.Range["A20"].Style.Font.IsBold = true;
sheet.Range["A20"].Style.HorizontalAlignment = HorizontalAlignType.Center;
Format Rows and Columns
Formatting entire rows or columns is useful when all cells in a specific area should follow the same style, such as a header row or a currency column.
sheet.Rows[21].Style.Font.IsBold = true;
sheet.Rows[21].Style.Color = Color.LightGray;
sheet.Columns[1].Style.NumberFormat = "$#,##0.00";
AutoFit Rows and Columns
AutoFit automatically adjusts row heights and column widths based on cell content. This helps prevent clipped text and improves the presentation of generated spreadsheets.
sheet.AllocatedRange.AutoFitColumns();
sheet.AllocatedRange.AutoFitRows();
Part 4. Apply Advanced Formatting
Conditional formatting enables Excel to apply styles automatically based on cell values. Instead of manually highlighting data, rules can be configured to identify trends, exceptions, or important values.
For example, you can highlight numbers above a threshold, display data bars, apply color scales, or use icon sets to visualize performance indicators. These features make large datasets easier to analyze and understand.
sheet.Range["A25"].NumberValue = 1200;
sheet.Range["A26"].NumberValue = 800;
sheet.Range["A27"].NumberValue = 1500;
XlsConditionalFormats formats = sheet.ConditionalFormats.Add();
formats.AddRange(sheet.Range["A25:A27"]);
IConditionalFormat format = formats.AddCondition();
format.FormatType = ConditionalFormatType.CellValue;
format.FirstFormula = "1000";
format.Operator = ComparisonOperatorType.Greater;
format.BackColor = Color.LightGreen;
Part 5. Format Excel Tables and Worksheets
Formatting can also be applied at the worksheet level to improve the overall structure and appearance of a workbook.
Format Excel Tables
Excel tables provide built-in styling options such as header formatting, alternating row colors, and predefined themes. Converting a data range into a table can instantly enhance readability and organization.
sheet.Range["A30"].Text = "Product";
sheet.Range["B30"].Text = "Sales";
sheet.Range["A31"].Text = "Laptop";
sheet.Range["B31"].NumberValue = 5000;
sheet.Range["A32"].Text = "Monitor";
sheet.Range["B32"].NumberValue = 2000;
IListObject table = sheet.ListObjects.Create("SalesTable", sheet.Range["A30:B32"]);
table.BuiltInTableStyle = TableBuiltInStyles.TableStyleMedium2;
Configure Page Layout
Page layout settings determine how worksheets appear when printed or exported. Common options include page orientation, margins, print areas, scaling, and repeating header rows.
Proper page setup ensures that reports look professional both on screen and on paper.
sheet.PageSetup.Orientation = PageOrientationType.Landscape;
sheet.PageSetup.LeftMargin = 0.5;
sheet.PageSetup.RightMargin = 0.5;
sheet.PageSetup.TopMargin = 0.75;
sheet.PageSetup.BottomMargin = 0.75;
sheet.PageSetup.FitToPagesWide = 1;
sheet.PageSetup.FitToPagesTall = 1;
Part 6. Create a Professional Report Example
In real-world scenarios, multiple formatting techniques are often used together. A typical report may include a merged title, custom fonts, colored headers, borders, number formats, conditional formatting, and optimized page settings.
By combining these features, you can generate polished Excel documents that are ready for distribution without requiring manual editing.
using Spire.Xls;
using Spire.Xls.Core.Spreadsheet.Collections;
using Spire.Xls.Core;
using System.Drawing;
class Program
{
static void Main()
{
// Create a new workbook
Workbook workbook = new Workbook();
Worksheet sheet = workbook.Worksheets[0];
sheet.Name = "Sales Summary Report";
// Title Row
CellRange title = sheet.Range["A1:E1"];
title.Merge();
title.Text = "Sales Summary Report";
title.Style.Font.FontName = "Arial";
title.Style.Font.Size = 16;
title.Style.Font.Color = Color.White;
title.Style.Color = Color.DarkBlue;
title.Style.HorizontalAlignment = HorizontalAlignType.Center;
title.Style.VerticalAlignment = VerticalAlignType.Center;
sheet.Rows[0].RowHeight = 30;
// Headers
string[] headers = { "Order ID", "Product", "Region", "Order Date", "Sales Amount" };
for (int i = 0; i < headers.Length; i++)
{
CellRange cell = sheet.Range[2, i + 1];
cell.Text = headers[i];
cell.Style.Font.IsBold = true;
cell.Style.Color = Color.LightGray;
cell.Style.Borders[BordersLineType.EdgeBottom].LineStyle = LineStyleType.Medium;
cell.Style.Borders[BordersLineType.EdgeBottom].Color = Color.DarkBlue;
}
// Data
object[][] data =
{
new object[] { 1001, "Laptop", "North", "2024-01-15", 15000 },
new object[] { 1002, "Monitor", "West", "2024-02-10", 12000 },
new object[] { 1003, "Keyboard", "East", "2024-03-05", 13500 },
new object[] { 1004, "Mouse", "South", "2024-04-12", 16000 }
};
for (int r = 0; r < data.Length; r++)
{
for (int c = 0; c < data[r].Length; c++)
{
CellRange cell = sheet.Range[r + 3, c + 1];
var value = data[r][c];
if (c == 3) // Order Date
{
cell.DateTimeValue = DateTime.Parse(value.ToString());
cell.NumberFormat = "yyyy-MM-dd";
}
else if (c == 4) // Sales Amount
{
cell.NumberValue = Convert.ToDouble(value);
cell.NumberFormat = "$#,##0.00";
}
else
{
cell.Text = value.ToString();
}
// Alternate row colors
cell.Style.Color = (r % 2 == 0)
? Color.LightYellow
: Color.LightCyan;
}
}
// Borders
CellRange range = sheet.Range["A2:E6"];
range.BorderAround(LineStyleType.Medium, Color.Black);
range.BorderInside(LineStyleType.Thin, Color.Gray);
// Auto Fit Columns
for (int i = 1; i <= 5; i++)
{
sheet.AutoFitColumn(i);
}
// Conditional Formatting
XlsConditionalFormats formats = sheet.ConditionalFormats.Add();
formats.AddRange(sheet.Range["E3:E6"]);
IConditionalFormat condition = formats.AddCondition();
condition.FormatType = ConditionalFormatType.CellValue;
condition.Operator = ComparisonOperatorType.Greater;
condition.FirstFormula = "14000";
condition.FontColor = Color.Red;
condition.IsBold = true;
// Align + Layout Formatting
CellRange all = sheet.AllocatedRange;
for (int r = 1; r < all.RowCount; r++)
{
all.Rows[r].HorizontalAlignment = HorizontalAlignType.Center;
// all.Rows[r].VerticalAlignment = VerticalAlignType.Center;
all.Rows[r].RowHeight = 20;
}
for (int c = 0; c < all.ColumnCount; c++)
{
all.Columns[c].ColumnWidth = (c == 1) ? 19 : 14;
}
// Save
workbook.SaveToFile("SalesSummaryReport.xlsx", ExcelVersion.Version2016);
workbook.Dispose();
}
}
Output:

Conclusion
Formatting is an essential step in creating professional Excel documents. With Spire.XLS for .NET, you can efficiently customize cell appearance, control number and date formats, manage worksheet layouts, apply conditional formatting, and build visually appealing reports entirely in C#.
By using the techniques covered in this guide, you can automate Excel formatting tasks and generate polished spreadsheets suitable for business, reporting, and data analysis scenarios.
FAQs
Can I format Excel files without Microsoft Excel installed?
Yes. Spire.XLS for .NET works independently of Microsoft Excel and can create, edit, and format spreadsheets directly through code.
Does formatting change the actual cell values?
No. Most formatting operations only affect how data is displayed. The underlying values remain unchanged unless explicitly modified.
Can I apply the same style to multiple cells at once?
Yes. Styles can be applied to ranges, rows, columns, or entire worksheets, making it easy to maintain consistent formatting.
Does Spire.XLS support conditional formatting?
Yes. The library supports common conditional formatting features, including highlighting rules, data bars, color scales, and icon sets.
Which Excel formats are supported?
Spire.XLS supports XLS, XLSX, XLSM, CSV, and several other spreadsheet formats for both reading and writing.
How to Convert HTML to Markdown in C# (File, String & Stream)
2026-05-29 09:55:41 Written by alice yang
HTML is widely used for web pages, online articles, and rich text content, while Markdown (.md) is often preferred for documentation, technical writing, and text-based publishing. If you need to reuse HTML content in a Markdown-based workflow, converting it manually can be time-consuming and error-prone.
In this tutorial, we’ll show you how to convert HTML to Markdown in C# step-by-step using Spire.Doc for .NET. You’ll learn how to convert HTML files, HTML strings, streams, and multiple HTML files in batch.
Table of Contents
- When Do You Need to Convert HTML to Markdown?
- Install C# HTML to Markdown Library
- Convert an HTML File to Markdown in C#
- Convert HTML Strings to Markdown in C#
- Convert HTML Stream to Markdown in C#
- Batch Convert Multiple HTML Files
- What HTML Elements Can Be Converted to Markdown?
- Troubleshooting Common HTML to Markdown Issues
When Do You Need to Convert HTML to Markdown?
Converting HTML to Markdown is useful when you want to reuse web-based or rich-text content in a cleaner, text-friendly format. Common scenarios include:
- Moving HTML articles or CMS content into Markdown-based documentation systems.
- Preparing content for GitHub, static site generators, or developer portals.
- Converting rich text editor output into editable Markdown files.
- Simplifying HTML pages for version control, review, or long-term maintenance.
- Exporting help center articles, product descriptions, or blog content as .md files.
Install C# HTML to Markdown Library
To convert HTML to Markdown programmatically, you need to add Spire.Doc for .NET to your project. This standalone document processing library allows you to parse HTML and export it to clean Markdown without requiring Microsoft Word or Microsoft Office interop assemblies on your server.
Method 1: Install via NuGet Package Manager
Run this command in your NuGet Package Manager Console:
Install-Package Spire.Doc
Method 2: Download and Reference DLLs Manually
If your development environment is offline or you prefer not to use NuGet, you can manually download and reference the library:
- Download & Unzip: Get the Spire.Doc for .NET package from the official download page and extract it.
- Add Reference: In the Solution Explorer of Visual Studio, right-click Dependencies (or References) > Add Project Reference (or Add Reference) > Browse and select the
Spire.Doc.dllthat matches your target .NET Framework or .NET Core version.
Note: Markdown support is available in Spire.Doc for .NET version 12.3.12 or later.
Convert an HTML File to Markdown in C#
If your HTML content is stored as a local .html or .htm file, you can convert it directly using the Document object. This approach is ideal for processing static web pages, documentation exports, or offline help articles.
C# Code Example
using Spire.Doc;
using Spire.Doc.Documents;
namespace ConvertHtmlFileToMarkdown
{
class Program
{
static void Main(string[] args)
{
// Initialize a Document instance within a using statement
using (Document document = new Document())
{
// Load the local HTML file
document.LoadFromFile("input.html", FileFormat.Html, XHTMLValidationType.None);
// Export the HTML file to a Markdown file
document.SaveToFile("output.md", FileFormat.Markdown);
}
}
}
}
How the Code Works:
using (Document document = new Document()): Ensures theDocumentobject is properly disposed of after conversion.LoadFromFile("input.html", FileFormat.Html, XHTMLValidationType.None): Reads the source HTML file without strict XHTML validation, allowing the library to parse the HTML even if it doesn’t fully comply with XHTML rules.SaveToFile("output.md", FileFormat.Markdown): Maps the supported HTML elements such as headings, bold text, lists, images, and links into Markdown syntax, and generate the .md file.
Output:

Convert HTML Strings to Markdown in C#
When dealing with dynamic web data—such as content fetched from a database, API responses, or CMS rich-text inputs—you can convert raw HTML strings directly to Markdown without saving them as physical files first.
C# Code Example
using Spire.Doc;
using Spire.Doc.Documents;
namespace ConvertHtmlStringToMarkdown
{
class Program
{
static void Main(string[] args)
{
// Initialize a Document instance
using (Document document = new Document())
{
// Add a section and paragraph to host the dynamic html content
Section section = document.AddSection();
Paragraph paragraph = section.AddParagraph();
// Define the source HTML string
string htmlString = @"
<h1>HTML to Markdown Conversion</h1>
<p>This is a sample paragraph with a <a href='https://www.example.com'>link</a>.</p>
<ul>
<li>First item</li>
<li>Second item</li>
<li>Third item</li>
</ul>";
// Parse and append the HTML string directly into the text paragraph
paragraph.AppendHTML(htmlString);
// Save the fully compiled document model as Markdown
document.SaveToFile("html-string-output.md", FileFormat.Markdown);
}
}
}
}
Key Methods Explanation:
document.AddSection()§ion.AddParagraph(): An emptyDocumentobject does not contain structural layouts. You must explicitly create a parent section and a text paragraph to serve as the container before injecting raw HTML string content.paragraph.AppendHTML(htmlString): Parses the HTML string and inserts supported HTML elements into the document structure.
Output:

Convert HTML Stream to Markdown in C#
In cloud-ready or backend enterprise applications, HTML content is often processed in memory as a stream rather than being read from a fixed physical path. Using LoadFromStream() and SaveToStream(), you can convert in-memory HTML content directly to a Markdown stream.
This approach is useful for web services, ASP.NET applications, background processing tasks, or conversion APIs where files are uploaded, converted, and returned without permanent disk storage.
C# Code Example
using System.IO;
using System.Text;
using Spire.Doc;
using Spire.Doc.Documents;
namespace ConvertHtmlStreamToMarkdown
{
class Program
{
static void Main(string[] args)
{
// Define a sample HTML string to simulate an in-memory input source
string htmlContent = "<h1>HTML Stream to Markdown Stream</h1><p>This process happens entirely in memory.</p>";
byte[] htmlBytes = Encoding.UTF8.GetBytes(htmlContent);
// Create an input stream from the HTML bytes
using (MemoryStream inputStream = new MemoryStream(htmlBytes))
{
// Create an empty memory stream to receive the converted Markdown data
using (MemoryStream outputStream = new MemoryStream())
{
// Initialize the Document instance
using (Document document = new Document())
{
// Load the HTML content directly from the input stream
document.LoadFromStream(inputStream, FileFormat.Html, XHTMLValidationType.None);
// Save the converted content directly into the output stream as Markdown
document.SaveToStream(outputStream, FileFormat.Markdown);
}
// Crucial: Reset the output stream position to the beginning before reading it
outputStream.Position = 0;
// Optional: Convert the output stream back to a string to verify the result (you can also save it as a .md file)
using (StreamReader reader = new StreamReader(outputStream, Encoding.UTF8))
{
string markdownResult = reader.ReadToEnd();
System.Console.WriteLine(markdownResult);
}
}
}
}
}
}
Batch Convert Multiple HTML Files
For large-scale publishing workflows, you can automate the conversion of multiple HTML files to Markdown using a loop.
C# Code Example
The following example converts all .html files in a source folder to .md files in an output folder.
using Spire.Doc;
using Spire.Doc.Documents;
using System;
using System.IO;
namespace BatchConvertHtmlToMarkdown
{
internal class Program
{
static void Main(string[] args)
{
string inputFolder = @"C:\HtmlFiles";
string outputFolder = @"C:\MarkdownFiles";
// Create output folder if it does not exist
Directory.CreateDirectory(outputFolder);
// Get all HTML files
string[] htmlFiles = Directory.GetFiles(inputFolder, "*.html");
foreach (string htmlFile in htmlFiles)
{
try
{
string fileName = Path.GetFileNameWithoutExtension(htmlFile);
string outputPath = Path.Combine(outputFolder, fileName + ".md");
using (Document document = new Document())
{
document.LoadFromFile(htmlFile, FileFormat.Html, XHTMLValidationType.None);
document.SaveToFile(outputPath, FileFormat.Markdown);
}
Console.WriteLine($"Converted: {Path.GetFileName(htmlFile)}");
}
catch (Exception ex)
{
Console.WriteLine($"Failed to convert {Path.GetFileName(htmlFile)}");
Console.WriteLine($"Error: {ex.Message}");
}
}
Console.WriteLine("HTML to Markdown batch conversion completed.");
}
}
}
What HTML Elements Can Be Converted to Markdown?
HTML has many elements, but Markdown supports only a smaller set of document structures. During conversion, content-focused elements are usually easier to preserve than layout-focused or style-heavy elements. For instance, standard Markdown tables only support basic rows and columns. If your source contains complex tables, you might want to convert HTML to Excel in C# instead.
The following table summarizes common HTML elements and how they may appear in Markdown.
| HTML Element | Markdown Syntax |
|---|---|
<h1> to <h6> |
# to ###### (Headings) |
<p> |
Plain paragraph |
<strong>, <b> |
**bold** |
<em>, <i> |
*italic* |
<ul>, <ol>, <li> |
Bulleted or numbered lists |
<a> |
[Link Text](URL) |
<img> |
 |
<table> |
Markdown table |
<code> |
Inline code |
<pre> |
Code block |
<br> |
Line break |
<div>, <section> |
Usually simplified |
| CSS styles | Limited or removed |
| JavaScript | Not supported |
Tip: Actual output may vary depending on the source HTML structure and the Markdown features supported by the target editor or platform.
Troubleshooting Common HTML to Markdown Issues
- Images not showing: Verify that all image paths are still valid after conversion; relative paths may need adjustment.
- Tables look different: Markdown supports only basic tables. For complex tables with merged cells, nested layouts, or custom styling, simplify the HTML table before conversion or manually adjust the generated Markdown table afterward.
- Special characters appear incorrectly: This is usually an encoding issue. Make sure the source HTML file uses UTF-8 encoding and open the generated Markdown file in an editor that supports UTF-8.
- Extra blank lines: Remove unnecessary empty tags, nested
divelements, or redundantbrtags from the source HTML before conversion. You can also clean the generated Markdown file afterward by opening it in a text editor like Notepad++ and then performing a find & replace.
Conclusion
With Spire.Doc for .NET, converting HTML to Markdown in C# can be implemented in just a few lines of code. This guide covered the core approaches needed for various development scenarios:
- Converting local HTML files and streams to Markdown.
- Inserting and converting dynamic HTML strings.
- Batch converting multiple HTML files simultaneously.
If your workflow also requires the reverse process, see this tutorial on how to convert Markdown to HTML in C#.
Frequently Asked Questions
Q1: Will images be preserved during HTML to Markdown conversion?
A1: Yes. Standard HTML <img> tags can be converted into Markdown image syntax (). Just ensure your source HTML links use valid URLs or correct file paths so the images can load.
Q2: Can I convert an HTML string or stream to Markdown without saving files?
A2: Yes. You can load an HTML string using AppendHTML() or a stream via LoadFromStream(), then export it entirely in memory using SaveToStream() without hitting the local disk.
Q3: Can I convert multiple HTML files to Markdown at once in C#?
A3: Yes. You can use a foreach loop in C# to scan a folder for *.html files, process each file through the converter, and output them to a destination folder in bulk.
Q4: Is Microsoft Word required for HTML to Markdown conversion?
A4: No. Spire.Doc for .NET is a standalone library, so Microsoft Word does not need to be installed.

CSV (Comma-Separated Values) and XML (Extensible Markup Language) are two mainstream data exchange formats in modern software development. CSV excels at lightweight storage and transmission of tabular data, while XML is widely used for cross-system data interaction due to its hierarchical structure and strict validation rules. Converting CSV to XML is a common demand in scenarios such as configuration file generation, database record export, and third-party API integration.
Manual CSV parsing and XML generation are error-prone, inefficient, and difficult to maintain for large datasets. For C# developers, Spire.XLS for .NET provides a lightweight, high-performance solution for CSV-to-XML conversion.
This article will guide you through two core methods to convert CSV to XML in C# using Spire.XLS, covering basic conversion and customized XML output with complete code examples.
- Understanding the Two XML Output Types
- Method 1: Convert CSV to Excel XML in C#
- Method 2: Convert CSV to Custom XML in C#
- Handling Real World CSV Scenarios
- FAQs About CSV to XML Conversion
Prerequisites
Install Spire.XLS via NuGet
The quickest way to add Spire.XLS to your project is through the NuGet Package Manager. In Visual Studio, run the following command in the Package Manager Console:
Install-Package Spire.XLS
Alternatively, search for “Spire.XLS” in the NuGet UI and install the latest version.
Prepare a Sample CSV Data (For Testing)
Create a Products.csv file in your project’s output folder with this sample tabular data (includes headers and rows):
ID,ProductName,Category,Price,StockQuantity,ReleaseDate
1,Laptop,Electronics,999.99,50,2023-01-15
2,Wireless Mouse,Electronics,25.50,200,2023-02-20
3,Cotton T-Shirt,Apparel,19.99,150,2022-11-05
4,Coffee Mug,Home Goods,12.99,300,2022-09-10
5,Desk Chair,Furniture,150.00,75,2023-03-01
Understanding the Two XML Output Types
When converting CSV file to XML using Spire.XLS for .NET, you can produce two distinct types of XML output:
1. Excel XML (SpreadsheetML)
A standardized XML format based on Microsoft's Open XML specification. It retains the original table layout, cell formatting and overall structure of the CSV file. This is an ideal choice if you need to open, edit or process the generated XML with Excel and other spreadsheet-compatible tools.
2. Custom XML Structure
A user-defined XML schema enables free mapping of CSV columns to custom element tags, nested hierarchies, and adjustable presentation rules. This is ideal for integrating with third-party APIs, legacy systems, and business platforms that require specific fixed XML node structures.
Both approaches are covered in detail with C# code examples in the following sections. For developers working in Python, refer to our separate guide: Convert CSV to XML in Python
Method 1: Convert CSV to Excel XML in C#
This is the simplest approach that requires only a few lines of code. The CSV is loaded into a Workbook, then saved directly as Excel XML (SpreadsheetML) format. Suitable for cases where the target system accepts Excel's native XML format.
CSV to XML C# Code Example:
using Spire.Xls;
namespace CsvToExcelXmlConverter
{
class Program
{
static void Main(string[] args)
{
// Create a Workbook instance
Workbook workbook = new Workbook();
// Load CSV into the first worksheet
workbook.LoadFromFile("Products.csv", ",", 1, 1);
// Save as Excel SpreadsheetML format
workbook.SaveAsXml("output.xml");
// Clean up resources
workbook.Dispose();
}
}
}
Core Methods:
- LoadFromFile() – Spire.XLS automatically parses the CSV content into the first worksheet of the workbook.
- SaveAsXml() – Export the loaded CSV data as an Excel XML file.
The core data parts looks like:

Beyond converting CSV files, Spire.XLS for .NET also enables seamless conversion of Excel (XLS / XLSX) to XML using the same robust Workbook.SaveAsXml() method.
Method 2: Convert CSV to Custom XML in C#
When the target system expects a specific XML schema, you’ll need to build a custom XML document. Spire.XLS, together with the built-in .NET XmlWriter class, makes this straightforward: simply iterate over worksheet rows and columns, then generate well-structured XML efficiently.
Custom XML Code Example:
using Spire.Xls;
using System.Xml;
namespace CSVtoXMLConverter
{
class program
{
static void Main(string[] args)
{
// Initialize workbook and load CSV
Workbook workbook = new Workbook();
workbook.LoadFromFile("Products.csv", ",", 1, 1);
Worksheet worksheet = workbook.Worksheets[0];
// Create custom XML settings (indentation for readability)
XmlWriterSettings settings = new XmlWriterSettings
{
Indent = true,
IndentChars = "\t",
OmitXmlDeclaration = false,
Encoding = System.Text.Encoding.UTF8
};
// Save CSV to XML with CUSTOM ROOT NODE and FORMATTING
XmlWriter writer = XmlWriter.Create("Custom_Output.xml", settings);
writer.WriteStartDocument();
// Custom root element: <ProductInventory>
writer.WriteStartElement("ProductInventory");
// Loop through CSV rows (skip header row: start at 2)
for (int row = 2; row <= worksheet.LastRow; row++)
{
// Custom data node: <Product>
writer.WriteStartElement("Product");
// Loop through CSV columns and write custom elements
for (int col = 1; col <= worksheet.LastColumn; col++)
{
string header = worksheet.Range[1, col].Text;
string value = worksheet.Range[row, col].Text;
writer.WriteElementString(header, value);
}
writer.WriteEndElement(); // Close <Product>
}
writer.WriteEndElement(); // Close <ProductInventory>
writer.WriteEndDocument();
writer.Close();
workbook.Dispose();
}
}
}
How the code works:
- Load the CSV file into a Workbook.
- Access the worksheet – Retrieve the first worksheet containing the imported data.
- Create an XmlWriter – Configure it with indentation, encoding, and other formatting preferences.
- Write the XML document – Start the document, write the custom root element, then loop through rows (skipping the header row) and columns, writing an element per column using the header text as the tag name.
- Close resources – Close the XmlWriter and dispose of the Workbook.
Output:
The generated XML uses <ProductInventory> as the root node and <Product> as the data node, with child nodes named after CSV headers, fully matching custom business requirements.

Bonus Tip: For scenarios requiring formal document delivery, you can also use Spire.XLS for .NET to convert XML files to PDF in C# effortlessly.
Handling Real‑World CSV Scenarios
Real-world CSV files often have non-standard delimiters, empty values, redundant rows/columns, or missing headers. The following solutions solve these common problems:
Different Delimiters
CSV files may use tabs (\t), semicolons (;), or pipes (|). Spire.XLS supports specifying custom delimiters when loading CSV files:
// Tab-separated
workbook.LoadFromFile("data.tsv", "\t", 1, 1);
// Semicolon-separated (common in European locales)
workbook.LoadFromFile("data.csv", ";", 1, 1);
// Pipe-separated
workbook.LoadFromFile("data.psv", "|", 1, 1);
Skipping Rows and Columns
If the CSV contains metadata rows or empty columns, adjust your loop bounds:
// start from row 3
for (int row = 3; row <= worksheet.LastRow; row++)
//start from column 3
for (int col = 3; col <= worksheet.LastColumn; col++)
Handling Missing Values and Empty Cells
Empty cells in CSV files appear as empty strings. You can handle them by writing a default value or omitting the elements:
// Option 1: Fill empty values with "N/A"
string value = worksheet.Range[row, col].Text;
if (string.IsNullOrEmpty(value))
value = "N/A";
writer.WriteElementString(header, value);
// Option 2: Omit empty nodes
string value = worksheet.Range[row, col].Text;
if (!string.IsNullOrEmpty(value))
writer.WriteElementString(header, value);
Missing Header Row
If your CSV has no header row, you can pass an array of custom names, or generate generic column names such as:
for (int col = 1; col <= worksheet.LastColumn; col++)
{
string header = $"Column{col}";
string value = worksheet.Range[row, col].Text;
writer.WriteElementString(header, value);
}
Conclusion
Converting CSV to XML in C# with Spire.XLS for .NET eliminates manual parsing and reduces code complexity. Whether you need a simple one-line conversion to Excel XML or a fully customized XML structure, the code examples in this guide let you seamlessly integrate CSV-to-XML conversion into your C# applications.
For more Excel or CSV-related tasks in .NET development, visit the online documentation.
FAQs About CSV to XML Conversion
Q1: Can I convert only a specific range of cells (e.g., A1:C10)?
Yes. Instead of looping to worksheet.LastRow and LastColumn, set custom bounds (e.g., loop rows 2 to 11 for A1:C10) or directly access a range via worksheet.Range.
Q2: Does Spire.XLS require Microsoft Excel to be installed?
No, it is an independent .NET component and does not rely on Microsoft Excel, Office interop, or any third-party Office software.
Q3: Can I batch convert multiple CSV files to XML at once?
Yes. You can loop through all CSV files in a folder, load each one with Spire.XLS, and export them to corresponding XML files in batches with the same logic in the tutorial.
Q4: Can I add custom attributes and namespaces to generated XML elements?
Absolutely. Use XmlWriter.WriteAttributeString() to add custom attributes to nodes, and WriteStartElement with namespace parameters to define XML namespaces for enterprise-standard XML schemas.

PowerPoint presentations are widely used for training materials, product demos, online courses, and business reporting. However, sharing raw PPT or PPTX files can be problematic—recipients may not have PowerPoint installed, animations may not play correctly, and manual exporting becomes inefficient for bulk processing.
Converting PowerPoint to video formats like MP4 or WMV solves these challenges by creating universally playable content that preserves formatting and animations. With Spire.Presentation from e-iceblue, developers can automate PowerPoint-to-video conversion programmatically without requiring Microsoft PowerPoint installation.
This article demonstrates how to convert PowerPoint presentations to MP4 and WMV video in C# using Spire.Presentation for .NET, including configuration options for frame rate, slide duration, and transition preservation.
1. Why Convert PowerPoint to Video Programmatically?
Developers often need to convert PowerPoint presentations to video as part of larger business workflows. Compared with manually exporting files in Microsoft PowerPoint, programmatic conversion offers more flexibility and scalability.
Common scenarios include:
- Automatically converting uploaded PPT/PPTX files into MP4 videos in web applications
- Batch-processing training presentations for LMS platforms
- Generating product demo videos from presentation templates
- Converting presentations on servers where Microsoft PowerPoint is not installed
- Standardizing presentation delivery across different devices
Programmatic conversion is especially useful when you need repeatable workflows, server-side processing, or integration with existing document automation systems.
2. Set Up the Environment
Before converting PowerPoint presentations to video, you need to prepare two components:
- Spire.Presentation for .NET – used to load and process PPT/PPTX files
- FFmpeg – used to encode slide frames into MP4 or WMV video files
Spire handles presentation rendering, while FFmpeg generates the final video output. Both are required for successful conversion.
Install Spire.Presentation for .NET
Install the library from NuGet:
Install-Package Spire.Presentation
You can also download Spire.Presentation for .NET package and install it manually.
This package allows your C# application to open PowerPoint presentations, access slides, and export them programmatically.
Install FFmpeg
Spire.Presentation relies on FFmpeg to combine rendered slide frames into a playable video file. If FFmpeg is not installed or the path is configured incorrectly, the export process will fail.
- On Windows
Follow these steps to install FFmpeg:
-
Download the FFmpeg essentials build
-
Extract the package to your local machine
-
Locate the bin folder path
Example:
D:\tools\ffmpeg\bin
This path will be used later when configuring SaveToVideoOption.
- On Linux (CentOS)
Install FFmpeg using the following commands:
sudo yum install epel-release
sudo yum localinstall --nogpgcheck https://download1.rpmfusion.org/free/el/rpmfusion-free-release-7.noarch.rpm
sudo yum install ffmpeg ffmpeg-devel
After installation, you can run the following command to locate the FFmpeg path:
which ffmpeg
Note: Older FFmpeg versions may not fully support certain slide transition effects.
3. Convert PowerPoint to MP4 in C#
Once the environment is configured, you can convert PowerPoint presentations to MP4 using just a few lines of code.
The basic workflow includes:
- Load the PowerPoint file
- Configure video export settings
- Export the presentation as MP4
Basic Conversion Example
The following example converts a PPTX file into an MP4 video:
using Spire.Presentation;
namespace PowerPointToVideo
{
class Program
{
static void Main(string[] args)
{
string inputFile = "ProductDemo.pptx";
string outputFile = "ProductDemo.mp4";
Presentation presentation = new Presentation();
presentation.LoadFromFile(inputFile);
presentation.SaveToVideoOption = new SaveToVideoOption(
@"D:\tools\ffmpeg\bin"
);
presentation.SaveToVideoOption.Fps = 30;
presentation.SaveToVideoOption.DurationForEachSlide = 2;
presentation.SaveToFile(outputFile, FileFormat.MP4);
presentation.Dispose();
}
}
}
After running the code:
- The PPTX file is loaded into memory
- Each slide is rendered as individual video frames
- FFmpeg combines the frames into a final MP4 file
- Supported animations, transitions, and embedded videos are preserved during export
Below is a sample PowerPoint presentation along with its converted video output.
Input: PowerPoint Presentation

Output: Converted MP4 Video
Click the preview above to watch how PowerPoint slides are converted into an MP4 video while preserving transitions and animations.
How the Core API Works
This example uses several key API methods:
- LoadFromFile() loads the PowerPoint presentation into memory
- SaveToVideoOption configures the FFmpeg path and playback settings
- Fps controls video smoothness
- DurationForEachSlide controls how long each slide appears
- SaveToFile() exports the final video file
- Dispose() releases system resources after conversion
This basic workflow is enough for most standard PowerPoint-to-video conversion tasks. If you need additional formats or customization options, continue to the advanced scenarios below.
If you need a static sharing format, you can also convert PowerPoint presentations to images (JPG/PNG) in C# for easier distribution and web display.
4. More PowerPoint to Video Options in C#
The basic example works for most scenarios, but some applications may require different output formats, custom playback settings, or bulk conversion workflows.
Convert PowerPoint to WMV
While MP4 is the most widely used video format, some legacy enterprise systems and Windows-based environments may still require WMV output.
To export a PowerPoint file as WMV, simply change the output file extension:
using Spire.Presentation;
Presentation presentation = new Presentation();
presentation.LoadFromFile("TrainingSlides.pptx");
presentation.SaveToVideoOption = new SaveToVideoOption(
@"D:\tools\ffmpeg\bin"
);
presentation.SaveToFile("TrainingVideo.wmv", FileFormat.WMV);
presentation.Dispose();
Customize Video Settings
If your presentation contains complex animations or requires specific playback timing, you can adjust frame rate and slide duration settings.
using Spire.Presentation;
Presentation presentation = new Presentation();
presentation.LoadFromFile("MarketingPitch.pptx");
presentation.SaveToVideoOption = new SaveToVideoOption(
@"D:\tools\ffmpeg\bin"
);
// Higher FPS for smoother playback
presentation.SaveToVideoOption.Fps = 60;
// Longer display time per slide
presentation.SaveToVideoOption.DurationForEachSlide = 10;
presentation.SaveToFile("MarketingPitch_HD.mp4", FileFormat.MP4);
presentation.Dispose();
Video Settings Reference
| Setting | Default | Maximum | Purpose |
|---|---|---|---|
| Fps | 30 | 60 | Controls playback smoothness |
| DurationForEachSlide | 5 seconds | 5 minutes | Controls slide display duration |
Higher values may increase processing time and temporary storage usage.
Batch Convert Multiple PPTX Files
Batch conversion is useful for LMS platforms, enterprise reporting systems, and document automation workflows that need to process multiple presentations automatically.
using Spire.Presentation;
using System.IO;
string ffmpegPath = @"D:\tools\ffmpeg\bin";
string inputFolder = @"C:\Presentations\";
string outputFolder = @"C:\Videos\";
string[] pptxFiles = Directory.GetFiles(inputFolder, "*.pptx");
foreach (string inputFile in pptxFiles)
{
string fileName = Path.GetFileNameWithoutExtension(inputFile);
string outputFile = Path.Combine(outputFolder, fileName + ".mp4");
Presentation presentation = new Presentation();
presentation.LoadFromFile(inputFile);
presentation.SaveToVideoOption = new SaveToVideoOption(ffmpegPath);
presentation.SaveToVideoOption.Fps = 30;
presentation.SaveToVideoOption.DurationForEachSlide = 3;
presentation.SaveToFile(outputFile, FileFormat.MP4);
presentation.Dispose();
}
This approach helps automate large-scale PowerPoint-to-video conversion workflows without requiring manual exports in Microsoft PowerPoint.
You can edit the PowerPoint presentation in C# before conversion to ensure the resulting video has better layout and animation effects.
5. Supported Transitions and Animations
During PowerPoint-to-video conversion, Spire.Presentation preserves key visual effects to ensure the output video closely matches the original presentation experience.
Slide Transitions
PowerPoint slide transitions are rendered during video generation to maintain smooth visual flow between slides.
The following transitions are supported:
- Fade
- Push
- Wipe (up, down, left, right)
- Reveal
- Cover
- Split
- Dissolve
- Clockwise Clock
These transitions are applied during frame rendering to simulate natural slide progression in the final video.
Animation Effects
Animations are processed and rendered during video generation to simulate PowerPoint playback behavior.
Entrance Animations:
- Fly In
- Float In
- Appear
- Fade
- Split
- Wipe
Exit Animations:
- Fly Out
- Float Out
- Disappear
- Fade
- Split
- Wipe
Animation sequences are processed as a single playback unit to ensure consistent rendering in the final video.
Additional Features
- Embedded Videos
Embedded media inside PowerPoint slides is included in the exported video, making it suitable for presentations with multimedia content.
- Automatic Duration Handling
Slide timing and animation durations are automatically interpreted during conversion to ensure accurate playback in the final video output.
- Cross-Platform Support
The conversion process can run on both Windows and Linux environments, making it suitable for server-side automation and enterprise workflows.
For more information on supported features, refer to the Spire.Presentation for .NET API documentation.
6. Common Pitfalls
When converting PowerPoint presentations to video, there are a few common issues that may affect output quality or runtime execution. Being aware of these helps ensure a smoother conversion process in production environments.
FFmpeg Path Not Found
The video export process depends on FFmpeg for encoding the final MP4 or WMV file.
Ensure that the FFmpeg path is correctly configured and points to the bin directory containing the FFmpeg executable.
On Windows, this typically looks like:
D:\tools\ffmpeg\bin
If the FFmpeg path is incorrect or not accessible, the video export process will fail at runtime.
Insufficient Disk Space
PowerPoint-to-video conversion involves rendering slides into intermediate frames before encoding them into a final video file.
As a result, disk usage may increase significantly depending on:
- Number of slides
- Slide duration
- Frame rate (FPS)
- Presentation resolution and content complexity
For high-quality or long-duration presentations, temporary disk usage can become substantial. It is recommended to ensure sufficient free disk space before processing large batch conversions.
Unsupported or Inconsistent Transitions
Most common PowerPoint transitions are supported during conversion. However, some complex or advanced transition effects may not be rendered exactly the same as in Microsoft PowerPoint.
In such cases, the final video will still preserve slide flow, but the visual effect may appear simplified compared to the original presentation.
It is recommended to test presentations with advanced transitions before using them in production workflows.
Font Rendering Differences
PowerPoint presentations rely on system-installed fonts. If a required font is missing on the environment where conversion is executed, the layout or text appearance in the final video may change.
To ensure consistent rendering:
- Install required fonts on the system
- Use widely available standard fonts when possible
- Verify output on target deployment environments
This is especially important for multilingual presentations or server-side conversion scenarios.
Conclusion
In this article, we demonstrated how to convert PowerPoint presentations to MP4 and WMV video in C# using Spire.Presentation. By leveraging the Spire API, developers can automate video generation with customizable frame rates, slide durations, and transition preservation.
Beyond video conversion, Spire.Presentation can also be used for tasks such as slide editing, media extraction, and presentation generation, making it useful for broader document automation workflows.
If you would like to evaluate the full functionality without limitations, you can apply for a temporary license.
FAQ
Can I convert PowerPoint to MP4 without Microsoft PowerPoint?
Yes. Spire.Presentation performs conversion independently and does not require Microsoft PowerPoint installation.
Are animations preserved in the video?
Yes, many common slide transitions and entrance/exit animations are preserved during conversion.
What video formats are supported?
Currently, MP4 and WMV formats are supported for video export.
Is Spire.Presentation suitable for server-side applications?
Yes. Spire.Presentation supports server environments and is widely used in automated document processing workflows.
How much disk space does video conversion require?
Video generation creates temporary image frames. A presentation with 5 slides at 60 FPS and 5-minute duration may require approximately 25GB of temporary storage.

Exporting database query results to PDF is a common requirement in applications such as reporting, data archiving, and document generation. In these scenarios, SQL query results need to be transformed into structured, readable documents that can be easily shared or printed.
Because database data is inherently tabular, preserving its structure during the export process is essential for maintaining clarity and usability. Without proper layout control, the resulting document can quickly become difficult to read, especially when dealing with large datasets.
This article demonstrates how to convert databases to PDF in C# using Spire.XLS for .NET, including examples on retrieving query results, organizing them into a structured table, and exporting them as a formatted PDF document.
Table of Contents
- Understanding the Task
- Convert Database to PDF Using C# (Step-by-Step)
- Complete C# Example for Database to PDF Conversion
- Advanced Scenarios
- Common Pitfalls
- Conclusion
- FAQ
1. Understanding the Task
Converting database content to PDF typically involves several key steps:
- Data retrieval: Execute SQL queries and load results into memory
- Data structuring: Organize query results into a consistent tabular format
- PDF export: Generate a document that preserves layout and readability
In practice, this workflow is commonly used for generating reports, creating invoices, or archiving query results, where maintaining a clear and structured presentation of data is essential.
2. Convert Database to PDF Using C# (Step-by-Step)
This section provides a complete workflow for converting database query results into a PDF document, including data retrieval, table structuring, formatting, and export.
2.1 Environment Setup
Before implementing the solution, make sure your development environment is ready:
-
.NET environment
Install Visual Studio or use the .NET CLI with a compatible .NET version (e.g., .NET 6 or later). -
Database access
Prepare a SQL Server database (or any relational database) and ensure you have a valid connection string. For modern .NET applications, use the recommended SQL client library:dotnet add package Microsoft.Data.SqlClientThis package provides the ADO.NET implementation for SQL Server and replaces the legacy
System.Data.SqlClient. -
Spire.XLS for .NET Install Spire.XLS via NuGet to handle table formatting and PDF export:
dotnet add package Spire.XLSYou can also download the Spire.XLS for .NET package and add it to your project manually.
Once configured, you can retrieve data from the database and use Spire.XLS to generate and export PDF documents.
2.2 Read Data from Database
The first step is to execute a SQL query and load the results into a DataTable. This structure preserves the schema and data types of the query result, making it suitable for further transformation.
using System.Data;
using Microsoft.Data.SqlClient;
string connectionString = "Server=localhost\\SQLEXPRESS;Database=SalesDB;User ID=demouser;Password=YourPassword;Encrypt=true;TrustServerCertificate=true;";
string query = @"
SELECT o.OrderID, c.CustomerName, o.OrderDate, o.TotalAmount
FROM Orders o
JOIN Customers c ON o.CustomerID = c.CustomerID
WHERE YEAR(o.OrderDate) = 2026;
";
DataTable dataTable = new DataTable();
using (SqlConnection conn = new SqlConnection(connectionString))
{
SqlDataAdapter adapter = new SqlDataAdapter(query, conn);
adapter.Fill(dataTable);
}
This example uses Microsoft.Data.SqlClient, the modern SQL client library for .NET, which is recommended over the legacy System.Data.SqlClient.
The SqlDataAdapter acts as a bridge between the database and in-memory data. It executes the query and fills the DataTable without requiring explicit connection management for reading operations.
In practical scenarios, this step can be extended to include:
- Parameterized queries to avoid SQL injection
- Stored procedures for complex data retrieval
- Data filtering and aggregation directly in SQL
By preparing clean and structured data at this stage, you reduce the complexity of downstream formatting and improve overall performance.
For a similar scenario involving exporting database query results to Excel instead of PDF, you can also refer to this guide: Export Database to Excel in C#.
2.3 Import Data and Export to PDF with Formatting
After retrieving the data, the next step is to map it into a worksheet, apply formatting, and export it as a PDF document. This approach leverages worksheet-based layout control to ensure the output remains structured and readable.
using Spire.Xls;
using System.Drawing;
// Create workbook and worksheet
Workbook workbook = new Workbook();
Worksheet sheet = workbook.Worksheets[0];
// Import DataTable with headers
sheet.InsertDataTable(dataTable, true, 1, 1);
// Format header row
CellRange headerRange = sheet.Range[1, 1, 1, dataTable.Columns.Count];
headerRange.Style.Font.IsBold = true;
headerRange.Style.Font.Size = 11;
headerRange.Style.Color = Color.LightGray;
// Apply borders to enhance table structure
CellRange dataRange = sheet.AllocatedRange;
dataRange.BorderAround(LineStyleType.Thin);
dataRange.BorderInside(LineStyleType.Thin);
// Align content for consistency
dataRange.Style.HorizontalAlignment = HorizontalAlignType.Center;
dataRange.Style.VerticalAlignment = VerticalAlignType.Center;
// Auto-fit columns for better layout
sheet.AllocatedRange.AutoFitColumns();
// Center the content horizontally in the page
sheet.PageSetup.CenterHorizontally = true;
// Export to PDF
workbook.SaveToFile("SalesReport_2026.pdf", FileFormat.PDF);
This step combines layout control and PDF generation into a single workflow.
Key points to note:
-
Worksheet as layout engine The worksheet acts as a structured canvas where database data is arranged into rows and columns. This ensures the original tabular structure is preserved in the final document.
-
Formatting directly impacts PDF output Adjustments such as column width, font style, and borders are not just visual improvements—they determine how the content is rendered in the PDF. Poor formatting can lead to truncated text or unreadable layouts.
-
Automatic pagination When exporting, the worksheet content is automatically split across pages based on layout and paper size, which is particularly useful for large datasets.
For further layout optimization, you can enhance the table formatting by:
- Enabling text wrapping for long fields
- Applying number/date formats for better readability
If your project requires more flexible PDF structure control, you can also explore converting DataTable to PDF in C# directly using Spire.PDF for .NET, which provides more advanced document-level layout capabilities for complex reporting needs.
3. Complete C# Example for Converting Databases to PDF
Below is the complete implementation that combines database retrieval, data formatting, and PDF export into a single workflow.
using System;
using System.Data;
using Microsoft.Data.SqlClient;
using Spire.Xls;
using System.Drawing;
class Program
{
static void Main()
{
// Step 1: Retrieve data from database
string connectionString = "Server=localhost\\SQLEXPRESS;Database=SalesDB;User ID=demouser;Password=YourPassword;Encrypt=true;TrustServerCertificate=true;";
string query = @"
SELECT o.OrderID, c.CustomerName, o.OrderDate, o.TotalAmount
FROM Orders o
JOIN Customers c ON o.CustomerID = c.CustomerID
WHERE YEAR(o.OrderDate) = 2026;
";
DataTable dataTable = new DataTable();
using (SqlConnection conn = new SqlConnection(connectionString))
{
SqlDataAdapter adapter = new SqlDataAdapter(query, conn);
adapter.Fill(dataTable);
}
// Step 2: Create workbook and import data
Workbook workbook = new Workbook();
Worksheet sheet = workbook.Worksheets[0];
sheet.InsertDataTable(dataTable, true, 1, 1);
// Step 3: Apply professional formatting
// Format header row
CellRange headerRange = sheet.Range[1, 1, 1, dataTable.Columns.Count];
headerRange.Style.Font.IsBold = true;
headerRange.Style.Font.Size = 11;
headerRange.Style.Color = Color.LightGray;
// Apply borders
CellRange dataRange = sheet.AllocatedRange;
dataRange.BorderAround(LineStyleType.Thin);
dataRange.BorderInside(LineStyleType.Thin);
// Set alignment
dataRange.Style.HorizontalAlignment = HorizontalAlignType.Center;
dataRange.Style.VerticalAlignment = VerticalAlignType.Center;
// Auto-fit columns
sheet.AllocatedRange.AutoFitColumns();
// Center the content horizontally in the pages
sheet.PageSetup.CenterHorizontally = true;
// Step 4: Export to PDF
workbook.SaveToFile("SalesReport_2026.pdf", FileFormat.PDF);
Console.WriteLine("Database query results successfully exported to PDF.");
}
}
Below is a preview of the generated PDF:

This example demonstrates an end-to-end workflow from SQL query execution to PDF generation.
4. Advanced Scenarios
In real-world applications, exporting database data to PDF often requires more than just basic conversion. You may need to handle batch exports, improve document readability, or adjust layout settings for better presentation. The following examples demonstrate common enhancements for real-world usage.
Export Multiple Query Results
For scenarios such as batch report generation or scheduled tasks, you may need to execute multiple queries and export each result as a separate PDF document:
string[] queries = {
"SELECT * FROM Orders WHERE Status = 'Pending'",
"SELECT * FROM Customers WHERE Region = 'North'"
};
for (int i = 0; i < queries.Length; i++)
{
DataTable dt = ExecuteQuery(queries[i]);
Workbook wb = new Workbook();
Worksheet ws = wb.Worksheets[0];
ws.InsertDataTable(dt, true, 1, 1);
ws.AllocatedRange.AutoFitColumns();
wb.SaveToFile($"Report_{i + 1}.pdf", FileFormat.PDF);
}
This approach is useful for automating report generation where multiple datasets need to be exported independently.
Add Title and Metadata
To improve readability and provide context, you can add a title row above the data before exporting to PDF:
// Insert title row
sheet.InsertRow(1);
sheet.Range[1, 1].Text = "Sales Report - 2026";
sheet.Range[1, 1].Style.Font.IsBold = true;
sheet.Range[1, 1].Style.Font.Size = 14;
// Merge title cells
sheet.Range[1, 1, 1, dataTable.Columns.Count].Merge();
// Auto-fit the title row
sheet.AutoFitRow(1);
The following image shows the generated PDF with the title row applied:

Adding a title helps users quickly understand the context of the document, especially when sharing or printing reports.
Set Page Size, Orientation, and Margins
To ensure the PDF layout fits your data properly, you can configure page size, orientation, and margins before exporting:
// Set the page size and orientation
sheet.PageSetup.PaperSize = PaperSizeType.PaperA4;
sheet.PageSetup.Orientation = PageOrientationType.Portrait;
// Set the page margins
sheet.PageSetup.TopMargin = 0.5f;
sheet.PageSetup.BottomMargin = 0.2f;
sheet.PageSetup.LeftMargin = 0.2f;
sheet.PageSetup.RightMargin = 0.2f;
Adjusting these settings helps prevent content overflow and ensures consistent layout across different reports.
Control Page Layout and Scaling
When working with large tables, you may need to control how content is distributed across pages. By default, content is split automatically, but you can adjust scaling behavior to fit more data within a page.
// Fit content to page width
workbook.ConverterSetting.SheetFitToWidth = true;
// Fit entire sheet into a single page (may reduce readability)
workbook.ConverterSetting.SheetFitToPage = true;
SheetFitToWidthensures the table fits within the page width while allowing vertical paginationSheetFitToPagescales the entire worksheet to fit into a single page
These settings are useful when generating compact reports, but should be used carefully to avoid making text too small.
Add Headers and Footers
Headers and footers are useful for adding contextual information such as report titles, timestamps, or page numbers:
sheet.PageSetup.LeftHeader = "&\"Arial,Bold\"&16 Sales Report - 2026";
sheet.PageSetup.RightHeader = "&\"Arial,Italic\"&10 Generated on &D";
sheet.PageSetup.CenterFooter = "&\"Arial,Regular\"&16 Page &P of &N";
The following image shows the generated PDF with headers and footers applied:

These elements improve document navigation and are especially valuable for multi-page reports.
Encrypt PDFs
To protect sensitive data, you can apply encryption to the exported PDF:
workbook.ConverterSetting.PdfSecurity.Encrypt("openpsd");
Encryption ensures that only authorized users can access the document, which is important for reports containing confidential or business-critical data.
For more related scenarios involving document export and PDF customization, you can also explore Excel to PDF conversion in C#.
5. Common Pitfalls
Database Connection Issues
Ensure the connection string is correct and the database server is accessible. Verify authentication settings (e.g., SQL authentication or integrated security) and confirm that encryption-related parameters match your environment configuration.
Empty Query Results
Check whether the DataTable contains data before proceeding. Empty result sets may lead to blank PDFs or unexpected formatting behavior.
if (dataTable.Rows.Count == 0)
{
Console.WriteLine("No data found for the specified query.");
return;
}
In production scenarios, you may also choose to generate a placeholder PDF or log the issue instead of exiting the process.
Column Width Overflow
When working with long text fields, AutoFitColumns() may produce excessively wide columns, which can negatively affect PDF layout.
To improve readability, consider:
- Setting a maximum column width
- Enabling text wrapping for long content
- Manually adjusting key columns based on data type
This is especially important when exporting large datasets with variable-length text.
Missing Font Support
If the exported PDF contains special characters (e.g., non-Latin text) or custom fonts, ensure the required fonts are installed and accessible at runtime.
Missing fonts may cause text rendering issues or fallback substitutions, which can affect document appearance and readability.
Unexpected PDF Layout
If the exported PDF layout appears compressed or improperly scaled, check page setup and scaling options such as SheetFitToWidth or SheetFitToPage.
Improper scaling may cause content to appear too small or distort the original table structure.
Conclusion
This article demonstrated a practical approach to converting database query results to PDF in C#. By combining structured data retrieval with worksheet-based formatting, you can generate clear and professional documents directly from SQL data.
This method is particularly effective for report generation and data presentation scenarios where maintaining table structure and readability is essential.
If you are evaluating Spire.XLS, you can request a free temporary license to remove evaluation limitations during development.
FAQ
Can Spire.XLS export database data to PDF without third-party tools?
Yes. Spire.XLS performs all operations independently and does not require Microsoft Office or any other external tools.
How do I handle large datasets when exporting to PDF?
For large datasets, consider paginating the results or filtering the query to retrieve only necessary data. You can also adjust PDF page settings to optimize output size.
Can I customize the PDF page layout?
Yes. Spire.XLS allows you to configure page settings including orientation, margins, and paper size before exporting to PDF.
Does this method work with databases other than SQL Server?
Yes. The approach works with any database that supports ADO.NET data providers, including MySQL, PostgreSQL, and Oracle. Simply use the appropriate connection class and data adapter.
Should I use Microsoft.Data.SqlClient or System.Data.SqlClient?
For modern .NET applications, it is recommended to use Microsoft.Data.SqlClient. It is actively maintained and provides better support for newer SQL Server features, while System.Data.SqlClient is considered legacy and no longer receives major updates.

VBA macros provide a convenient way to automate data processing, formatting, and business logic within Excel, especially for repetitive or rule-based tasks. However, managing macros manually inside Excel becomes inefficient when you need to generate, update, or maintain files at scale.
For .NET developers, a more scalable approach is to programmatically add macros to Excel using C#. By working with Excel VBA in C#, you can write Excel macros, read existing macro code, and modify VBA logic across multiple files without opening Excel and manually editing the VBA code.
In this article, you'll learn how to add, read, and edit Excel VBA macros in C# for automated workflows. Using Spire.XLS for .NET, you can directly manipulate VBA projects embedded in Excel files without requiring Microsoft Excel installation.
Quick Navigation
- How C# Works with Excel Macros
- Environment Setup
- How to Add a Macro to Excel in C#
- Read, Edit, and Remove Macros from Excel in C#
- Common Pitfalls When Working with Excel Macros
- Conclusion
- Frequently Asked Questions
Understanding Excel Macros and How C# Works with Them
An Excel macro is essentially VBA (Visual Basic for Applications) code stored inside a workbook. In macro-enabled files (such as .xlsm), this code is organized within a VBA project, which contains modules and procedures.
In practice, most programmatic macro operations focus on standard modules, which are simple containers for VBA procedures (Sub or Function). When working with C#, you typically do not interact with Excel UI elements like buttons or events. Instead, you directly create modules and inject VBA code.
From a technical perspective, macros are not just executable scripts—they are part of the file structure. This means they can be accessed and modified programmatically, provided that your tool supports VBA project manipulation.
In C#, there are two main approaches to working with Excel macros:
- Using Microsoft Excel Interop to automate Excel
- Using standalone libraries such as Spire.XLS for .NET to manipulate the file directly
The first approach depends on Excel being installed and is typically used to execute macros. The second approach allows you to create, read, edit, and delete VBA code directly inside Excel files, making it more suitable for backend services and automated workflows.
In the following sections, we'll focus on the second approach and demonstrate how to manage Excel macros entirely in C#.
Environment Setup: Install Spire.XLS for .NET
Before working with Excel macros in C#, you need to install Spire.XLS for .NET, which allows you to manage Excel files and manipulate VBA projects without requiring Microsoft Excel.
Install via NuGet
You can install Spire.XLS for .NET using NuGet Package Manager:
Install-Package Spire.XLS
Or via the .NET CLI:
dotnet add package Spire.XLS
You can also download the Spire.XLS for .NET package and add it to your project manually.
Project Requirements
- .NET Framework, .NET Core, .NET Standard, or .NET 5+
- No Microsoft Excel installation required
- Supports Windows, Linux, and macOS environments
The library is runtime-agnostic and can be used in any .NET-supported environment, including web, desktop, and cross-platform applications.
Namespace to Import
After installation, include the following namespaces in your project:
using Spire.Xls;
Once the setup is complete, you can start creating, reading, and modifying Excel VBA macros programmatically.
How to Add a Macro to Excel in C# (Complete Example)
Before writing macro code, it's important to understand how Spire.XLS exposes the VBA structure in Excel:
- Workbook.VbaProject represents the VBA project embedded in the workbook
- IVbaProject.Modules provides access to all VBA modules
- IVbaModule represents an individual module that stores macro code
In addition to modules, the VBA project also supports project-level configuration, such as name, description, password protection, and conditional compilation settings.
The following example shows how to create a VBA project, configure its properties, add a module, and write a macro into an Excel file programmatically.
using Spire.Xls;
Workbook workbook = new Workbook();
// Create VBA project
IVbaProject vbaProject = workbook.VbaProject;
// Configure project-level properties (optional but important in real scenarios)
vbaProject.Name = "ReportGenerator";
vbaProject.Description = "VBA project for generating quarterly reports";
vbaProject.Password = "securepassword";
vbaProject.LockProjectView = false;
vbaProject.HelpFileName = "ReportGeneratorHelp.chm";
vbaProject.ConditionalCompilation = "DEBUG_MODE=1"; // External compile-time flag
// Add a VBA module
IVbaModule vbaModule = vbaProject.Modules.Add("ReportModule", VbaModuleType.Module);
// Write VBA macro code
vbaModule.SourceCode = @"
Sub GenerateQuarterlyReport()
Dim ws As Worksheet
Dim i As Integer
Dim quarter As String
Set ws = ActiveSheet
ws.Cells.Clear
' Prompt user input
quarter = InputBox(""Enter quarter (e.g., Q1 2026):"", ""Report Generation"")
With ws
.Range(""A1:E1"").Value = Array(""Date"", ""Product"", ""Region"", ""Sales"", ""Status"")
For i = 1 To 50
.Cells(i + 1, 1).Value = DateSerial(2026, 1 + Int((i - 1) / 17), 1 + ((i - 1) Mod 17))
.Cells(i + 1, 2).Value = ""Product "" & (1 + Int((i - 1) / 10))
.Cells(i + 1, 3).Value = Choose((i Mod 5) + 1, ""North"", ""South"", ""East"", ""West"", ""Central"")
.Cells(i + 1, 4).Value = Round(Rnd() * 10000, 2)
.Cells(i + 1, 5).Value = IIf(.Cells(i + 1, 4).Value > 5000, ""Target Met"", ""Below Target"")
Next i
.Columns(""A:E"").AutoFit
' Conditional compilation example
#If DEBUG_MODE = 1 Then
MsgBox ""DEBUG: Report generated (50 rows)"", vbInformation, ""Debug Info""
#End If
End With
MsgBox ""Quarterly report for "" & quarter & "" generated successfully!"", vbInformation, ""Report Status""
End Sub";
// Save as macro-enabled Excel file
workbook.SaveToFile("QuarterlyReportGenerator.xlsm", FileFormat.Version2016);
workbook.Dispose();
Below is a preview of the generated Macro in Excel:

In this workflow, Spire.XLS allows you to construct the full VBA structure—not only modules and macro code, but also project-level metadata and compilation behavior—directly in code. This is conceptually similar to configuring a VBA project in the editor, but fully automated and independent of Excel.
Key API Breakdown
-
workbook.VbaProject
Initializes or retrieves the VBA project within the workbook.
-
vbaProject.Modules.Add(name, type)
Adds a new module to the project as a container for macro code.
-
vbaModule.SourceCode
Defines the full VBA script inside the module.
-
vbaProject.Description
Adds a description to the VBA project.
-
vbaProject.CodePage
Defines the character encoding used in the VBA project.
-
vbaProject.Password / LockProjectView
Controls access and visibility of the VBA project.
-
vbaProject.ConditionalCompilation
Enables compile-time flags (e.g., debug logic) inside VBA using #If.
Workbooks containing macros must be saved in macro-enabled formats such as .xls, .xlsm, .xltm, or .xlsb, as other formats (e.g., .xlsx) do not support VBA. Refer to the FileFormat enumeration to correctly set the output format when saving.
Reading, Editing, and Removing Excel Macros in C#
Once a VBA project exists, you can use Spire.XLS to inspect and modify its contents programmatically, including both module code and project-level metadata.
In real-world scenarios, this allows you to analyze existing macros, update business logic, enforce security settings, or remove legacy VBA code without opening Excel. Whether you're maintaining automated reporting systems or processing third-party Excel files, having full control over VBA projects in code is essential.
Read Macro Code and Project-Level Metadata
To read macros from an Excel file, load the workbook and iterate through all modules in the VBA project.
using Spire.Xls;
Workbook workbook = new Workbook();
workbook.LoadFromFile("QuarterlyReportGenerator.xlsm");
IVbaProject vbaProject = workbook.VbaProject;
string macroInfo = "VBA Project Name: " + vbaProject.Name + Environment.NewLine;
macroInfo += "Code Page: " + vbaProject.CodePage + Environment.NewLine;
macroInfo += "Is Protected: " + vbaProject.IsProtected + Environment.NewLine;
foreach (IVbaModule module in vbaProject.Modules)
{
macroInfo += Environment.NewLine + "Module: " + module.Name + Environment.NewLine;
macroInfo += "Source Code:" + Environment.NewLine;
macroInfo += module.SourceCode + Environment.NewLine;
}
Console.WriteLine(macroInfo);
System.IO.File.WriteAllText("MacroAnalysis.txt", macroInfo);
workbook.Dispose();
Below is a preview of the console output:

Here, each module exposes its VBA code through the SourceCode property, while the VBA project itself provides metadata such as name, encoding, and protection status.
The property vbaProject.IsProtected in this example indicates whether the VBA project is protected (locked for viewing or editing).
Edit Macro Code and Project-Level Metadata
To edit a macro, access a specific module and update its SourceCode. You can also modify project-level properties if needed. If the VBA project is protected, the correct password must be provided through vbaProject.Password before making such changes.
using Spire.Xls;
Workbook workbook = new Workbook();
workbook.LoadFromFile("QuarterlyReportGenerator.xlsm");
IVbaProject vbaProject = workbook.VbaProject;
// Update macro logic
IVbaModule module = vbaProject.Modules["ReportModule"]; // Or use index if name is unknown
module.SourceCode = module.SourceCode.Replace(
"\"North\", \"South\", \"East\", \"West\", \"Central\"",
"\"North America\", \"Europe\", \"Asia Pacific\", \"Latin America\", \"Middle East\"");
// Update project metadata
// For protected VBA projects, passwords are required to modify the project-level metadata
vbaProject.Password = "securepassword";
vbaProject.Description = "Updated report logic with global regions";
vbaProject.Name = "UpdatedReportGenerator";
workbook.SaveToFile("UpdatedReportGenerator.xlsm", FileFormat.Version2016);
workbook.Dispose();
Below is a preview of the updated macro code:

Spire.XLS treats macro code as editable text within a module, so updates are typically done by modifying the existing source and saving the workbook.
If you need to convert legacy .xls files to .xlsm format while preserving macros, see How to Convert XLS to XLSM and Maintain Macros Using C#.
Remove Macros
To remove macros, delete modules from the VBA project. This effectively removes all macro logic from the workbook.
using Spire.Xls;
using Spire.Xls.Core.Spreadsheet;
Workbook workbook = new Workbook();
workbook.LoadFromFile("LegacyReport.xlsm");
IVbaProject vbaProject = workbook.VbaProject;
// Remove specific module by its name
vbaProject.Modules.Remove("ReportModule");
// Or remove a module by its index
vbaProject.Modules.RemoveAt(0);
// Remove all modules
vbaProject.Modules.Clear();
workbook.SaveToFile("CleanReport.xlsm", FileFormat.Version2016);
workbook.Dispose();
After removing all modules, the workbook no longer contains executable VBA code, making it equivalent to a macro-free Excel file.
Alternatively, converting a macro-enabled Excel file to .xlsx will remove all macros, since the .xlsx format does not support VBA code.
Common Pitfalls When Working with Excel Macros
File Format Requirements
Macros require macro-enabled formats such as .xlsm, .xls, or .xlsb. The .xlsx format does not support VBA code. Always save files with the appropriate extension.
Encoding Issues
VBA code uses specific encoding (typically CodePage 1252 for Western languages). When working with international characters, ensure the CodePage is set correctly before writing macro code.
Common CodePage values include:
- 1252 – English / Western European (default in many environments)
- 936 – Simplified Chinese (GBK)
- 950 – Traditional Chinese (Big5)
- 932 – Japanese (Shift-JIS)
- 65001 – UTF-8 (⚠️ limited support in VBA, may cause compatibility issues)
Macro Security Settings
Excel macro security settings may prevent macros from running. When distributing macro-enabled files, consider adding instructions for enabling macros or digitally signing the VBA project.
Excel Version Compatibility
VBA code written for newer Excel versions may not work correctly in older versions. Test macros across target Excel versions to ensure compatibility.
Conclusion
C# provides robust capabilities for managing Excel macros programmatically. By using Spire.XLS for .NET, developers can add, read, edit, and remove VBA code directly inside Excel files without requiring Excel installation.
This approach focuses on managing macro code rather than executing it, making it ideal for automated workflows, backend services, and large-scale document processing.
To test these features without limitations, you can apply for a free temporary license of Spire.XLS for .NET.
Frequently Asked Questions (FAQ)
Can C# add VBA macros without Microsoft Excel?
Yes. Libraries such as Spire.XLS for .NET allow you to manipulate VBA projects directly without requiring Excel installation.
What format is required for Excel macros?
Macros require macro-enabled formats such as .xlsm, .xls, .xlsb, or .xltm (for templates). The .xlsx format does not support VBA code.
Can I edit existing macros in Excel using C#?
Yes. You can access VBA modules and modify their source code programmatically to update business logic or fix issues across multiple files.
Is this approach suitable for server environments?
Yes. Since it does not rely on Microsoft Excel, this approach is ideal for backend services, automated systems, and cloud-based applications.
Can I read macro code from password-protected Excel files?
It depends on the type of protection applied to the Excel file.
- Workbook protection (file password): You must provide the password (e.g., via Workbook.OpenPassword) when loading the file before accessing its contents.
- VBA project protection: Reading macro code is generally allowed, but modifying project-level properties (such as name or description) requires the VBA project password.
Additionally, if the Excel file is digitally signed, any modification to the document (including macros or metadata) will invalidate the signature.