<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jen</title>
    <description>The latest articles on DEV Community by Jen (@jenll).</description>
    <link>https://dev.to/jenll</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3826848%2Fc566bd52-364f-45f1-b4ca-91b1ef8efd21.png</url>
      <title>DEV Community: Jen</title>
      <link>https://dev.to/jenll</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jenll"/>
    <language>en</language>
    <item>
      <title>How to Export Excel to PDF in .NET (Fast &amp; Accurate Method)</title>
      <dc:creator>Jen</dc:creator>
      <pubDate>Mon, 30 Mar 2026 07:46:31 +0000</pubDate>
      <link>https://dev.to/jenll/how-to-export-excel-to-pdf-in-net-fast-accurate-method-3d8i</link>
      <guid>https://dev.to/jenll/how-to-export-excel-to-pdf-in-net-fast-accurate-method-3d8i</guid>
      <description>&lt;p&gt;If you’ve ever tried exporting Excel to PDF in a backend service, you probably started with &lt;code&gt;Microsoft.Office.Interop.Excel&lt;/code&gt;. And then it broke.&lt;/p&gt;

&lt;p&gt;No Office is installed on the server. Random crashes under load. Processes that refuse to die. It might work on your machine — but not in production. What you really want is something predictable: no Office dependency, no layout surprises, and something that actually survives in a server or container environment.&lt;/p&gt;

&lt;p&gt;So how do you do that cleanly in .NET?&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Traditional Approaches Fall Short
&lt;/h2&gt;

&lt;p&gt;The go-to options for &lt;a href="https://www.e-iceblue.com/Tutorials/Spire.XLS/Spire.XLS-Program-Guide/Excel-Conversion/NET-Excel-New-method-of-Convert-Excel-to-PDF.html" rel="noopener noreferrer"&gt;Excel-to-PDF conversion in .NET&lt;/a&gt; each come with real trade-offs.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;Microsoft.Office.Interop.Excel&lt;/code&gt;&lt;/strong&gt; requires a full Office installation on the server — something &lt;a href="https://support.microsoft.com/en-us/topic/considerations-for-server-side-automation-of-office-48bcfe93-8a89-47f1-0bce-017433ad79e2" rel="noopener noreferrer"&gt;Microsoft explicitly advises against&lt;/a&gt; for server-side use. Even when it works, it's single-threaded COM automation running in a multi-request environment: slow, fragile, and prone to leaving zombie processes when something goes wrong.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Manual rendering with libraries like iTextSharp&lt;/strong&gt; flips the problem. No Office dependency, but now you're manually reading every cell, recreating styles, handling merged regions, and rebuilding layouts from scratch. The code is brittle, and even after all that work, the output rarely looks like the original.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These approaches either don't scale well or fail to preserve formatting — often both.&lt;/p&gt;

&lt;h2&gt;
  
  
  What We Actually Need
&lt;/h2&gt;

&lt;p&gt;The real challenge isn't just converting Excel to PDF — it's doing it reliably in a backend environment where Office doesn't exist and formatting still has to look right.&lt;/p&gt;

&lt;p&gt;A practical solution needs to check a few boxes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No Microsoft Office dependency&lt;/strong&gt; — must run on a clean server or Linux container.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accurate output&lt;/strong&gt; — merged cells, fonts, column widths, and print layout all preserved.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Handles complex files&lt;/strong&gt; — charts, images, and multi-sheet workbooks shouldn't break it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simple API&lt;/strong&gt; — integration shouldn't require rebuilding your document rendering logic from scratch.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With these requirements in mind, let's look at a practical approach.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started (Minimal Setup)
&lt;/h2&gt;

&lt;p&gt;A practical way to achieve this is by using a dedicated .NET Excel processing library such as &lt;a href="https://www.e-iceblue.com/Introduce/xls-for-net-introduce.html" rel="noopener noreferrer"&gt;Spire.XLS for .NET&lt;/a&gt; — a standalone Excel processing library that handles conversion without any Office dependency.&lt;/p&gt;

&lt;p&gt;Install it via &lt;a href="https://www.nuget.org/packages/Spire.XLS/" rel="noopener noreferrer"&gt;NuGet&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package Spire.XLS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or through the Package Manager Console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Install-Package Spire.XLS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few things worth noting before we write any code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Works with .NET 6, 7, 8 and .NET Framework.&lt;/li&gt;
&lt;li&gt;Runs on Windows, Linux, and in Docker containers.&lt;/li&gt;
&lt;li&gt;No Microsoft Office installation required.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's all the setup needed. Let's convert something.&lt;/p&gt;

&lt;h2&gt;
  
  
  Basic Example: Export Entire Workbook to PDF
&lt;/h2&gt;

&lt;p&gt;Here's the simplest form of the conversion — load a workbook, export to PDF:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Spire.Xls&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Program&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Create a Workbook object&lt;/span&gt;
        &lt;span class="n"&gt;Workbook&lt;/span&gt; &lt;span class="n"&gt;workbook&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Workbook&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// Load the Excel file&lt;/span&gt;
        &lt;span class="n"&gt;workbook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LoadFromFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;@"C:\Users\Sample.xlsx"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Option: Fit each sheet to a page on export&lt;/span&gt;
        &lt;span class="n"&gt;workbook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ConverterSetting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SheetFitToPage&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// Save as PDF&lt;/span&gt;
        &lt;span class="n"&gt;workbook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveToFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;@"C:\Users\Sample.pdf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FileFormat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PDF&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Release resources&lt;/span&gt;
        &lt;span class="n"&gt;workbook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Dispose&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F14tpa59hfb2zudp00a7v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F14tpa59hfb2zudp00a7v.png" alt="Basic Excel to PDF in C#" width="800" height="424"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's the full conversion. No COM objects, no Office installation, no temp files to clean up. This approach works especially well in background jobs, scheduled tasks, or API-based report generation.&lt;/p&gt;

&lt;p&gt;A few things the library handles automatically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Merged cells — preserved as-is.&lt;/li&gt;
&lt;li&gt;Fonts and cell styles — carried over to the output.&lt;/li&gt;
&lt;li&gt;Column widths and row heights — no reflow or guesswork.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;SheetFitToPage = true&lt;/code&gt; is worth setting by default. Without it, wide sheets may split across pages in ways that break the layout — we'll cover more layout controls in a later section.&lt;/p&gt;

&lt;p&gt;At this point, you already have a reliable Excel-to-PDF pipeline that works without Office and preserves most formatting out of the box.&lt;/p&gt;

&lt;h2&gt;
  
  
  Advanced Excel to PDF Conversion Scenarios
&lt;/h2&gt;

&lt;p&gt;The basic example works well — but real-world projects rarely stay that simple. Here are three variations you'll likely run into.&lt;/p&gt;

&lt;h3&gt;
  
  
  Export a Specific Worksheet
&lt;/h3&gt;

&lt;p&gt;Useful when your workbook contains multiple sheets and you only need to export one — a department report, a specific template tab:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Spire.Xls&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Program&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Create a Workbook object&lt;/span&gt;
        &lt;span class="n"&gt;Workbook&lt;/span&gt; &lt;span class="n"&gt;workbook&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Workbook&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// Load the Excel file&lt;/span&gt;
        &lt;span class="n"&gt;workbook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LoadFromFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;@"C:\Users\Multi-SheetChart.xlsx"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Get the second worksheet (index starts from 0)&lt;/span&gt;
        &lt;span class="n"&gt;Worksheet&lt;/span&gt; &lt;span class="n"&gt;sheet&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;workbook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Worksheets&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

        &lt;span class="c1"&gt;// Option: Fit the worksheet to a single page (worksheet level)&lt;/span&gt;
        &lt;span class="n"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PageSetup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FitToPagesWide&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// Fit width to 1 page&lt;/span&gt;
        &lt;span class="n"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PageSetup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FitToPagesTall&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// Height adjusts automatically&lt;/span&gt;

        &lt;span class="c1"&gt;// Save the specific worksheet as PDF&lt;/span&gt;
        &lt;span class="n"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveToPdf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;@"C:\Users\Multi-SheetChart_Sheet2.pdf"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Release resources&lt;/span&gt;
        &lt;span class="n"&gt;workbook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Dispose&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1k11pbt1vlgkgn4w62oz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1k11pbt1vlgkgn4w62oz.png" alt="Specific Worksheet to PDF in C#" width="800" height="468"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Batch Export Multiple Worksheets
&lt;/h3&gt;

&lt;p&gt;When you need each sheet as a separate PDF file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Spire.Xls&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.IO&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Program&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Create a Workbook object and load the Excel file&lt;/span&gt;
        &lt;span class="n"&gt;Workbook&lt;/span&gt; &lt;span class="n"&gt;workbook&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Workbook&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;workbook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LoadFromFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;@"C:\Users\Multi-SheetChart.xlsx"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Get the output directory (same as input file location)&lt;/span&gt;
        &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;outputDir&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetDirectoryName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;@"C:\Users\Multi-SheetChart.xlsx"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Iterate through all worksheets&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;workbook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Worksheets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Worksheet&lt;/span&gt; &lt;span class="n"&gt;sheet&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;workbook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Worksheets&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

            &lt;span class="c1"&gt;// Use the worksheet name as the PDF filename&lt;/span&gt;
            &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;fileName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.pdf"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;outputPath&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Combine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outputDir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// Save the worksheet as PDF&lt;/span&gt;
            &lt;span class="n"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveToPdf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outputPath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Exported: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; → &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// Release resources&lt;/span&gt;
        &lt;span class="n"&gt;workbook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Dispose&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\nAll worksheets exported successfully!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxsrqjc4zvgbfl0xxgslx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxsrqjc4zvgbfl0xxgslx.png" alt="C#: Batch Export Multiple Worksheets" width="800" height="524"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each sheet is saved as an individual file named after the sheet tab. Pair this with &lt;code&gt;Directory.CreateDirectory()&lt;/code&gt; to ensure the output folder exists before running.&lt;/p&gt;

&lt;h3&gt;
  
  
  Export a Defined Print Area
&lt;/h3&gt;

&lt;p&gt;If your sheet has a defined print area — say, a template where only rows 10–34 contain actual data — you can export just that range:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Spire.Xls&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Program&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Create a Workbook object&lt;/span&gt;
        &lt;span class="n"&gt;Workbook&lt;/span&gt; &lt;span class="n"&gt;workbook&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Workbook&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// Load the Excel file&lt;/span&gt;
        &lt;span class="n"&gt;workbook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LoadFromFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;@"C:\Users\StyleSheet.xlsx"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Get the first worksheet&lt;/span&gt;
        &lt;span class="n"&gt;Worksheet&lt;/span&gt; &lt;span class="n"&gt;sheet&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;workbook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Worksheets&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

        &lt;span class="c1"&gt;// Define the range to export&lt;/span&gt;
        &lt;span class="n"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PageSetup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PrintArea&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"A10:H34"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// Optional: Fit the print area to a single page width&lt;/span&gt;
        &lt;span class="n"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PageSetup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FitToPagesWide&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;    &lt;span class="c1"&gt;// Scale width to 1 page&lt;/span&gt;
        &lt;span class="n"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PageSetup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FitToPagesTall&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;    &lt;span class="c1"&gt;// Height automatically adjusts&lt;/span&gt;

        &lt;span class="c1"&gt;// Save the print area as PDF&lt;/span&gt;
        &lt;span class="n"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveToPdf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;@"C:\Users\StyleSheet_PrintArea.pdf"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Release resources&lt;/span&gt;
        &lt;span class="n"&gt;workbook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Dispose&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw6if8iu0un0n2c9avb7d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw6if8iu0un0n2c9avb7d.png" alt="Export a Defined Print Area in C#" width="800" height="391"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is particularly useful for template-based reports where the sheet contains helper formulas or reference data outside the visible range that you don't want included in the output.&lt;/p&gt;

&lt;h2&gt;
  
  
  Returning PDF in ASP.NET Core
&lt;/h2&gt;

&lt;p&gt;In a real backend service, you usually don't want to write files to disk — especially in containerized environments where the filesystem may be read-only or ephemeral. Here's how to convert and return the PDF entirely in memory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.AspNetCore.Mvc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Spire.Xls&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ApiController&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"api/[controller]"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ReportController&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ControllerBase&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;HttpGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"export"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;ExportToPdf&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Load the Excel workbook&lt;/span&gt;
        &lt;span class="n"&gt;Workbook&lt;/span&gt; &lt;span class="n"&gt;workbook&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Workbook&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;workbook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LoadFromFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"report.xlsx"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// Ensure file exists in the correct location&lt;/span&gt;
        &lt;span class="n"&gt;workbook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ConverterSetting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SheetFitToPage&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// Convert directly to memory — no temp file needed&lt;/span&gt;
        &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;MemoryStream&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;workbook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveToStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FileFormat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PDF&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;workbook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Dispose&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// Rewind before reading&lt;/span&gt;
        &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Position&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// Return as file download&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;File&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToArray&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;  &lt;span class="c1"&gt;// Can be optimized (For large files, consider returning the stream directly instead of ToArray())&lt;/span&gt;
            &lt;span class="s"&gt;"application/pdf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"report.pdf"&lt;/span&gt;         &lt;span class="c1"&gt;// triggers download in the browser&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few things worth noting:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;SaveToStream&lt;/code&gt; with &lt;code&gt;FileFormat.PDF&lt;/code&gt; writes directly to the &lt;code&gt;MemoryStream&lt;/code&gt; — no intermediate file on disk.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;stream.Position = 0&lt;/code&gt; rewinds the stream before reading; skipping this returns an empty response.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Content-Disposition&lt;/code&gt; is handled automatically by ASP.NET Core's &lt;code&gt;File()&lt;/code&gt; result when you pass a filename as the third argument.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This pattern works cleanly in Docker containers, Azure App Service, AWS Lambda, or any environment where writing to the local filesystem isn't an option. It also avoids file I/O overhead, which can become a bottleneck under high concurrency.&lt;/p&gt;

&lt;h2&gt;
  
  
  Controlling Layout for Accurate Output
&lt;/h2&gt;

&lt;p&gt;The conversion works — but on complex sheets, the default settings don't always produce clean output. Wide tables get sliced across pages, margins clip content, and landscape data ends up portrait. These three settings fix most of it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Page Size &amp;amp; Orientation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Spire.Xls&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;Workbook&lt;/span&gt; &lt;span class="n"&gt;workbook&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Workbook&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;workbook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LoadFromFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"report.xlsx"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;Worksheet&lt;/span&gt; &lt;span class="n"&gt;sheet&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;workbook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Worksheets&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="c1"&gt;// Set paper size and orientation&lt;/span&gt;
&lt;span class="n"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PageSetup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PaperSize&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PaperSizeType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PaperA4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PageSetup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Orientation&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PageOrientationType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Landscape&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveToPdf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"report.pdf"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;workbook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Dispose&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Margins
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Values are in inches&lt;/span&gt;
&lt;span class="n"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PageSetup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TopMargin&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PageSetup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BottomMargin&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PageSetup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LeftMargin&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PageSetup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RightMargin&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tightening the margins gives your content more room to breathe — particularly useful when you're fitting a wide table onto a single page.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scaling (the important one)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Force the sheet to fit within one page wide&lt;/span&gt;
&lt;span class="n"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PageSetup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FitToPagesWide&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PageSetup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FitToPagesTall&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 0 = no vertical limit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;FitToPagesWide = 1&lt;/code&gt; is the single most effective setting for preventing wide sheets from splitting mid-column across pages. Setting &lt;code&gt;FitToPagesTall = 0&lt;/code&gt; lets the content grow vertically as needed — you get a clean single-column layout without squashing everything onto one page.&lt;/p&gt;

&lt;p&gt;These three settings mirror what you'd configure in &lt;a href="https://www.e-iceblue.com/Tutorials/Spire.XLS/Spire.XLS-Program-Guide/Document-Operation/How-to-set-Excel-page-margins-before-printing-a-worksheet-in-C.html" rel="noopener noreferrer"&gt;Excel's &lt;strong&gt;Page Layout&lt;/strong&gt; tab before printing&lt;/a&gt; — if the sheet looks right there, it'll look right in the exported PDF.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Issues &amp;amp; Fixes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Fonts render as squares or tofu characters
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; PDF output looks fine on Windows but shows blank boxes where text should be on your CI server or container.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cause:&lt;/strong&gt; The Excel file uses fonts that aren't available in the server environment. The library falls back to a missing font and renders nothing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Install the missing fonts in your environment, or point the library to a custom font directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Tell Spire.XLS where to find fonts at runtime&lt;/span&gt;
&lt;span class="n"&gt;workbook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CustomFontPath&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"/usr/share/fonts/custom"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Windows-style fonts on Linux, installing &lt;code&gt;ttf-mscorefonts-installer&lt;/code&gt; via apt usually resolves the most common cases.&lt;/p&gt;

&lt;h3&gt;
  
  
  Charts or images missing from the PDF
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Your workbook contains charts or embedded images that simply don't appear in the exported PDF.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cause:&lt;/strong&gt; Chart rendering requires the sheet to be processed in a specific rendering mode. Default settings may skip embedded objects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Make sure you're converting via the workbook-level method rather than the worksheet-level one — workbook-level conversion applies full rendering passes, including charts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Prefer this for sheets with charts or images&lt;/span&gt;
&lt;span class="n"&gt;workbook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ConverterSetting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SheetFitToPage&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;workbook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveToFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"report.pdf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FileFormat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PDF&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Layout errors or crashes on Linux / Docker
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Everything works locally on Windows, but after deploying to a Linux container you get a &lt;code&gt;DllNotFoundException&lt;/code&gt; or the PDF layout is completely broken.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cause:&lt;/strong&gt; Spire.XLS depends on &lt;code&gt;System.Drawing.Common&lt;/code&gt;, which in turn requires &lt;code&gt;libgdiplus&lt;/code&gt; — a native library that isn't included in the default .NET Linux base image.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Add the following to your Dockerfile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nt"&gt;--no-install-recommends&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;        libgdiplus &lt;span class="se"&gt;\
&lt;/span&gt;        libc6-dev &lt;span class="se"&gt;\
&lt;/span&gt;        libx11-dev &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/apt/lists/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the &lt;a href="https://www.e-iceblue.com/forum/exception-the-type-initializer-for-spire-xls-core-spreadsh-t10260.html" rel="noopener noreferrer"&gt;officially recommended setup&lt;/a&gt; for running Spire.XLS in Linux containers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap-Up
&lt;/h2&gt;

&lt;p&gt;At this point, you’ve got a solid Excel-to-PDF workflow in .NET — no Office dependencies, no fragile COM automation, and no need to rebuild layouts from scratch. It works just as well for background jobs, APIs, or containerized services, and scales from simple exports to more complex reporting scenarios.&lt;/p&gt;

&lt;p&gt;If you're dealing with document generation in your own projects, I’m curious — how are you handling Excel-to-PDF today?&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>csharp</category>
      <category>dotnet</category>
      <category>productivity</category>
    </item>
    <item>
      <title>OpenXML SDK vs Spire.Doc: Which One Should You Use for Word Processing in C#?</title>
      <dc:creator>Jen</dc:creator>
      <pubDate>Mon, 23 Mar 2026 09:32:00 +0000</pubDate>
      <link>https://dev.to/jenll/openxml-sdk-vs-spiredoc-which-one-should-you-use-for-word-processing-in-c-5gkj</link>
      <guid>https://dev.to/jenll/openxml-sdk-vs-spiredoc-which-one-should-you-use-for-word-processing-in-c-5gkj</guid>
      <description>&lt;p&gt;Processing Word documents in C# is a common requirement in many backend systems, such as report generation, document automation, and data extraction. Developers typically choose between the Open XML SDK, which provides low-level access to the &lt;code&gt;.docx&lt;/code&gt; structure, and third-party libraries like Spire.Doc that offer higher-level APIs.&lt;/p&gt;

&lt;p&gt;Each approach comes with trade-offs in terms of development complexity, feature support, and flexibility. In this article, we’ll compare these two options through a practical scenario to help you choose the right solution for your project.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Understanding .docx and Server-Side Constraints&lt;/li&gt;
&lt;li&gt;
Option A: DocumentFormat.OpenXML SDK

&lt;ul&gt;
&lt;li&gt;Example 1: Read All Paragraph Text&lt;/li&gt;
&lt;li&gt;Example 2: Replace Template Placeholders {{name}}&lt;/li&gt;
&lt;li&gt;Example 3: Insert a Formatted Table&lt;/li&gt;
&lt;li&gt;Limitations&lt;/li&gt;
&lt;li&gt;When OpenXML SDK Is the Right Choice&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Option B: Using Third-Party Libraries (Spire.Doc)

&lt;ul&gt;
&lt;li&gt;Example 1: Read All Paragraph Text&lt;/li&gt;
&lt;li&gt;Example 2: Replace Template Placeholders {{name}}&lt;/li&gt;
&lt;li&gt;Example 3: Insert a Formatted Table&lt;/li&gt;
&lt;li&gt;Example 4: Export to PDF&lt;/li&gt;
&lt;li&gt;Limitations&lt;/li&gt;
&lt;li&gt;When It Makes Sense to Use Third-Party Libraries&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Side-by-Side Comparison&lt;/li&gt;

&lt;li&gt;Performance &amp;amp; Scalability Considerations&lt;/li&gt;

&lt;li&gt;Common Pitfalls and Hidden Traps&lt;/li&gt;

&lt;li&gt;Decision Guide: Which One Should You Choose?&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;a id="1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding .docx and Server-Side Constraints
&lt;/h2&gt;

&lt;p&gt;Before choosing a library, it helps to understand what you're actually dealing with.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;.docx&lt;/code&gt; file is not a binary format. It's a ZIP archive containing a collection of XML files, defined by the ECMA-376 standard — also known as Office Open XML (OOXML). Rename any &lt;code&gt;.docx&lt;/code&gt; to &lt;code&gt;.zip&lt;/code&gt;, extract it, and you'll find a structure like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mydocument.docx (extracted)
├── [Content_Types].xml
├── _rels/
│   └── .rels
└── word/
    ├── document.xml        ← your actual content lives here
    ├── styles.xml          ← paragraph and character styles
    ├── settings.xml
    ├── theme/
    │   └── theme1.xml
    └── _rels/
        └── document.xml.rels
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The main content lives in &lt;code&gt;word/document.xml&lt;/code&gt;. A single paragraph with bold text looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;w:p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;w:r&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;w:rPr&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;w:b/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/w:rPr&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;w:t&amp;gt;&lt;/span&gt;Hello, World!&lt;span class="nt"&gt;&amp;lt;/w:t&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/w:r&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/w:p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;w:p&lt;/code&gt; is a paragraph. &lt;code&gt;w:r&lt;/code&gt; is a run — a contiguous region of text sharing the same formatting. &lt;code&gt;w:rPr&lt;/code&gt; holds the run's properties; &lt;code&gt;w:b&lt;/code&gt; toggles bold. This is the atom of Word's content model, and every library you evaluate is ultimately reading and writing variations of this structure.&lt;/p&gt;

&lt;p&gt;The ECMA-376 specification that defines all of this runs to over 6,000 pages. In practice, you don't need to read it — but you do need to understand that the abstraction level your library provides over this XML is the single most important factor in your day-to-day development experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Server-Side Processing Adds Complexity
&lt;/h3&gt;

&lt;p&gt;Manipulating &lt;code&gt;.docx&lt;/code&gt; files in a desktop context is relatively forgiving. On a server, four constraints change the equation:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No Office installation.&lt;/strong&gt; Microsoft explicitly warns against using Word Automation (COM/Interop) in server environments. That rules out the most "complete" Word-compatible engine unless you license it separately.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cross-platform deployment.&lt;/strong&gt; If your service runs in a Linux container — and most do — any library with a Windows-only dependency is immediately disqualified.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Concurrency.&lt;/strong&gt; A server handling concurrent document generation requests needs thread-safe library behavior. Not all libraries guarantee this, and the ones that don't require per-request instantiation at a minimum.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PDF export.&lt;/strong&gt; "Generate a Word document" almost always means "generate a Word document &lt;em&gt;and&lt;/em&gt; a PDF rendition." The OpenXML SDK has no rendering engine; PDF output requires a separate solution. Some third-party libraries include one.&lt;/p&gt;

&lt;p&gt;These four constraints are what make the OpenXML SDK vs. third-party library decision non-trivial — and why the "just use the free one" instinct doesn't always hold up in production.&lt;/p&gt;

&lt;p&gt;&lt;a id="2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Option A: DocumentFormat.OpenXML SDK
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://learn.microsoft.com/en-us/office/open-xml/open-xml-sdk" rel="noopener noreferrer"&gt;Open XML SDK&lt;/a&gt; is a Microsoft-provided library for working directly with Office Open XML documents such as &lt;code&gt;.docx&lt;/code&gt;, &lt;code&gt;.xlsx&lt;/code&gt;, and &lt;code&gt;.pptx&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Instead of relying on Microsoft Word, it allows you to read and modify documents by interacting with their underlying XML structure. This makes it suitable for server-side environments where installing Office is not an option.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package DocumentFormat.OpenXml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It maps directly to the XML structure described in the previous section — which means it's precise and complete, but also verbose. There is no abstraction between you and the OOXML spec.&lt;/p&gt;

&lt;p&gt;&lt;a id="2-1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Example 1: Read All Paragraph Text
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;DocumentFormat.OpenXml.Packaging&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;DocumentFormat.OpenXml.Wordprocessing&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;ReadParagraphs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WordprocessingDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isEditable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MainDocumentPart&lt;/span&gt;&lt;span class="p"&gt;!.&lt;/span&gt;&lt;span class="n"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;!;&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Descendants&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Paragraph&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Concat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Descendants&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrWhiteSpace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is one of the cleaner operations in the SDK. &lt;code&gt;Descendants&amp;lt;T&amp;gt;()&lt;/code&gt; traverses the XML tree generically, and the LINQ chain stays readable.&lt;/p&gt;

&lt;p&gt;&lt;a id="2-2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Example 2: Replace Template Placeholders &lt;code&gt;{{name}}&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;A common server-side pattern: fill a pre-authored &lt;code&gt;.docx&lt;/code&gt; template with runtime data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;DocumentFormat.OpenXml.Packaging&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;DocumentFormat.OpenXml.Wordprocessing&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;MailMerge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;templatePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;outputPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;WordprocessingDocument&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WordprocessingDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;templatePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MainDocumentPart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Descendants&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;())&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{{name}}"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{{name}}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveAs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outputPath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One important caveat: Word sometimes splits a placeholder like &lt;code&gt;{{name}}&lt;/code&gt; across multiple &lt;code&gt;&amp;lt;w:t&amp;gt;&lt;/code&gt; elements when the user types or edits it — for example, &lt;code&gt;{{&lt;/code&gt;, &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;}}&lt;/code&gt; may each land in a separate &lt;code&gt;Run&lt;/code&gt;. A production-grade implementation needs to consolidate runs within each paragraph before scanning for placeholders. The code above works reliably only for placeholders that were pasted in as a single text node, which is the case for programmatically generated templates.&lt;/p&gt;

&lt;p&gt;&lt;a id="2-3"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Example 3: Insert a Formatted Table
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;DocumentFormat.OpenXml&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;DocumentFormat.OpenXml.Packaging&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;DocumentFormat.OpenXml.Wordprocessing&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Linq&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;InsertTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WordprocessingDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MainDocumentPart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Body&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Descendants&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Paragraph&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FirstOrDefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InnerText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"The table below presents the sales data for key items."&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;InsertAfterSelf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;CreateTable&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;InsertAfterSelf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Paragraph&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;Table&lt;/span&gt; &lt;span class="nf"&gt;CreateTable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Table&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AppendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;TableProperties&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;TableBorders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;TopBorder&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Val&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BorderValues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Single&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Size&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;BottomBorder&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Val&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BorderValues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Single&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Size&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;LeftBorder&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Val&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BorderValues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Single&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Size&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;RightBorder&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Val&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BorderValues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Single&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Size&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;InsideHorizontalBorder&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Val&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BorderValues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Single&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Size&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;InsideVerticalBorder&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Val&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BorderValues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Single&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Size&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;)));&lt;/span&gt;

    &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[,]&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"Product"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Units Sold"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Revenue ($)"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"Laptop"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"1,250"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"1,299,000"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"Monitor"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"850"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"765,000"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"Wireless Mouse"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"3,200"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"92,800"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;TableRow&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;para&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Paragraph&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]));&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RunProperties&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RunProperties&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Bold&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
                &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AppendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;TableCell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;TableCellProperties&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Shading&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Val&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ShadingPatternValues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Clear&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Fill&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"4472C4"&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
                    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Paragraph&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RunProperties&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;])))&lt;/span&gt;
                &lt;span class="p"&gt;));&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;para&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AppendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;para&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ParagraphProperties&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ParagraphProperties&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Justification&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Val&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;JustificationValues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Right&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
                &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AppendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;TableCell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;para&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AppendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is where the verbosity of the SDK becomes apparent. This is where many developers start to feel the friction. The object graph mirrors the XML tree exactly — every border, every shading value, every color is a separate object. This is accurate and fully controllable, but it requires familiarity with the underlying OOXML structure before you can write it confidently.&lt;/p&gt;

&lt;p&gt;&lt;a id="2-4"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Limitations
&lt;/h3&gt;

&lt;p&gt;At first glance, the SDK seems manageable. But as soon as you move beyond simple text operations, complexity increases quickly.&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Verbose and Low-Level API
&lt;/h4&gt;

&lt;p&gt;Even basic formatting requires navigating multiple layers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Paragraph&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Run&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;RunProperties&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A small visual change in Word often translates into a surprisingly large amount of code.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Understanding Document Structure Is Required
&lt;/h4&gt;

&lt;p&gt;To do anything non-trivial, you need to understand how Word structures content internally:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How text is split across runs.&lt;/li&gt;
&lt;li&gt;How styles are applied.&lt;/li&gt;
&lt;li&gt;How relationships (&lt;code&gt;rId&lt;/code&gt;) are managed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without this, it's easy to produce documents that &lt;em&gt;look fine in code&lt;/em&gt; but break in Word.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. No Built-in Rendering (e.g., PDF)
&lt;/h4&gt;

&lt;p&gt;The SDK does not support converting Word documents to PDF.&lt;/p&gt;

&lt;p&gt;To achieve this, you typically need to integrate external tools such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;LibreOffice (via command line).&lt;/li&gt;
&lt;li&gt;Other conversion services.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This adds operational complexity, especially in containerized environments.&lt;/p&gt;

&lt;p&gt;&lt;a id="2-5"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  When OpenXML SDK Is the Right Choice
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Your service runs on Linux or in a container and you cannot take on a commercial dependency.&lt;/li&gt;
&lt;li&gt;You need zero-cost licensing with no per-server or per-document fees.&lt;/li&gt;
&lt;li&gt;You require precise, low-level control over document structure.&lt;/li&gt;
&lt;li&gt;PDF export is not a requirement, or you are prepared to manage a separate rendering pipeline.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In short, OpenXML gives you maximum control — but that control comes with a significant development cost.&lt;/p&gt;

&lt;p&gt;&lt;a id="3"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Option B: Using Third-Party Libraries (Spire.Doc)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.e-iceblue.com/Introduce/word-for-net-introduce.html" rel="noopener noreferrer"&gt;Spire.Doc&lt;/a&gt; is a commercial .NET library developed by E-iceblue. Unlike the OpenXML SDK, it does not expose the OOXML object model directly — instead, it provides a document-oriented API that abstracts the XML layer entirely. It also ships with its own rendering engine, which means PDF export is a first-class feature rather than an afterthought.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package Spire.Doc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The free tier (&lt;code&gt;Spire.Doc for .NET Free&lt;/code&gt;) is available on &lt;a href="https://www.nuget.org/packages/Spire.Doc/" rel="noopener noreferrer"&gt;NuGet&lt;/a&gt; without registration. It supports most core features but imposes two hard limits: documents are capped at 3 pages, and a watermark is appended beyond that threshold. Production use requires a commercial license.&lt;/p&gt;

&lt;p&gt;&lt;a id="3-1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Example 1: Read All Paragraph Text
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Spire.Doc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;ReadAllParagraphs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Document&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LoadFromFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetText&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;Paragraph.Text&lt;/code&gt; property aggregates all runs automatically. No descending into child nodes required.&lt;/p&gt;

&lt;p&gt;&lt;a id="3-2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Example 2: Replace Template Placeholders &lt;code&gt;{{name}}&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Spire.Doc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;MailMerge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;templatePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;outputPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Document&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LoadFromFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;templatePath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{{name}}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveToFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outputPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FileFormat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Docx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Document.Replace()&lt;/code&gt; handles run-splitting internally. The problem described in the OpenXML SDK section — where a placeholder like &lt;code&gt;{{name}}&lt;/code&gt; is split across multiple &lt;code&gt;&amp;lt;w:t&amp;gt;&lt;/code&gt; elements — does not surface here. The library normalizes the text representation before searching.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feqcylw8wevk1vf72wnil.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feqcylw8wevk1vf72wnil.webp" alt="Replace Template Placeholders {{name}}" width="800" height="389"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a id="3-3"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Example 3: Insert a Formatted Table
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Spire.Doc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Spire.Doc.Documents&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Program&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Document&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LoadFromFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"input.docx"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;sel&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FindString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"The table below presents the sales data for key items."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sel&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetAsOneRange&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;OwnerParagraph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Owner&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;idx&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ChildObjects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IndexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetAsOneRange&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;OwnerParagraph&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ChildObjects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;CreateTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
                &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveToFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"output.docx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FileFormat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Docx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Dispose&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;Table&lt;/span&gt; &lt;span class="nf"&gt;CreateTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Document&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[,]&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"Product"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Units Sold"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Revenue ($)"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"Laptop"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"1,250"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"1,299,000"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"Monitor"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"850"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"765,000"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"Wireless Mouse"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"3,200"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"92,800"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="n"&gt;Table&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ResetCells&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
                &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Rows&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;Cells&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;AddParagraph&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;AppendText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The structure is comparable in length to the OpenXML SDK version for this particular operation — table construction is inherently row-and-cell iteration regardless of the library. The difference is in the API surface: &lt;code&gt;cell.CellFormat.BackColor&lt;/code&gt; versus constructing a &lt;code&gt;Shading&lt;/code&gt; object with &lt;code&gt;ShadingPatternValues.Clear&lt;/code&gt; and a hex string.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyxp9us4tf5obp68z2als.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyxp9us4tf5obp68z2als.webp" alt="Insert a Formatted Table" width="800" height="388"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a id="3-4"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Example 4: Export to PDF
&lt;/h3&gt;

&lt;p&gt;The OpenXML SDK does not provide a rendering engine, so PDF export requires integrating external tools (e.g., LibreOffice or commercial renderers).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Spire.Doc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ExportToPdf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;docxPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;pdfPath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;docxPath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveToFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pdfPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FileFormat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PDF&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Spire.Doc uses its own rendering engine to produce the PDF — no LibreOffice, no Word installation, no additional process to manage. Font fidelity and layout accuracy are generally reliable for standard documents; complex layouts with custom fonts may require additional configuration (covered in the pitfalls section).&lt;/p&gt;

&lt;p&gt;&lt;a id="3-5"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Limitations
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. Higher-Level API
&lt;/h4&gt;

&lt;p&gt;Most operations map directly to how developers think about documents:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sections.&lt;/li&gt;
&lt;li&gt;Paragraphs.&lt;/li&gt;
&lt;li&gt;Tables.&lt;/li&gt;
&lt;li&gt;Styles.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This reduces both development time and cognitive overhead.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Rich Feature Support
&lt;/h4&gt;

&lt;p&gt;Common real-world requirements are handled natively:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Word → PDF / HTML / Image conversion.&lt;/li&gt;
&lt;li&gt;Complex table layouts.&lt;/li&gt;
&lt;li&gt;Headers, footers, styles.&lt;/li&gt;
&lt;li&gt;Images and formatting.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  3. Faster Development
&lt;/h4&gt;

&lt;p&gt;Tasks that require dozens of lines in OpenXML can often be implemented in just a few lines here.&lt;/p&gt;

&lt;p&gt;👉 This difference becomes more significant as document complexity increases.&lt;/p&gt;

&lt;h3&gt;
  
  
  Trade-offs to Consider
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. Licensing
&lt;/h4&gt;

&lt;p&gt;Spire.Doc offers a free version with limitations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Watermarks added to longer documents.&lt;/li&gt;
&lt;li&gt;Some advanced features are restricted.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For production use, a commercial license is typically required.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Less Low-Level Control
&lt;/h4&gt;

&lt;p&gt;Since the library abstracts away the XML layer, fine-grained control over document internals is more limited compared to OpenXML.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Dependency Size and Runtime Considerations
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Larger package size than OpenXML.&lt;/li&gt;
&lt;li&gt;Cold start impact in serverless or container environments.&lt;/li&gt;
&lt;li&gt;Closed-source — debugging internal issues may be harder.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a id="3-6"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  When It Makes Sense to Use Third-Party Libraries
&lt;/h3&gt;

&lt;p&gt;This approach is often a better fit when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need &lt;strong&gt;document conversion (e.g., Word → PDF)&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;You are dealing with &lt;strong&gt;complex layouts or formatting&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;You want to &lt;strong&gt;reduce development time&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Your project has a &lt;strong&gt;budget for commercial components&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In short, third-party libraries trade low-level control for productivity — and in many real-world applications, that trade-off is worth it.&lt;/p&gt;

&lt;p&gt;&lt;a id="4"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Side-by-Side Comparison
&lt;/h2&gt;

&lt;p&gt;The previous two sections walked through identical operations with both libraries. Here is a consolidated view across the dimensions that matter most for a production decision.&lt;/p&gt;

&lt;h3&gt;
  
  
  Feature Comparison
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;OpenXML SDK&lt;/th&gt;
&lt;th&gt;Spire.Doc (Free)&lt;/th&gt;
&lt;th&gt;Spire.Doc (Commercial)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Read / write &lt;code&gt;.docx&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Template placeholder replace&lt;/td&gt;
&lt;td&gt;✅ ⚠️&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Table insertion&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mail merge&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PDF export&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅ ⚠️&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTML export&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Image insertion&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Header / footer editing&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Document encryption&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Page limit&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;3 pages&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Watermark on output&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes (&amp;gt;3 pages)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;⚠️ OpenXML SDK placeholder replacement requires manual run consolidation to handle split text nodes.&lt;br&gt;&lt;br&gt;
⚠️ Spire.Doc Free appends a watermark on documents exceeding 3 pages.&lt;/p&gt;
&lt;h3&gt;
  
  
  Technical Characteristics
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;OpenXML SDK&lt;/th&gt;
&lt;th&gt;Spire.Doc&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;License&lt;/td&gt;
&lt;td&gt;MIT&lt;/td&gt;
&lt;td&gt;Commercial (free tier available)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NuGet package size&lt;/td&gt;
&lt;td&gt;~7 MB&lt;/td&gt;
&lt;td&gt;~130 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;.NET support&lt;/td&gt;
&lt;td&gt;.NET 6 / 7 / 8 / Standard 2.0&lt;/td&gt;
&lt;td&gt;.NET 6 / 7 / 8 / Standard 2.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Linux / macOS support&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅ ⚠️&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Thread safety&lt;/td&gt;
&lt;td&gt;✅ (per-instance)&lt;/td&gt;
&lt;td&gt;❌ (per-request instantiation required)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PDF export&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Source available&lt;/td&gt;
&lt;td&gt;✅ (GitHub)&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vendor support&lt;/td&gt;
&lt;td&gt;Community / GitHub Issues&lt;/td&gt;
&lt;td&gt;Commercial support included&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Learning curve&lt;/td&gt;
&lt;td&gt;High (OOXML knowledge required)&lt;/td&gt;
&lt;td&gt;Low to medium&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;⚠️ Spire.Doc on Linux requires manual font installation for accurate rendering. Documents using fonts not present on the host system will fall back silently, which can affect PDF layout fidelity.&lt;/p&gt;
&lt;h3&gt;
  
  
  Operational Cost Comparison
&lt;/h3&gt;

&lt;p&gt;This is the dimension developers most commonly overlook during library selection.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Cost factor&lt;/th&gt;
&lt;th&gt;OpenXML SDK&lt;/th&gt;
&lt;th&gt;Spire.Doc&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Library license&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;Free tier / paid commercial&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PDF rendering&lt;/td&gt;
&lt;td&gt;LibreOffice or equivalent required&lt;/td&gt;
&lt;td&gt;Included&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Infrastructure overhead&lt;/td&gt;
&lt;td&gt;LibreOffice process management&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Linux font setup&lt;/td&gt;
&lt;td&gt;Not required&lt;/td&gt;
&lt;td&gt;Required for non-Latin fonts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Debug ceiling&lt;/td&gt;
&lt;td&gt;Full source&lt;/td&gt;
&lt;td&gt;Public API only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The OpenXML SDK's zero license cost is real, but "free" does not account for the engineering time spent implementing PDF export, managing a LibreOffice process in containers, and handling edge cases in the OOXML object model. In a commercial project with a team of more than two developers, that time has a measurable cost.&lt;/p&gt;
&lt;h3&gt;
  
  
  Summary Recommendation
&lt;/h3&gt;

&lt;p&gt;Neither library is categorically better. &lt;/p&gt;

&lt;p&gt;If you are building a service where document processing is peripheral — one feature among many — Spire.Doc's higher-level API reduces the surface area of code you need to own and maintain. If document structure manipulation is central to your product and you need full visibility into the output, the OpenXML SDK gives you that control without any licensing constraints.&lt;/p&gt;

&lt;p&gt;&lt;a id="5"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  6. Performance &amp;amp; Scalability Considerations
&lt;/h2&gt;

&lt;p&gt;When moving from prototypes to production systems, performance and scalability become critical factors — especially in high-load scenarios such as batch processing or document generation APIs.&lt;/p&gt;
&lt;h3&gt;
  
  
  Thread Safety
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;OpenXML SDK&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Open XML SDK is suitable for server-side use, but its document instances are not thread-safe and should not be shared across threads.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Each document must be handled within its own scope.&lt;/li&gt;
&lt;li&gt;Avoid sharing document instances across threads.&lt;/li&gt;
&lt;li&gt;Proper use of &lt;code&gt;using&lt;/code&gt; blocks is essential to release resources.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 In practice, it works well in parallel processing as long as each task operates on its own file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Spire.Doc&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Spire.Doc is &lt;strong&gt;not thread-safe by design&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You should create a new &lt;code&gt;Document&lt;/code&gt; instance per request.&lt;/li&gt;
&lt;li&gt;Avoid reusing instances in multi-threaded environments.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 This is a common pattern for most document libraries, but it needs to be explicitly handled in high-concurrency systems.&lt;/p&gt;
&lt;h3&gt;
  
  
  Memory Usage
&lt;/h3&gt;

&lt;p&gt;Memory consumption depends heavily on document size and complexity.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;OpenXML SDK&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;More lightweight in terms of dependencies.&lt;/li&gt;
&lt;li&gt;Gives you finer control over how data is processed.&lt;/li&gt;
&lt;li&gt;Can be optimized for streaming scenarios.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Spire.Doc&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Loads more structure into memory due to higher-level abstractions.&lt;/li&gt;
&lt;li&gt;Typically uses more memory for complex documents.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 For small to medium documents, the difference is usually negligible.&lt;br&gt;
👉 For large files (e.g., 10MB+ with images and tables), memory usage becomes a factor worth testing.&lt;/p&gt;
&lt;h3&gt;
  
  
  Performance in Practice
&lt;/h3&gt;

&lt;p&gt;In real-world scenarios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OpenXML may perform better for &lt;strong&gt;simple, targeted operations&lt;/strong&gt; (e.g., text replacement, metadata updates).&lt;/li&gt;
&lt;li&gt;Spire.Doc often performs better for &lt;strong&gt;complex workflows&lt;/strong&gt;, since it avoids manual XML manipulation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 A key insight:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In most real-world systems, development time and maintainability matter more than raw execution speed.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Cold Start &amp;amp; Deployment Considerations
&lt;/h3&gt;

&lt;p&gt;For modern deployments (e.g., Docker, serverless):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;OpenXML SDK&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Smaller package size.&lt;/li&gt;
&lt;li&gt;Faster cold start.&lt;/li&gt;
&lt;li&gt;Minimal external dependencies.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Spire.Doc&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Larger DLL size.&lt;/li&gt;
&lt;li&gt;Slightly slower cold start in environments like AWS Lambda or Azure Functions.&lt;/li&gt;
&lt;li&gt;May require additional setup (e.g., fonts in Linux containers).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Use OpenXML when you need &lt;strong&gt;lightweight, controlled processing at scale&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Use Spire.Doc when you need &lt;strong&gt;feature-rich processing with less development overhead&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a id="6"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  7. Common Pitfalls and Hidden Traps
&lt;/h2&gt;

&lt;p&gt;No matter which approach you choose, there are a few non-obvious issues that can cause serious problems in production.&lt;/p&gt;

&lt;p&gt;Here are some of the most common ones.&lt;/p&gt;
&lt;h3&gt;
  
  
  OpenXML SDK Pitfalls
&lt;/h3&gt;
&lt;h4&gt;
  
  
  1. Broken Documents Due to Relationship ID Conflicts
&lt;/h4&gt;

&lt;p&gt;When adding images, styles, or other parts, each element is linked using a relationship ID (&lt;code&gt;rId&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Hardcoding or reusing IDs can lead to corrupted files:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Word found unreadable content…”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;👉 &lt;strong&gt;Best practice:&lt;/strong&gt;&lt;br&gt;
Always use built-in methods to generate IDs instead of manually assigning them.&lt;/p&gt;
&lt;h4&gt;
  
  
  2. Text Replacement Isn’t as Simple as It Looks
&lt;/h4&gt;

&lt;p&gt;In Word, text is often split across multiple &lt;code&gt;Run&lt;/code&gt; elements.&lt;/p&gt;

&lt;p&gt;So a placeholder like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight handlebars"&gt;&lt;code&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;may actually be stored as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight handlebars"&gt;&lt;code&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;na&lt;/span&gt;
&lt;span class="nv"&gt;me&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;👉 This breaks naive string replacement logic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Normalize runs before replacement.&lt;/li&gt;
&lt;li&gt;Or use more robust search strategies.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  3. Strict vs Transitional OOXML Compatibility
&lt;/h4&gt;

&lt;p&gt;Not all &lt;code&gt;.docx&lt;/code&gt; files follow the same OOXML standard.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Some use &lt;strong&gt;Strict&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Others use &lt;strong&gt;Transitional&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 This can lead to unexpected parsing or formatting issues.&lt;/p&gt;

&lt;h3&gt;
  
  
  Spire.Doc Pitfalls
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. Watermarks in Production
&lt;/h4&gt;

&lt;p&gt;The free version of Spire.Doc adds watermarks to documents beyond certain limits.&lt;/p&gt;

&lt;p&gt;This can easily slip into production if licensing is not configured properly.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;Best practice:&lt;/strong&gt;&lt;br&gt;
Load the license at application startup (e.g., via environment variables).&lt;/p&gt;
&lt;h4&gt;
  
  
  2. Font Issues in Linux Environments
&lt;/h4&gt;

&lt;p&gt;When running in Docker or Linux containers, missing fonts can cause:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Garbled text.&lt;/li&gt;
&lt;li&gt;Missing characters.&lt;/li&gt;
&lt;li&gt;Layout inconsistencies.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 &lt;strong&gt;Solution:&lt;/strong&gt;&lt;br&gt;
Install necessary fonts in your container, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; fonts-wqy-zenhei
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  3. Limited Debugging Visibility
&lt;/h4&gt;

&lt;p&gt;Since the library is closed-source:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Internal processing is not visible&lt;/li&gt;
&lt;li&gt;Debugging rendering or layout issues can be harder&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 You may need to rely on documentation or vendor support.&lt;/p&gt;

&lt;h3&gt;
  
  
  Shared Pitfall: Security Risks (XXE)
&lt;/h3&gt;

&lt;p&gt;Both approaches can be vulnerable when handling user-uploaded documents.&lt;/p&gt;

&lt;p&gt;A common risk is &lt;strong&gt;XML External Entity (XXE) injection&lt;/strong&gt;, which can lead to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data leakage&lt;/li&gt;
&lt;li&gt;Remote file access&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 &lt;strong&gt;Mitigation strategies:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use secure XML parsing settings (e.g., prohibit DTD processing).&lt;/li&gt;
&lt;li&gt;Validate and sanitize uploaded files.&lt;/li&gt;
&lt;li&gt;Avoid processing untrusted documents directly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Takeaway
&lt;/h3&gt;

&lt;p&gt;Most issues don’t come from “wrong APIs” — they come from assumptions about how Word documents behave internally.&lt;/p&gt;

&lt;p&gt;Understanding these pitfalls early can save hours of debugging and prevent production incidents.&lt;/p&gt;

&lt;p&gt;&lt;a id="7"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Decision Guide: Which One Should You Choose?
&lt;/h2&gt;

&lt;p&gt;At this point, the choice between the Open XML SDK and a third-party library like Spire.Doc should be clearer — but let’s make it more practical.&lt;/p&gt;

&lt;p&gt;Instead of abstract comparison, here’s a simple decision flow based on real-world needs:&lt;/p&gt;

&lt;h3&gt;
  
  
  Quick Decision Flow
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Do you need PDF or other format conversion?&lt;/strong&gt;&lt;br&gt;
→ Yes → Use a third-party library.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Do you have a budget for commercial components?&lt;/strong&gt;&lt;br&gt;
→ No → Use OpenXML (optionally combined with external tools like LibreOffice).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Is your document logic simple (e.g., text replacement, basic structure)?&lt;/strong&gt;&lt;br&gt;
→ Yes → OpenXML is sufficient.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Do you need to handle complex layouts, tables, or styling?&lt;/strong&gt;&lt;br&gt;
→ Yes → Third-party libraries will save significant time.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Are you targeting Linux containers or fully open-source stacks?&lt;/strong&gt;&lt;br&gt;
→ Yes → OpenXML is the safest choice.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Scenario-Based Recommendations
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Recommended Approach&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Internal tools / scripts&lt;/td&gt;
&lt;td&gt;OpenXML SDK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Open-source projects&lt;/td&gt;
&lt;td&gt;OpenXML SDK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Commercial SaaS platforms&lt;/td&gt;
&lt;td&gt;Third-party libraries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Document-heavy workflows&lt;/td&gt;
&lt;td&gt;Third-party libraries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MVP with tight deadlines&lt;/td&gt;
&lt;td&gt;Third-party libraries&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  A Flexible Approach (Best of Both Worlds)
&lt;/h3&gt;

&lt;p&gt;In some projects, you don’t have to commit to a single solution.&lt;/p&gt;

&lt;p&gt;A common strategy is to define an abstraction layer, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IWordProcessor&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ReplaceText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;InsertTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ExportToPdf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;inputPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;outputPath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;👉 Then provide different implementations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;OpenXmlWordProcessor&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SpireWordProcessor&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This allows you to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Start with one approach&lt;/li&gt;
&lt;li&gt;Switch later if requirements change&lt;/li&gt;
&lt;li&gt;Test performance or cost trade-offs&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Final Thoughts on Choosing
&lt;/h3&gt;

&lt;p&gt;There is no universally “better” solution.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;OpenXML SDK&lt;/strong&gt; is ideal when you need control, flexibility, and a free solution.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Third-party libraries&lt;/strong&gt; are better when you need speed, features, and simpler development.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 The real decision comes down to this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Are you optimizing for control, or for productivity?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  9. Conclusion
&lt;/h2&gt;

&lt;p&gt;Processing Word documents in C# can range from simple text manipulation to complex document generation pipelines.&lt;/p&gt;

&lt;p&gt;The Open XML SDK gives you full control over the document structure, but requires a deeper understanding of OOXML and more development effort. Third-party libraries like Spire.Doc simplify most tasks and accelerate delivery, especially for feature-rich scenarios.&lt;/p&gt;

&lt;p&gt;The difference isn’t just about APIs — it’s about how much complexity you’re willing to manage yourself.&lt;/p&gt;

&lt;p&gt;If you're building production systems, choosing the right approach early can save significant time and effort down the line.&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>tutorial</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How to Convert Word to PDF in C# Without Microsoft Office (Fast &amp; Server-Safe)</title>
      <dc:creator>Jen</dc:creator>
      <pubDate>Mon, 16 Mar 2026 09:40:48 +0000</pubDate>
      <link>https://dev.to/jenll/how-to-convert-word-to-pdf-in-c-without-microsoft-office-fast-server-safe-4004</link>
      <guid>https://dev.to/jenll/how-to-convert-word-to-pdf-in-c-without-microsoft-office-fast-server-safe-4004</guid>
      <description>&lt;p&gt;If you've ever tried to convert Word to PDF in C# using &lt;strong&gt;Microsoft Office Interop&lt;/strong&gt;, you probably know the pain.&lt;/p&gt;

&lt;p&gt;COM dependency issues, unstable server-side automation, and the requirement to install the full &lt;strong&gt;Microsoft Office&lt;/strong&gt; suite just to perform document conversion.&lt;/p&gt;

&lt;p&gt;This approach might work on a local machine, but it quickly becomes problematic in production environments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Office automation is &lt;strong&gt;not recommended for server-side applications&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;It introduces &lt;strong&gt;heavy dependencies&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;It often causes &lt;strong&gt;performance and stability issues&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So what's the better way?&lt;/p&gt;

&lt;p&gt;In this guide, we'll show how to convert Word documents to PDF in C# &lt;strong&gt;without installing Microsoft Office&lt;/strong&gt;, using &lt;a href="https://www.e-iceblue.com/Introduce/word-for-net-introduce.html" rel="noopener noreferrer"&gt;&lt;strong&gt;Spire.Doc for .NET&lt;/strong&gt;&lt;/a&gt; - a lightweight .NET library designed for programmatic Word processing.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Spire.Doc for .NET for Word to PDF Conversion?
&lt;/h2&gt;

&lt;p&gt;Before we write any code, let's address the obvious question: &lt;em&gt;why Spire.Doc specifically?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Unlike Office automation, it allows developers to work with Word documents &lt;strong&gt;directly through a managed .NET API&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Here is a quick comparison of common approaches:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Feature&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Spire.Doc&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Word Interop&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;LibreOffice Headless&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Office required&lt;/td&gt;
&lt;td&gt;✅ None&lt;/td&gt;
&lt;td&gt;❌ Full install&lt;/td&gt;
&lt;td&gt;✅ None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Linux / Docker&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes (via CLI)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Conversion fidelity&lt;/td&gt;
&lt;td&gt;✅ High&lt;/td&gt;
&lt;td&gt;✅ High&lt;/td&gt;
&lt;td&gt;⚠️ Variable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Server-side safe&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌ Not recommended&lt;/td&gt;
&lt;td&gt;⚠️ Via subprocess&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Offline / air-gapped&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Latency&lt;/td&gt;
&lt;td&gt;✅ In-process&lt;/td&gt;
&lt;td&gt;🐢 Slow (COM)&lt;/td&gt;
&lt;td&gt;🐢 Process spin-up&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost model&lt;/td&gt;
&lt;td&gt;One-time license&lt;/td&gt;
&lt;td&gt;Office license&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The pattern is pretty clear. Word Interop is the go-to for desktop apps where Office is already present - but it's &lt;a href="https://learn.microsoft.com/en-us/office/troubleshoot/office-developer/consider-using-third-party-component" rel="noopener noreferrer"&gt;explicitly not recommended by Microsoft&lt;/a&gt; for server-side use. LibreOffice headless is a solid free option, but spawning a new process per conversion under load creates its own reliability headaches.&lt;/p&gt;

&lt;p&gt;Spire.Doc hits the sweet spot for server-side automation: it's a single NuGet package, fully managed .NET, no external processes, no Office, no network calls.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8018iz2j3yrzhq23ymcq.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8018iz2j3yrzhq23ymcq.webp" alt="Spire.Doc for .NET" width="800" height="471"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This makes it a reliable solution for &lt;strong&gt;modern .NET applications&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;💡Tip:&lt;/strong&gt; Spire.Doc for .NET offers a free version for evaluation and small projects. A commercial license is required for full features and production use.&lt;/p&gt;

&lt;p&gt;🚀 If your .NET application runs in Linux containers, make sure the libgdiplus package is installed. It provides the GDI+ compatibility layer required for document rendering.&lt;/p&gt;




&lt;h2&gt;
  
  
  Setup: Install Spire.Doc
&lt;/h2&gt;

&lt;p&gt;Installation only takes a few seconds using &lt;a href="https://www.nuget.org/packages/Spire.Doc" rel="noopener noreferrer"&gt;NuGet&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;PM&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Install-Package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Spire.Doc&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once installed, you're ready to start converting Word documents programmatically.&lt;/p&gt;




&lt;h2&gt;
  
  
  Basic Conversion - Word to PDF in C
&lt;/h2&gt;

&lt;p&gt;Let's start with the simplest scenario: converting &lt;strong&gt;a .docx&lt;/strong&gt; file to &lt;strong&gt;PDF&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Spire.Doc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;ConvertWordToPdf&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Program&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Document&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

            &lt;span class="c1"&gt;// Load a Word document&lt;/span&gt;
            &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LoadFromFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"C:\\Users\\Tommy\\Desktop\\Sample.docx"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// Save the document to PDF&lt;/span&gt;
            &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveToFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"C:\\Users\\Tommy\\Desktop\\Sample.pdf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FileFormat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PDF&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// Dispose resources&lt;/span&gt;
            &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Dispose&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it.&lt;/p&gt;

&lt;p&gt;Example output:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4w58u9t6dh0qfecnn8i5.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4w58u9t6dh0qfecnn8i5.webp" alt="Convert Word to PDF in C#" width="800" height="433"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The code performs three steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Load the Word document into memory.&lt;/li&gt;
&lt;li&gt;Render the document layout.&lt;/li&gt;
&lt;li&gt;Export the rendered content as a PDF file.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Advanced Word to PDF Conversion Settings
&lt;/h2&gt;

&lt;p&gt;The default conversion works well, but production systems often need more control over the output. Spire.Doc provides several options that allow developers to tune PDF generation for different scenarios.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stream-based Conversion - for Web API responses
&lt;/h3&gt;

&lt;p&gt;If you're building an API endpoint that returns a PDF download, writing to disk first is wasteful. Spire.Doc can write directly to any &lt;strong&gt;Stream&lt;/strong&gt;, which means you can pipe the output straight to the HTTP response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Spire.Doc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;HttpPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"convert"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;ConvertToPdf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IFormFile&lt;/span&gt; &lt;span class="n"&gt;wordFile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wordFile&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;wordFile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;BadRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"No file uploaded."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;inputStream&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;wordFile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OpenReadStream&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;outputStream&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;MemoryStream&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Load from upload stream directly — no temp file needed&lt;/span&gt;
    &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LoadFromStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inputStream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FileFormat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Docx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveToStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outputStream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FileFormat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PDF&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Rewind before returning&lt;/span&gt;
    &lt;span class="n"&gt;outputStream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Position&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;File&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;outputStream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;//&lt;/span&gt;
        &lt;span class="s"&gt;"application/pdf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetFileNameWithoutExtension&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wordFile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FileName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;".pdf"&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key Points Explanation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;doc.LoadFromStream(inputStream, FileFormat.Docx);&lt;/strong&gt; - Loads Word document directly from upload stream, no temp file needed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;doc.SaveToStream(outputStream, FileFormat.PDF);&lt;/strong&gt; - Writes converted PDF directly to memory stream&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;outputStream.Position = 0;&lt;/strong&gt; - Resets stream position before returning&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;return File(outputStream, "application/pdf", fileName);&lt;/strong&gt; - Streams PDF directly to HTTP response&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;⚠️ Note on &lt;strong&gt;MemoryStream&lt;/strong&gt; and large files: For documents under ~50 MB this pattern works fine. For larger files in high-throughput scenarios, consider writing to a &lt;strong&gt;FileStream&lt;/strong&gt; on a temp path and streaming that back - holding very large documents in &lt;strong&gt;MemoryStream&lt;/strong&gt; will put pressure on the LOH (Large Object Heap).&lt;/p&gt;

&lt;h3&gt;
  
  
  PDF output options - page size, image quality, font embedding
&lt;/h3&gt;

&lt;p&gt;The default output is good, but Spire.Doc exposes a ToPdfParameterList object that gives you control over the most commonly tuned settings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Spire.Doc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Spire.Pdf&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;ConvertWordToPdf&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Program&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Document&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

            &lt;span class="k"&gt;try&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// Load a Word document&lt;/span&gt;
                &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LoadFromFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;@"C:\Users\Tommy\Desktop\Sample.docx"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="c1"&gt;// Create PDF parameter list for output options&lt;/span&gt;
                &lt;span class="n"&gt;ToPdfParameterList&lt;/span&gt; &lt;span class="n"&gt;pdfParams&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ToPdfParameterList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

                &lt;span class="c1"&gt;// 1. Set page size&lt;/span&gt;
                &lt;span class="c1"&gt;// Options: A1, A2, A3, A4, A5, Letter, Legal, etc.&lt;/span&gt;
                &lt;span class="n"&gt;pdfParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PdfPageSize&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PdfPageSize&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;A4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

                &lt;span class="c1"&gt;// 2. Set image quality (0-100, higher = better quality, larger file size)&lt;/span&gt;
                &lt;span class="n"&gt;pdfParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ImageQuality&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 80% quality, good balance&lt;/span&gt;

                &lt;span class="c1"&gt;// 3. Set font embedding&lt;/span&gt;
                &lt;span class="n"&gt;pdfParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsEmbeddedAllFonts&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Embed all fonts for consistent display&lt;/span&gt;
                &lt;span class="c1"&gt;// Alternative: pdfParams.IsEmbeddedFonts = true; // Some versions use this&lt;/span&gt;

                &lt;span class="c1"&gt;// Optional: Set PDF conformance level (for long-term archiving)&lt;/span&gt;
                &lt;span class="c1"&gt;// pdfParams.PdfConformanceLevel = PdfConformanceLevel.Pdf_A1B;&lt;/span&gt;

                &lt;span class="c1"&gt;// Optional: Set compression level&lt;/span&gt;
                &lt;span class="c1"&gt;// pdfParams.CompressionLevel = PdfCompressionLevel.Best;&lt;/span&gt;

                &lt;span class="c1"&gt;// Save the document to PDF with custom parameters&lt;/span&gt;
                &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveToFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;@"C:\Users\Tommy\Desktop\Sample.pdf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pdfParams&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"PDF created successfully with custom options!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Code Explanation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ToPdfParameterList&lt;/strong&gt; - Object that configures PDF output settings like page size, image quality, and font embedding.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PdfPageSize.A4&lt;/strong&gt; - Sets the output page dimensions (A4, Letter, Legal, etc.).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ImageQuality = 80&lt;/strong&gt; - Controls image compression (0-100); 80 balances quality and file size.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IsEmbeddedAllFonts&lt;/strong&gt; = true - Embeds all fonts to ensure cross-platform display consistency.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Batch conversion - process an entire folder
&lt;/h3&gt;

&lt;p&gt;For report generation pipelines, scheduled exports, or migration jobs, you'll typically need to process multiple files. Here's a production-ready pattern with basic error isolation so one bad file doesn't abort the whole batch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Spire.Doc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.IO&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Program&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;sourceDir&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;@"C:\Users\Tommy\Desktop\WordFiles"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;targetDir&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;@"C:\Users\Tommy\Desktop\PDFFiles"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="n"&gt;Directory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateDirectory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;targetDir&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;Directory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetFiles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sourceDir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"*.docx"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Document&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LoadFromFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;pdfFile&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Combine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;targetDir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetFileNameWithoutExtension&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;".pdf"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveToFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pdfFile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FileFormat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PDF&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Dispose&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Convert: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetFileName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Typical use cases include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;document archiving systems.&lt;/li&gt;
&lt;li&gt;automated report generation.&lt;/li&gt;
&lt;li&gt;contract processing pipelines.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Key Points Explanation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Directory.CreateDirectory(targetDir);&lt;/strong&gt; - Ensures output folder exists before saving files.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Directory.GetFiles(sourceDir, "*.docx")&lt;/strong&gt; - Retrieves all Word documents from the source folder for batch processing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Path.Combine(targetDir, ...)&lt;/strong&gt; - Constructs output file path while preserving filename.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Path.GetFileNameWithoutExtension(file) + ".pdf"&lt;/strong&gt; - Generates PDF filename by replacing the original extension.&lt;/li&gt;
&lt;li&gt;Creating a new &lt;strong&gt;Document&lt;/strong&gt; instance per file ensures resources are released and prevents memory buildup during large batch jobs&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Real-World Scenarios: ASP.NET Core Web API
&lt;/h2&gt;

&lt;p&gt;The most common server-side pattern: a client POSTs a &lt;strong&gt;.docx&lt;/strong&gt;, gets a &lt;strong&gt;.pdf&lt;/strong&gt; back in the same response. Here's a complete, production-ready controller:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.AspNetCore.Mvc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Spire.Doc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"api/[controller]"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ApiController&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WordToPdfController&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ControllerBase&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;HttpPost&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IActionResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IFormFile&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;ms&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;MemoryStream&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CopyToAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Position&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LoadFromStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FileFormat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Docx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;pdf&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;MemoryStream&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveToStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FileFormat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PDF&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Position&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;File&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"application/pdf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"output.pdf"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach is commonly used in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;document generation platforms.&lt;/li&gt;
&lt;li&gt;SaaS reporting systems.&lt;/li&gt;
&lt;li&gt;automated invoice processing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Code Explanation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;[Route("api/[controller]")]&lt;/strong&gt; and &lt;strong&gt;[ApiController]&lt;/strong&gt; - Standard ASP.NET Core attributes that define the API endpoint and enable automatic model validation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;await file.CopyToAsync(ms);&lt;/strong&gt; - Asynchronously copies uploaded file to memory stream for processing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ms.Position = 0;&lt;/strong&gt; and &lt;strong&gt;pdf.Position = 0;&lt;/strong&gt; - Resets stream positions to beginning before reading or returning.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;doc.LoadFromStream(ms, FileFormat.Docx);&lt;/strong&gt; - Loads Word document directly from memory stream without saving to disk.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;doc.SaveToStream(pdf, FileFormat.PDF);&lt;/strong&gt; - Converts and writes PDF to memory stream.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;return File(pdf, "application/pdf", "output.pdf");&lt;/strong&gt; - Streams PDF directly back to client as downloadable file.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Common Pitfalls Developers Hit When Converting Word to PDF
&lt;/h2&gt;

&lt;p&gt;When implementing &lt;a href="https://www.e-iceblue.com/Tutorials/Spire.Doc/Spire.Doc-Program-Guide/How-to-Convert-Word-to-PDF.html" rel="noopener noreferrer"&gt;Word-to-PDF conversion in C#&lt;/a&gt; in production, there are a few common issues developers run into.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Missing Fonts on Linux ServersLinux&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If your application runs on Linux, missing fonts can cause layout problems.&lt;/p&gt;

&lt;p&gt;Install common fonts using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;fontconfig
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You may also need to install additional font packages depending on your document templates.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Large Document Memory Usage&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Large documents with high-resolution images can consume significant memory.&lt;/p&gt;

&lt;p&gt;Possible solutions include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;reducing image resolution.&lt;/li&gt;
&lt;li&gt;streaming processing pipelines.&lt;/li&gt;
&lt;li&gt;batch processing instead of single large jobs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Layout Differences&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Some complex Word features (especially embedded objects) may render slightly differently in PDF.&lt;/p&gt;

&lt;p&gt;Testing with real-world documents is always recommended.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wrap-up
&lt;/h2&gt;

&lt;p&gt;If you're building a C# application that needs Word-to-PDF conversion:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Avoid Microsoft Office Interop for server environments.&lt;/li&gt;
&lt;li&gt;Use a dedicated document library like Spire.Doc for .NET.&lt;/li&gt;
&lt;li&gt;Implement stream-based or batch processing for scalable systems.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With just a few lines of code, you can integrate reliable Word-to-PDF conversion into your .NET applications - without installing Microsoft Office.&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>productivity</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
