<?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: Jack9012 </title>
    <description>The latest articles on DEV Community by Jack9012  (@jack_du_64a902eb1614b3933).</description>
    <link>https://dev.to/jack_du_64a902eb1614b3933</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4114752%2F72b3d9cb-c234-4065-824d-7f5c0a1a3fc0.png</url>
      <title>DEV Community: Jack9012 </title>
      <link>https://dev.to/jack_du_64a902eb1614b3933</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jack_du_64a902eb1614b3933"/>
    <language>en</language>
    <item>
      <title>Convert Markdown to PDF in Python: Basic Conversion and Page Setup</title>
      <dc:creator>Jack9012 </dc:creator>
      <pubDate>Thu, 12 Nov 2026 01:30:46 +0000</pubDate>
      <link>https://dev.to/jack_du_64a902eb1614b3933/convert-markdown-to-pdf-in-python-basic-conversion-and-page-setup-3e6b</link>
      <guid>https://dev.to/jack_du_64a902eb1614b3933/convert-markdown-to-pdf-in-python-basic-conversion-and-page-setup-3e6b</guid>
      <description>&lt;p&gt;Markdown is commonly used for technical documentation, project descriptions, and work notes. Its simple markup organizes headings, paragraphs, and lists, making documents easy to edit and manage with version control. For printing, archiving, or external delivery, however, these documents often need to be converted to PDF with fixed pagination and page layouts.&lt;/p&gt;

&lt;p&gt;This article uses Spire.Doc for Python to demonstrate two approaches: converting a Markdown file directly to PDF and customizing the paper size, page orientation, and margins before conversion.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Install the Dependency and Prepare a Sample File
&lt;/h2&gt;

&lt;p&gt;Spire.Doc for Python is a Python library for reading, creating, and converting documents. In this article, it is used to load Markdown files and export them as PDFs.&lt;/p&gt;

&lt;p&gt;Run the following command in your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip install Spire.Doc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a file named &lt;code&gt;Sample.md&lt;/code&gt; in the Python script's working directory. You can use the following content for testing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;# Document Conversion Project: Weekly Report
&lt;/span&gt;
&lt;span class="ge"&gt;&amp;gt; Reporting period: November 2–6, 2026  
&amp;gt; Project lead: Alex Chen  
&amp;gt; Document status: Pending review
&lt;/span&gt;
&lt;span class="gu"&gt;## 1. Project Overview
&lt;/span&gt;
This project converts team-maintained &lt;span class="gs"&gt;**Markdown documents into PDF files**&lt;/span&gt; for printing, archiving, and external delivery.

This week, testing focused on:

&lt;span class="k"&gt;- &lt;/span&gt;Layout of headings, paragraphs, and lists
&lt;span class="k"&gt;- &lt;/span&gt;Display of &lt;span class="gs"&gt;**bold text**&lt;/span&gt; and *italic text*
&lt;span class="k"&gt;- &lt;/span&gt;Conversion of hyperlinks, tables, and code blocks
&lt;span class="k"&gt;- &lt;/span&gt;A4 paper size, page orientation, and margin settings

For Markdown syntax, see the [Markdown Guide](https://www.markdownguide.org/basic-syntax/). Submit generated PDF files to the [Project Document Center](https://example.com/documents).

&lt;span class="gu"&gt;## 2. Weekly Task Progress
&lt;/span&gt;
| Task ID | Task | Owner | Status | Completion |
| --- | --- | --- | --- | --- |
| DOC-101 | Organize the product user guide | Alex Chen | Completed | 100% |
| DOC-102 | Write the conversion script | Emma Li | Completed | 100% |
| DOC-103 | Verify Chinese text layout | Sophie Wang | In progress | 80% |
| DOC-104 | Check tables and code blocks | Ryan Zhao | In progress | 60% |
| DOC-105 | Review delivery documents | Chris Liu | Not started | 0% |

......
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save the Markdown file using UTF-8 encoding. The examples below automatically create an &lt;code&gt;output&lt;/code&gt; directory to store the generated PDFs.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Convert a Markdown File Directly to PDF
&lt;/h2&gt;

&lt;p&gt;If you do not have specific paper size or margin requirements, you can load the Markdown file and save it directly in PDF format.&lt;/p&gt;

&lt;p&gt;Here is the complete code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;spire&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;spire&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;common&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;

&lt;span class="c1"&gt;# Create the output directory&lt;/span&gt;
&lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"output"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mkdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parents&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exist_ok&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Create a document object&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Document&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 Markdown file&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;LoadFromFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Sample.md"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;FileFormat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Markdown&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Save the document as a PDF&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SaveToFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"output/MarkdownToPDF.pdf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;FileFormat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="no"&gt;PDF&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Release document resources&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Dispose&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 conversion primarily involves two methods:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LoadFromFile("Sample.md", FileFormat.Markdown)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Loads the source file in Markdown format&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SaveToFile("output/MarkdownToPDF.pdf", FileFormat.PDF)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Exports the loaded document as a PDF&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The conversion parses the Markdown document structure and generates formatted pages, rather than placing the raw Markdown source into a PDF as plain text. For example, headings, lists, and emphasis markers in the source file are used to format the document.&lt;/p&gt;

&lt;p&gt;After running the script, you can find &lt;code&gt;MarkdownToPDF.pdf&lt;/code&gt; in the &lt;code&gt;output&lt;/code&gt; directory.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Feriuofco3fr2ld83g3b4.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Feriuofco3fr2ld83g3b4.png" alt="Convert Markdown to PDF" width="800" height="771"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Customize Page Settings Before Conversion
&lt;/h2&gt;

&lt;p&gt;Markdown primarily describes content structure. The source file typically does not specify A4 paper, landscape orientation, or print margins. To control these settings, modify the document's &lt;code&gt;PageSetup&lt;/code&gt; after loading the file and before exporting it to PDF.&lt;/p&gt;

&lt;p&gt;The following example uses &lt;strong&gt;A4 paper in landscape orientation with 50-point margins on all four sides&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;spire&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;spire&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;common&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;

&lt;span class="c1"&gt;# Create the output directory&lt;/span&gt;
&lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"output"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mkdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parents&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exist_ok&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Create a document object&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Document&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 Markdown file&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;LoadFromFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Sample.md"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;FileFormat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Markdown&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Get the first section of the document&lt;/span&gt;
&lt;span class="n"&gt;section&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Sections&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_Item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Get the page settings&lt;/span&gt;
&lt;span class="n"&gt;page_setup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;section&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PageSetup&lt;/span&gt;

&lt;span class="c1"&gt;# Set the paper size, page orientation, and margins&lt;/span&gt;
&lt;span class="n"&gt;page_setup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PageSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PageSize&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="no"&gt;A4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;page_setup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Orientation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PageOrientation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Landscape&lt;/span&gt;
&lt;span class="n"&gt;page_setup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Margins&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;All&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;

&lt;span class="c1"&gt;# Save the document as a PDF&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SaveToFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"output/MarkdownToPDFPageSetup.pdf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;FileFormat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="no"&gt;PDF&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Release document resources&lt;/span&gt;
&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Dispose&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;Compared with the basic conversion example, this code adds steps to access a document section and configure its page layout. The settings are explained below:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;Example Value&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PageSize&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PageSize.A4()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Uses A4 paper&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Orientation&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PageOrientation.Landscape&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Uses landscape orientation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Margins.All&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;50&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sets the top, bottom, left, and right margins to the same value&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Margins are measured in points (pt), not millimeters.&lt;/strong&gt; One inch equals 72 points, so 50 points is approximately 17.64 millimeters. To use 20-millimeter margins, convert the value to points:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;page_setup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Margins&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;All&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;72&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;25.4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Page settings affect the available content width and pagination. Landscape orientation provides more horizontal space, making it suitable for documents with wide content. Standard documentation can use portrait orientation according to its printing requirements.&lt;/p&gt;

&lt;p&gt;Note that &lt;code&gt;doc.Sections.get_Item(0)&lt;/code&gt; retrieves the document's first section, not its first page. Page settings apply to the pages within that section. If a document contains multiple sections and requires a consistent layout throughout, configure each section separately.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. What Should You Check After Conversion?
&lt;/h2&gt;

&lt;p&gt;After exporting the PDF, check the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Chinese fonts:&lt;/strong&gt; Ensure that the required Chinese fonts are installed in the runtime environment and that the exported text displays correctly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Image resources:&lt;/strong&gt; If the Markdown file references images, ensure that the resources are accessible and that the corresponding images appear in the output.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wide content:&lt;/strong&gt; Check line wrapping and page boundaries for tables and long lines of code. Adjust the page orientation or margins if necessary.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extended syntax:&lt;/strong&gt; Support for extended syntax can vary between Markdown parsers. For documents containing formulas, embedded HTML, or specialized diagrams, verify the conversion using actual files.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Page settings control the paper and content area, but the layout of individual elements still needs to be checked. Before converting files in bulk, test a representative document to identify potential issues with fonts, resource paths, and pagination.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Summary
&lt;/h2&gt;

&lt;p&gt;The basic workflow for converting Markdown to PDF with Spire.Doc for Python is to create a document object, load the Markdown file, export it as a PDF, and release the resources. To control the print layout, use &lt;code&gt;PageSetup&lt;/code&gt; to configure the paper size, page orientation, and margins before exporting.&lt;/p&gt;

&lt;p&gt;Basic conversion provides a starting point for ordinary notes and documentation. For materials that need to be printed or delivered in a consistent format, add page settings to the same workflow and review the final pagination.&lt;/p&gt;

</description>
      <category>coding</category>
      <category>documentation</category>
      <category>python</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Reorder PDF Pages in C#: Custom Page Order and Full Document Reversal</title>
      <dc:creator>Jack9012 </dc:creator>
      <pubDate>Tue, 10 Nov 2026 01:34:49 +0000</pubDate>
      <link>https://dev.to/jack_du_64a902eb1614b3933/reorder-pdf-pages-in-c-custom-page-order-and-full-document-reversal-28ij</link>
      <guid>https://dev.to/jack_du_64a902eb1614b3933/reorder-pdf-pages-in-c-custom-page-order-and-full-document-reversal-28ij</guid>
      <description>&lt;p&gt;After merging scanned files, you may find that the pages are in the wrong order, or you may want to move pages from the end of a document to the beginning. You can reorder PDF pages by specifying page indices, without first converting the PDF into images.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkafc6zpqs7n9so2vng6t.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkafc6zpqs7n9so2vng6t.png" alt="Reorder PDF Pages" width="800" height="419"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This article uses Spire.PDF for .NET to demonstrate two approaches: arranging pages in a custom order and reversing the page order of an entire document. Both examples use the same method, &lt;code&gt;PdfPageCollection.ReArrange(int[] orderArray)&lt;/code&gt;. The difference lies in how the ordering array is generated.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Install Spire.PDF for .NET
&lt;/h2&gt;

&lt;p&gt;Spire.PDF for .NET is a library for reading, creating, and processing PDF files in .NET applications. This article uses its document loading, page reordering, and file saving APIs.&lt;/p&gt;

&lt;p&gt;Run the following command in the NuGet Package Manager Console in Visual Studio:&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="nc"&gt;Install&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nc"&gt;Package&lt;/span&gt; &lt;span class="nc"&gt;Spire&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="no"&gt;PDF&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alternatively, use the .NET CLI from your project directory:&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.PDF
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The examples below use C# top-level statements. Each example can be placed in the &lt;code&gt;Program.cs&lt;/code&gt; file of a console project that supports this syntax and run separately. If you use a relative path for the input file, place the file in the application's working directory. You can also use an absolute path.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Understand Page Indices and the Ordering Array
&lt;/h2&gt;

&lt;p&gt;Before reordering pages, distinguish between the page positions shown in a PDF reader and the page indices used in code: &lt;strong&gt;indices start at 0, so page 1 corresponds to index 0, page 2 corresponds to index 1, and so on.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ReArrange()&lt;/code&gt; accepts an integer array. Each value represents a page index in the original document, and the sequence of values determines the new page order.&lt;/p&gt;

&lt;p&gt;For example, consider the following array for a four-page 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;new&lt;/span&gt; &lt;span class="kt"&gt;int&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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The mapping is as follows:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Position After Reordering&lt;/th&gt;
&lt;th&gt;Index in the Array&lt;/th&gt;
&lt;th&gt;Page in the Original Document&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Page 1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Original page 2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Page 2&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;Original page 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Page 3&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Original page 3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Page 4&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Original page 4&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This array swaps the first two pages and leaves the remaining two in their original order.&lt;/p&gt;

&lt;p&gt;This article focuses on reordering while retaining all pages. The array should therefore include every page index from the original document exactly once, with values ranging from &lt;code&gt;0&lt;/code&gt; to &lt;code&gt;pdf.Pages.Count - 1&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Reorder PDF Pages in a Custom Sequence
&lt;/h2&gt;

&lt;p&gt;Custom ordering is useful when you already know the desired page sequence, such as when swapping the first two pages or moving a cover page to the beginning of a document.&lt;/p&gt;

&lt;p&gt;The following example uses a PDF containing &lt;strong&gt;exactly four pages&lt;/strong&gt;. It places the original second page before the first, producing this sequence: original page 2, original page 1, original page 3, and original page 4.&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="nc"&gt;Spire&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Pdf&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Create a document object and load the PDF&lt;/span&gt;
&lt;span class="nc"&gt;PdfDocument&lt;/span&gt; &lt;span class="n"&gt;pdf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PdfDocument&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;pdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;LoadFromFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"input.pdf"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Reorder using the original page indices: swap the first two pages&lt;/span&gt;
&lt;span class="n"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Pages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ReArrange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;int&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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&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="c1"&gt;// Save to a new file and close the document&lt;/span&gt;
&lt;span class="n"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SaveToFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"ChangeOrder.pdf"&lt;/span&gt;&lt;span class="p"&gt;)&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="nc"&gt;Close&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 code follows three steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Load the original PDF with &lt;code&gt;LoadFromFile()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Call &lt;code&gt;pdf.Pages.ReArrange()&lt;/code&gt; to reorder the pages according to the original page indices in the array.&lt;/li&gt;
&lt;li&gt;Save the result with &lt;code&gt;SaveToFile()&lt;/code&gt;, then call &lt;code&gt;Close()&lt;/code&gt; to close the document.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To move the last page of this four-page document to the beginning, simply change the array to &lt;code&gt;{ 3, 0, 1, 2 }&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The array in this example is hard-coded for a four-page document. For files with a different number of pages, construct a complete array based on the actual page count instead of using this array directly.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Reverse the Page Order of an Entire PDF
&lt;/h2&gt;

&lt;p&gt;To place the last page first, the second-to-last page second, and so on until the original first page becomes the last, you can automatically generate a reversed index array based on the total page count.&lt;/p&gt;

&lt;p&gt;Compared with entering the array manually, this approach is better suited to documents with varying page counts.&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="nc"&gt;Spire&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Pdf&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Linq&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Create a document object and load the PDF&lt;/span&gt;
&lt;span class="nc"&gt;PdfDocument&lt;/span&gt; &lt;span class="n"&gt;pdf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PdfDocument&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;pdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;LoadFromFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Input.pdf"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Get the total page count&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;pageCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Pages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Generate page indices in reverse order&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;reverseOrder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Enumerable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pageCount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Reverse&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="nc"&gt;ToArray&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="c1"&gt;// Reorder the pages using the reversed indices&lt;/span&gt;
&lt;span class="n"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Pages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ReArrange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reverseOrder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Save as a PDF file and close the document&lt;/span&gt;
&lt;span class="n"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SaveToFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Reversed.pdf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;FileFormat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="no"&gt;PDF&lt;/span&gt;&lt;span class="p"&gt;)&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="nc"&gt;Close&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 reversed array is generated as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Enumerable.Range(0, pageCount)&lt;/code&gt; generates &lt;code&gt;pageCount&lt;/code&gt; consecutive integers starting at 0, covering all page indices.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Reverse()&lt;/code&gt; reverses the index sequence.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ToArray()&lt;/code&gt; converts the result into the integer array required by &lt;code&gt;ReArrange()&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, a five-page document initially produces the indices &lt;code&gt;{ 0, 1, 2, 3, 4 }&lt;/code&gt;. Reversing them gives &lt;code&gt;{ 4, 3, 2, 1, 0 }&lt;/code&gt;, so the output document contains the original pages in the order 5, 4, 3, 2, and 1.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Practical Considerations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Page Order and Printed Page Numbers Are Different
&lt;/h3&gt;

&lt;p&gt;Reordering changes a page's position within the document. It does not rewrite page numbers in headers or footers to match the new positions. For example, if the original page 5 is moved to the beginning, any existing “Page 5” text on that page must be handled separately.&lt;/p&gt;

&lt;p&gt;If the document contains a table of contents, bookmarks, or internal links, check that they point to the expected destinations after saving. A correct page sequence alone does not guarantee that all navigation elements have been updated accordingly.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Match the Custom Array to the Actual Page Count
&lt;/h3&gt;

&lt;p&gt;When reordering while retaining all pages, check the array length, index range, and duplicate values before calling the method. The numbers in the array represent original page indices; do not enter the one-based page numbers shown in a PDF reader directly.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Use Different Input and Output Paths
&lt;/h3&gt;

&lt;p&gt;The examples save the results as new files to make comparison easier. When checking the output, confirm the page count first, then inspect the first page, the last page, and any pages that were moved.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Distinguish Example Code from Production Code
&lt;/h3&gt;

&lt;p&gt;Exception handling is omitted here to keep the core page reordering logic clear. Production applications should also handle invalid input paths, file reading failures, and unwritable output directories. They should ensure that the document is closed and related resources are released even if an exception occurs.&lt;/p&gt;

&lt;p&gt;Also, Spire.PDF for .NET and Free Spire.PDF for .NET are different packages. When using a trial license or the free edition, check the licensing terms and processing limits for your specific version so that these restrictions are not mistaken for problems with the page reordering logic.&lt;/p&gt;

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

&lt;p&gt;The key to reordering PDF pages with Spire.PDF for .NET is to construct an array of original page indices that represents the desired sequence. If the target order is already known, specify the array directly. To reverse an entire document, generate the indices from &lt;code&gt;Pages.Count&lt;/code&gt; and reverse them using LINQ.&lt;/p&gt;

&lt;p&gt;With either approach, remember that indices start at 0, and check both the actual page order and the document's navigation elements after saving.&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>softwaredevelopment</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>PDF Attachments with Python: Embed Files and Extract Them</title>
      <dc:creator>Jack9012 </dc:creator>
      <pubDate>Thu, 05 Nov 2026 01:35:26 +0000</pubDate>
      <link>https://dev.to/jack_du_64a902eb1614b3933/pdf-attachments-with-python-embed-files-and-extract-them-32kb</link>
      <guid>https://dev.to/jack_du_64a902eb1614b3933/pdf-attachments-with-python-embed-files-and-extract-them-32kb</guid>
      <description>&lt;p&gt;A complete project report often includes more than the PDF itself. It may also come with Excel spreadsheets used for calculations, original images, or supplementary notes. When these files are sent separately, recipients have to download and organize them individually—and may overlook a file along the way.&lt;/p&gt;

&lt;p&gt;PDF attachments offer another way to organize these materials: &lt;strong&gt;embed supporting files directly in the PDF so the main document and related resources can be delivered together.&lt;/strong&gt; For example, you can attach a source workbook to a financial report, sample files to a product manual, or supplementary materials to a contract.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fug8t5ks3zmjg43kjld8r.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fug8t5ks3zmjg43kjld8r.png" alt="Add or Extract Attachments in PDF" width="800" height="419"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This article explains how to use Spire.PDF for Python to perform two tasks: add attachments to a PDF and extract attachments from a PDF.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Do PDF Attachments Differ from Page Content?
&lt;/h2&gt;

&lt;p&gt;Think of a PDF with attachments as a document with its own built-in folder: the pages display the main content, while the attachments hold the supporting files.&lt;/p&gt;

&lt;p&gt;Embedding a file this way differs from inserting an image or adding a hyperlink:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;How the file is stored in the PDF&lt;/th&gt;
&lt;th&gt;What readers see&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Insert an image into a page&lt;/td&gt;
&lt;td&gt;The image becomes part of the page content&lt;/td&gt;
&lt;td&gt;The image is visible when the page is opened&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Add a file hyperlink&lt;/td&gt;
&lt;td&gt;Usually, only the destination URL or path is stored&lt;/td&gt;
&lt;td&gt;Clicking the link opens the destination&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Add an attachment&lt;/td&gt;
&lt;td&gt;The file itself is embedded in the PDF&lt;/td&gt;
&lt;td&gt;The file can be viewed or saved using a reader that supports attachments&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Therefore,  &lt;strong&gt;attaching an Excel file to a PDF does not convert its worksheets into PDF pages&lt;/strong&gt; . After extracting the attachment, recipients can still open and edit the original workbook in Excel.&lt;/p&gt;

&lt;p&gt;Attachment support varies between PDF readers. To access attachments, use a desktop reader with an attachments panel, such as Adobe Acrobat. Some browser-based PDF viewers may not provide an option to access embedded files.&lt;/p&gt;

&lt;h2&gt;
  
  
  Environment Setup
&lt;/h2&gt;

&lt;p&gt;Spire.PDF for Python is a library for creating and processing PDF documents. This article uses its attachment APIs to work directly with files stored inside a PDF, without manually opening a reader to add or export them.&lt;/p&gt;

&lt;p&gt;First, install the library:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;Spire.PDF
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Import the required modules in your code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;spire.pdf&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;spire.pdf.common&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The following examples demonstrate how to add and extract attachments. Before running them, replace the file paths with the actual paths on your computer.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Add Attachments to a PDF
&lt;/h2&gt;

&lt;p&gt;Suppose you have a PDF report and want to deliver its source data, &lt;code&gt;Report.xlsx&lt;/code&gt;, and a supporting image, &lt;code&gt;Logo.png&lt;/code&gt;, together. Embedding them as attachments allows recipients to keep the main document and both supporting files in a single PDF.&lt;/p&gt;

&lt;p&gt;The process consists of four steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Load the target PDF.&lt;/li&gt;
&lt;li&gt;Create attachment objects from external files.&lt;/li&gt;
&lt;li&gt;Add the attachments to the document’s attachment collection.&lt;/li&gt;
&lt;li&gt;Save a new PDF containing the attachments.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Code Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;spire.pdf&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;spire.pdf.common&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;

&lt;span class="c1"&gt;# Create a PDF document object
&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PdfDocument&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Load the target PDF
&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;LoadFromFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Input.pdf&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Create attachment objects from external files
&lt;/span&gt;&lt;span class="n"&gt;attachment_one&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PdfAttachment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Report.xlsx&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;attachment_two&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PdfAttachment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Logo.png&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Add the attachments to the PDF
&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;Attachments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attachment_one&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;Attachments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attachment_two&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Save as a new PDF file
&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SaveToFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Attachments.pdf&lt;/span&gt;&lt;span class="sh"&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;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Code Explanation and Checking the Result
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;PdfAttachment("Report.xlsx")&lt;/code&gt; creates an attachment object from the specified file. Calling &lt;code&gt;doc.Attachments.Add()&lt;/code&gt; then adds it to the current PDF. To attach more files, create additional attachment objects and add them to the collection.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Attachments do not take up page space or change the layout of the main content.&lt;/strong&gt; When you open the saved &lt;code&gt;Attachments.pdf&lt;/code&gt;, its pages may look exactly like those in the original document. However, a reader that supports attachments will list the embedded files in its “Attachments” or “Attached Files” panel.&lt;/p&gt;

&lt;p&gt;This example uses different input and output filenames, so the original &lt;code&gt;Input.pdf&lt;/code&gt; is preserved, while the version containing attachments is saved as &lt;code&gt;Attachments.pdf&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;File size is another consideration. Attachments contain the actual file data, so large workbooks or high-resolution images may significantly increase the PDF’s size. If you plan to send the PDF by email or upload it to a system, check the file size limits of that delivery channel.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Extract Attachments from a PDF
&lt;/h2&gt;

&lt;p&gt;After receiving a PDF with attachments, you may need to edit an embedded workbook, organize supporting images, or pass the files to another program. You can extract the attachments as separate files for these purposes.&lt;/p&gt;

&lt;p&gt;Extracting attachments does not require parsing PDF pages or performing OCR. The program reads the document’s attachment collection directly and writes the stored file data to disk.&lt;/p&gt;

&lt;p&gt;The process is as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Load the PDF containing attachments.&lt;/li&gt;
&lt;li&gt;Get the document’s attachment collection.&lt;/li&gt;
&lt;li&gt;Iterate through the attachments and read their filenames and data.&lt;/li&gt;
&lt;li&gt;Save each attachment to the specified directory.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Code Example
&lt;/h3&gt;

&lt;p&gt;Before running the code, create the &lt;code&gt;Output\ExtractedFiles&lt;/code&gt; folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;spire.pdf&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;spire.pdf.common&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;

&lt;span class="c1"&gt;# Create a PdfDocument object
&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PdfDocument&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;# Load a PDF file
&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;LoadFromFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Attachments.pdf&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# Get the attachment collection from the document
&lt;/span&gt;&lt;span class="n"&gt;collection&lt;/span&gt; &lt;span class="o"&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;Attachments&lt;/span&gt;
&lt;span class="c1"&gt;# Loop through the collection
&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;collection&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="c1"&gt;# Get a specific attachment
&lt;/span&gt;        &lt;span class="n"&gt;attachment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_Item&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;# Get the file name and data of the attachment
&lt;/span&gt;        &lt;span class="n"&gt;fileName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;attachment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FileName&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;attachment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Data&lt;/span&gt;
        &lt;span class="c1"&gt;# Save it to a specified folder
&lt;/span&gt;        &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Output&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s"&gt;ExtractedFiles&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;fileName&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="nc"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Code Explanation and Checking the Result
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;doc.Attachments&lt;/code&gt; returns the document’s attachment collection, and &lt;code&gt;collection.Count&lt;/code&gt; indicates how many attachments it contains. If there are no attachments, the example skips the save operation.&lt;/p&gt;

&lt;p&gt;During iteration, &lt;code&gt;get_Item(i)&lt;/code&gt; retrieves an attachment by index, &lt;code&gt;FileName&lt;/code&gt; provides its filename, and &lt;code&gt;Data.Save(path)&lt;/code&gt; writes its data to the specified location.&lt;/p&gt;

&lt;p&gt;For the PDF created in the previous example, the output directory will contain &lt;code&gt;Report.xlsx&lt;/code&gt; and &lt;code&gt;Logo.png&lt;/code&gt; after extraction. You can open these files in their respective applications without accessing them through a PDF reader.&lt;/p&gt;

&lt;h3&gt;
  
  
  Details to Consider When Extracting Attachments
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;First, check the output directory.&lt;/strong&gt; The example assumes that &lt;code&gt;Output\ExtractedFiles&lt;/code&gt; already exists. In an actual project, you can use Python’s &lt;code&gt;os.makedirs()&lt;/code&gt; to create the directory automatically before extraction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Next, handle duplicate filenames.&lt;/strong&gt; Different PDFs may contain attachments named &lt;code&gt;Report.xlsx&lt;/code&gt;. Saving them all to the same folder could cause files to be overwritten. When extracting attachments in batches, create a separate subfolder for each PDF. For attachments with identical names within the same PDF, you can add an index to the beginning of each filename.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Also, sanitize filenames before saving.&lt;/strong&gt; When processing PDFs from external sources, use &lt;code&gt;os.path.basename()&lt;/code&gt; to obtain the filename portion of an attachment’s name, then combine it with the target directory. This avoids using path information from the attachment name directly as the save location.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q1. Why do the PDF pages look the same after adding attachments?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Attachments are stored inside the document and are not part of the content displayed on its pages. Use a reader that supports attachments and check its attachments panel.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q2. Do I still need to send the original files after attaching them?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once the attachments have been saved inside the PDF, recipients can extract them from it. Before delivery, however, open the output PDF and confirm that all the required files have been added.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q3. Is extracting attachments the same as extracting images from a PDF?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No. Extracting attachments retrieves independent files embedded in the PDF. Extracting page images retrieves image resources used by the pages. An image displayed on a PDF page does not necessarily belong to the attachment collection.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q4. Can I process multiple PDFs in batches?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes. Place the processing logic for a single PDF inside a loop that iterates through the input files. For batch extraction, create separate output directories based on the PDF filenames to help track the source of each attachment and reduce filename conflicts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;PDF attachments provide a practical way to keep the main document and its supporting materials together. The pages retain their readable layout, while original workbooks, images, and supplementary files travel with the document.&lt;/p&gt;

&lt;p&gt;With Spire.PDF for Python, you can add attachments using &lt;code&gt;PdfAttachment&lt;/code&gt; and &lt;code&gt;doc.Attachments.Add()&lt;/code&gt;, or iterate through &lt;code&gt;doc.Attachments&lt;/code&gt; to save existing attachments as separate files. In practical use, pay attention to reader compatibility, file size, and the handling of output directories and filenames during extraction.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>python</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Working with PDF Metadata in C#: A Complete Guide to Setting and Extracting Document Properties</title>
      <dc:creator>Jack9012 </dc:creator>
      <pubDate>Tue, 03 Nov 2026 01:19:44 +0000</pubDate>
      <link>https://dev.to/jack_du_64a902eb1614b3933/working-with-pdf-metadata-in-c-a-complete-guide-to-setting-and-extracting-document-properties-179f</link>
      <guid>https://dev.to/jack_du_64a902eb1614b3933/working-with-pdf-metadata-in-c-a-complete-guide-to-setting-and-extracting-document-properties-179f</guid>
      <description>&lt;p&gt;PDF document properties are metadata embedded within a file, including its title, author, subject, keywords, and producer name. Although this information does not appear directly on PDF pages, it is useful for document searches, archive management, and automated workflows. In C# projects, Free Spire.PDF for .NET provides a straightforward way to write and read these properties. This article explains both operations and highlights several implementation details.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsmh6vjxl6u62jzcuwf5i.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsmh6vjxl6u62jzcuwf5i.png" alt="Manage PDF Metadata in C#" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Environment Setup
&lt;/h2&gt;

&lt;p&gt;Free Spire.PDF for .NET is a standalone PDF processing library that runs without Adobe Acrobat or other third-party software. Install it in your project through NuGet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Install-Package FreeSpire.PDF
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that the free edition limits PDF loading and creation to 10 pages; pages beyond this limit are truncated. For lightweight tasks such as setting and reading properties, you can verify the examples using files that stay within this limit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set PDF Document Properties
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;DocumentInformation&lt;/code&gt; property of the &lt;code&gt;PdfDocument&lt;/code&gt; class provides access to standard document properties. It returns a &lt;code&gt;PdfDocumentInformation&lt;/code&gt; object whose fields—including &lt;code&gt;Title&lt;/code&gt;, &lt;code&gt;Author&lt;/code&gt;, &lt;code&gt;Subject&lt;/code&gt;, &lt;code&gt;Keywords&lt;/code&gt;, and &lt;code&gt;Producer&lt;/code&gt;—are readable and writable string properties.&lt;/p&gt;

&lt;p&gt;The following example demonstrates the complete process of loading an existing PDF, updating its properties, and saving it as a new file. The sample property values contain practical information related to project archiving:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using Spire.Pdf;

namespace PdfPropertyDemo
{
    class SetProperties
    {
        static void Main(string[] args)
        {
            PdfDocument pdf = new PdfDocument();
            pdf.LoadFromFile("ProjectReport_Original.pdf");

            pdf.DocumentInformation.Title = "Q2 2026 Project Summary Report";
            pdf.DocumentInformation.Author = "Technical Documentation Team";
            pdf.DocumentInformation.Subject = "Summary of Project Progress and Resource Usage";
            pdf.DocumentInformation.Keywords = "Project Management, Quarterly Report, Progress Tracking";
            pdf.DocumentInformation.Producer = "Internal Document Generation Tool";

            pdf.SaveToFile("ProjectReport_Tagged.pdf");
            pdf.Close();
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After execution, &lt;code&gt;ProjectReport_Tagged.pdf&lt;/code&gt; retains the original document content and carries the updated property information. The &lt;code&gt;Producer&lt;/code&gt; field typically identifies the application that generated the PDF, making it useful for tracing a file’s origin in automated workflows.&lt;/p&gt;

&lt;p&gt;If a property does not exist in the original PDF, reading it may return &lt;code&gt;null&lt;/code&gt; or an empty string. This does not affect writing: you can assign a value directly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Read PDF Document Properties
&lt;/h2&gt;

&lt;p&gt;Reading properties also relies on &lt;code&gt;DocumentInformation&lt;/code&gt;. The following example loads the PDF whose properties were updated, extracts each field, and writes the results to a text file for verification or archiving:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using Spire.Pdf;
using System.IO;
using System.Text;

namespace PdfPropertyDemo
{
    class GetProperties
    {
        static void Main(string[] args)
        {
            PdfDocument pdf = new PdfDocument();
            pdf.LoadFromFile("ProjectReport_Tagged.pdf");

            StringBuilder content = new StringBuilder();
            content.AppendLine("Title: " + pdf.DocumentInformation.Title);
            content.AppendLine("Author: " + pdf.DocumentInformation.Author);
            content.AppendLine("Subject: " + pdf.DocumentInformation.Subject);
            content.AppendLine("Keywords: " + pdf.DocumentInformation.Keywords);
            content.AppendLine("Producer: " + pdf.DocumentInformation.Producer);

            File.WriteAllText("ExtractedProperties.txt", content.ToString(), Encoding.UTF8);
            pdf.Close();
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This version uses &lt;code&gt;AppendLine&lt;/code&gt; instead of explicit &lt;code&gt;\r\n&lt;/code&gt; sequences and explicitly specifies UTF-8 encoding in &lt;code&gt;File.WriteAllText&lt;/code&gt;. Specifying the encoding is particularly useful when property values contain Chinese or other non-ASCII characters: an encoding mismatch between the written file and the application reading it can produce garbled text. &lt;code&gt;AppendLine&lt;/code&gt; also keeps the code concise while placing each field on a separate line.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation Notes
&lt;/h2&gt;

&lt;p&gt;As these examples show, Free Spire.PDF provides a direct interface for working with document properties. The property names largely correspond to entries in the PDF specification’s Info dictionary. Writing and reading use the same property accessors, with no need to parse the underlying structure separately.&lt;/p&gt;

&lt;p&gt;Two details deserve attention in practical use. First, explicitly calling the &lt;code&gt;PdfDocument&lt;/code&gt; object’s &lt;code&gt;Close&lt;/code&gt; method after reading or writing helps release file handles promptly, especially when a subsequent operation needs immediate access to the same file. Second, properties that have not been set may return empty values. Before passing these values to downstream logic, such as conditional checks or database operations, it is sensible to check for null or empty values.&lt;/p&gt;

&lt;p&gt;In addition to standard properties, PDFs can contain custom properties. These can be managed through the &lt;code&gt;SetCustomProperty&lt;/code&gt; and &lt;code&gt;GetCustomProperty&lt;/code&gt; methods and are useful for storing business information such as internal identifiers or version labels. Although custom properties are outside the scope of these examples, they can complement standard metadata in a document management system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This article demonstrated the complete workflow for setting and reading PDF document properties in C# using Free Spire.PDF for .NET. To set properties, assign values directly to the string properties exposed by &lt;code&gt;DocumentInformation&lt;/code&gt;. To read them, retrieve the corresponding values, collect them in a &lt;code&gt;StringBuilder&lt;/code&gt;, and save the results to a text file.&lt;/p&gt;

&lt;p&gt;The key implementation details are to specify the text encoding explicitly, close the document after processing, and handle potentially empty property values before using them in downstream operations. These small steps contribute to reliable behavior in real-world projects.&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Sorting Excel Data with Python: Three Scenarios and Implementations</title>
      <dc:creator>Jack9012 </dc:creator>
      <pubDate>Thu, 29 Oct 2026 01:58:26 +0000</pubDate>
      <link>https://dev.to/jack_du_64a902eb1614b3933/sorting-excel-data-with-python-three-scenarios-and-implementations-1lo</link>
      <guid>https://dev.to/jack_du_64a902eb1614b3933/sorting-excel-data-with-python-three-scenarios-and-implementations-1lo</guid>
      <description>&lt;p&gt;Sorting is one of the most common operations in Excel data processing. Manual sorting is perfectly adequate for small datasets and occasional tasks. However, when data changes frequently, multiple files need batch processing, or records must follow custom business rules, automating the process with code can produce more consistent and reproducible results.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Froclokxn5vazqdwz9ctk.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Froclokxn5vazqdwz9ctk.png" alt="Sort Excel Data with Python" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This article uses Spire.XLS for Python. We will first explain a shared preprocessing step, then demonstrate three sorting scenarios: sorting a single column, sorting an entire table by a specific column, and sorting by a custom sequence.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Environment Setup and a Shared Prerequisite
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Install and Import the Library
&lt;/h3&gt;

&lt;p&gt;Install Spire.XLS for Python using pip:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install Spire.XLS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once installed, add the following import statement at the beginning of your code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from spire.xls import *
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Convert Formulas to Static Values Before Sorting
&lt;/h3&gt;

&lt;p&gt;The first two examples share the same preprocessing step: iterate through the sorting range and replace formulas with their calculated values. This lets the sorting operation work with static cell values and avoids potential changes to formula references when cells move.&lt;/p&gt;

&lt;p&gt;The following code performs this conversion. We explain it here once rather than repeating the explanation in each example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for cell in cellRange:
    if cell.HasFormula:
        value = cell.FormulaValue
        cell.Clear(ExcelClearOptions.ClearContent)
        cell.Value = value
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also, the column index used by &lt;code&gt;SortColumns.Add()&lt;/code&gt; is zero-based: column A corresponds to 0, column B corresponds to 1, and so on. Check the index against your worksheet structure when configuring the sort.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Three Sorting Scenarios
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Scenario 1: Sort a Single Column
&lt;/h3&gt;

&lt;p&gt;This approach rearranges the values within one column only. Other columns remain unchanged.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from spire.xls import *

workbook = Workbook()
workbook.LoadFromFile("Input.xlsx")

worksheet = workbook.Worksheets[0]
cellRange = worksheet["G1:G13"]

# Convert formulas to static values (as explained in Section 1)
for cell in cellRange:
    if (cell.HasFormula):
        value = cell.FormulaValue
        cell.Clear(ExcelClearOptions.ClearContent)
        cell.Value = value

# Sort by column G (zero-based index 6), by value in descending order
workbook.DataSorter.SortColumns.Add(6, SortComparsionType.Values, OrderBy.Descending)
workbook.DataSorter.Sort(cellRange)

workbook.SaveToFile("output/SortByColumn.xlsx", ExcelVersion.Version2016)
workbook.Dispose()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key points:&lt;/strong&gt; The sorting range is &lt;code&gt;G1:G13&lt;/code&gt;, which includes only the target column. The value 6 in &lt;code&gt;SortColumns.Add(6, ...)&lt;/code&gt; is the zero-based index for column G. Values are sorted in descending order.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scenario 2: Sort an Entire Table by a Specific Column
&lt;/h3&gt;

&lt;p&gt;Use one column as the sorting key to rearrange entire rows while preserving the relationships between values within each row.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from spire.xls import *

workbook = Workbook()
workbook.LoadFromFile("Input.xlsx")

worksheet = workbook.Worksheets[0]
cellRange = worksheet["A1:G13"]

# Convert formulas to static values (as explained in Section 1)
for cell in cellRange:
    if (cell.HasFormula):
        value = cell.FormulaValue
        cell.Clear(ExcelClearOptions.ClearContent)
        cell.Value = value

# Use column G (zero-based index 6) as the key, sorting by value in descending order
workbook.DataSorter.SortColumns.Add(6, SortComparsionType.Values, OrderBy.Descending)
workbook.DataSorter.Sort(cellRange)

workbook.SaveToFile("output/SortByColumns.xlsx", ExcelVersion.Version2016)
workbook.Dispose()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key points:&lt;/strong&gt; Compared with Scenario 1, the sorting key remains the same. Only the sorting range changes—from &lt;code&gt;G1:G13&lt;/code&gt; to &lt;code&gt;A1:G13&lt;/code&gt;. When &lt;code&gt;Sort()&lt;/code&gt; runs, each row within the range moves according to its value in column G. The contents of columns A through F move with the row.&lt;/p&gt;

&lt;p&gt;This approach is more suitable for typical business data. For example, when sorting a sales table by the Sales Amount column, the product, region, date, and other information in each row must move together to keep the records aligned.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scenario 3: Sort by a Custom Sequence
&lt;/h3&gt;

&lt;p&gt;Ascending and descending order cover only some sorting requirements. Sometimes, records need to follow a business-defined sequence, such as High, Medium, and Low priority, or a predefined order of identifiers. A custom sort list handles these cases.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from spire.xls import *

workbook = Workbook()
workbook.LoadFromFile("Input.xlsx")

worksheet = workbook.Worksheets[0]
cellRange = worksheet["A1:G13"]

# Explicitly define the sorting order
customList = ["6", "5", "4", "3", "2", "1", "12", "11", "10", "9", "8", "7"]

# Sort by column A (zero-based index 0) using the custom sequence
workbook.DataSorter.SortColumns.Add(0, customList)
workbook.DataSorter.Sort(cellRange)

workbook.SaveToFile("output/CustomSortList.xlsx", ExcelVersion.Version2016)
workbook.Dispose()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key points:&lt;/strong&gt; Values in the sorting column follow their order in &lt;code&gt;customList&lt;/code&gt; rather than their numerical magnitude. The &lt;code&gt;Sort()&lt;/code&gt; operation still applies to the entire &lt;code&gt;A1:G13&lt;/code&gt; range, with the rows arranged according to the custom sequence of values in column A.&lt;/p&gt;

&lt;p&gt;Keep two points in mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The element types in the custom list should match the data type of the sorting column. In this example, the numbers are provided as strings. Otherwise, values may not match correctly.&lt;/li&gt;
&lt;li&gt;If the sorting column contains values that are absent from the custom list, verify their placement in the actual output. Check that the data and custom list correspond before running the final sort.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. Comparing the Three Scenarios
&lt;/h2&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;Sorting Range&lt;/th&gt;
&lt;th&gt;Sorting Rule&lt;/th&gt;
&lt;th&gt;Suitable Use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Single-column sorting&lt;/td&gt;
&lt;td&gt;Target column only&lt;/td&gt;
&lt;td&gt;Values in ascending or descending order&lt;/td&gt;
&lt;td&gt;Rearrange values within one column&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Entire-table sorting&lt;/td&gt;
&lt;td&gt;Entire table, using a specific column as the key&lt;/td&gt;
&lt;td&gt;Values in the key column in ascending or descending order&lt;/td&gt;
&lt;td&gt;Sort complete rows by a key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Custom-sequence sorting&lt;/td&gt;
&lt;td&gt;Entire table&lt;/td&gt;
&lt;td&gt;Order defined in a custom list&lt;/td&gt;
&lt;td&gt;Follow a predefined business sequence&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The central distinction is the  &lt;strong&gt;sorting range&lt;/strong&gt; : it determines which cells move, while the sorting key determines their order. The code for single-column sorting and entire-table sorting is almost identical; only the range passed to &lt;code&gt;Sort()&lt;/code&gt; changes. Custom-sequence sorting replaces value comparison with the order defined in a list.&lt;/p&gt;

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

&lt;p&gt;When sorting with Spire.XLS for Python, keep three points in mind:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Handle formulas first:&lt;/strong&gt; If you need static values for sorting, convert formulas to their calculated values beforehand to avoid potential reference changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Choose the correct sorting range:&lt;/strong&gt; Pass a single-column range to rearrange that column alone, or a full-table range to move complete rows together.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Remember that column indexes start at 0:&lt;/strong&gt; Check the column index against your worksheet structure when configuring &lt;code&gt;SortColumns.Add()&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Apply these points to your data, then use the appropriate example above to handle common Excel sorting tasks.&lt;/p&gt;

</description>
      <category>automation</category>
      <category>python</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Import Word Table Data into Excel Using Python</title>
      <dc:creator>Jack9012 </dc:creator>
      <pubDate>Tue, 27 Oct 2026 05:44:36 +0000</pubDate>
      <link>https://dev.to/jack_du_64a902eb1614b3933/import-word-table-data-into-excel-using-python-3167</link>
      <guid>https://dev.to/jack_du_64a902eb1614b3933/import-word-table-data-into-excel-using-python-3167</guid>
      <description>&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa8200qji9uvu915p089y.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa8200qji9uvu915p089y.png" alt="Import Word Table Data into Excel" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tables in Word documents often contain structured data, but Excel is better suited for analyzing, filtering, and charting that data. When a document contains multiple tables, copying and pasting them manually can be time-consuming and may introduce errors during conversion.&lt;/p&gt;

&lt;p&gt;This article explains how to use Spire.Doc for Python and Spire.XLS for Python to import each table from a Word document into a separate Excel worksheet. It also describes what the solution can handle and where its limitations lie.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install the Required Libraries
&lt;/h2&gt;

&lt;p&gt;Before you begin, install two libraries: &lt;code&gt;Spire.Doc&lt;/code&gt; reads and parses the table structure in Word documents, while &lt;code&gt;Spire.XLS&lt;/code&gt; creates and writes Excel files. Run the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install Spire.Doc
pip install Spire.XLS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Spire products also offer free editions, such as &lt;code&gt;spire.doc.free&lt;/code&gt; and &lt;code&gt;spire.xls.free&lt;/code&gt;, which you can install with &lt;code&gt;pip install spire.doc.free spire.xls.free&lt;/code&gt;. The free editions have certain limitations. For example, Free Spire.Doc supports up to 500 paragraphs and 25 tables per document. The free editions may be sufficient for smaller documents, while larger documents or those containing more tables require the full versions.&lt;/p&gt;

&lt;p&gt;Both libraries support Windows, Linux, and macOS, and neither requires Microsoft Office or Word to be installed.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the Conversion Works
&lt;/h2&gt;

&lt;p&gt;The conversion process can be divided into two stages:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Extraction:&lt;/strong&gt; Load the Word document and iterate through each section, then through each table in that section. For every cell, read and combine the text from all its paragraphs to retain content that spans multiple paragraphs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Writing:&lt;/strong&gt; Create a new Excel workbook and a separate worksheet for each Word table. Write the extracted text to the corresponding cells in row and column order, automatically adjust the column widths, and save the file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Complete Code Example
&lt;/h2&gt;

&lt;p&gt;The following code implements this process. It is written for the full versions of the Spire libraries. If you use the free editions, adjust the import statements to &lt;code&gt;from spire.doc import *&lt;/code&gt; and &lt;code&gt;from spire.xls import *&lt;/code&gt;, respectively.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;spire.doc&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;spire.xls&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;

&lt;span class="c1"&gt;# Load the Word document
&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&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="nc"&gt;LoadFromFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;input.docx&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Create an Excel workbook and remove the default worksheets
&lt;/span&gt;&lt;span class="n"&gt;wb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Workbook&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;wb&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="nc"&gt;Clear&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Iterate through all sections in the document
&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&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;Sections&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;section&lt;/span&gt; &lt;span class="o"&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;Sections&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_Item&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;# Iterate through all tables in the section
&lt;/span&gt;    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;section&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tables&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;table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;section&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tables&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_Item&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="c1"&gt;# Create a worksheet for each table, using the section and table numbers
&lt;/span&gt;        &lt;span class="n"&gt;ws&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;wb&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="nc"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Table_&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;_&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Write data one row and one cell at a time
&lt;/span&gt;        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;table&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;Count&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;tableRow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;table&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="nf"&gt;get_Item&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="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;cell&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tableRow&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;Count&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;tableCell&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tableRow&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="nf"&gt;get_Item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

                &lt;span class="c1"&gt;# Combine the text of all paragraphs in the cell
&lt;/span&gt;                &lt;span class="n"&gt;cellText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;''&lt;/span&gt;
                &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;paragraph&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tableCell&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Paragraphs&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;paragraph&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tableCell&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Paragraphs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_Item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;paragraph&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="n"&gt;cellText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cellText&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;paragraph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

                &lt;span class="c1"&gt;# Assign text starting with "00" directly to prevent
&lt;/span&gt;                &lt;span class="c1"&gt;# Excel from automatically converting it to a number
&lt;/span&gt;                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;cellText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;00&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                    &lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Range&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cell&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cellText&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;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SetCellValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cell&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cellText&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Automatically adjust column widths to fit the content
&lt;/span&gt;        &lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AllocatedRange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AutoFitColumns&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Save in Excel 2016 format
&lt;/span&gt;&lt;span class="n"&gt;wb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SaveToFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;output/WordTableToExcel.xlsx&lt;/span&gt;&lt;span class="sh"&gt;'&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;Version2016&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="nc"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;wb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Dispose&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before running the script, make sure &lt;code&gt;input.docx&lt;/code&gt; is in the same directory as the script and that the &lt;code&gt;output&lt;/code&gt; folder already exists. You can also modify the paths to suit your environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Details in the Code
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Combining multiple paragraphs in a cell.&lt;/strong&gt; A Word table cell can contain multiple paragraphs. Reading only the first paragraph would omit the rest. The code iterates through &lt;code&gt;tableCell.Paragraphs&lt;/code&gt;, combines their text in order, and adds a space after each paragraph to separate the content.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Protecting text that starts with &lt;code&gt;"00"&lt;/code&gt;.&lt;/strong&gt; When a string is written with &lt;code&gt;SetCellValue&lt;/code&gt;, Excel may recognize a value such as &lt;code&gt;"001"&lt;/code&gt; as the number &lt;code&gt;1&lt;/code&gt;. The code checks for this case and assigns the value through &lt;code&gt;ws.Range[row + 1, cell + 1].Text = cellText&lt;/code&gt; instead, which is intended to preserve it as text. This protection only covers values beginning with &lt;code&gt;"00"&lt;/code&gt;; other formats that may be misinterpreted, such as long numeric strings or date-like text, are not handled.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Naming worksheets.&lt;/strong&gt; The format &lt;code&gt;Table_{section number}_{table number}&lt;/code&gt; makes it easier to identify the location of each source table in the Word document.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cases Not Covered
&lt;/h2&gt;

&lt;p&gt;The current code handles basic two-dimensional text tables. It does not correctly handle the following common cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Merged cells.&lt;/strong&gt; When iterating through a Word table with merged cells, &lt;code&gt;tableRow.Cells.Count&lt;/code&gt; and the cell indices may not correspond to the visual grid. The code traverses the &lt;code&gt;Cells&lt;/code&gt; collection in order and does not read properties such as &lt;code&gt;GridSpan&lt;/code&gt; or &lt;code&gt;VerticalMerge&lt;/code&gt;. As a result, merged regions may be split in Excel, and text may be duplicated or placed in the wrong position. Handling this requires reading the merge properties and applying corresponding &lt;code&gt;Merge&lt;/code&gt; operations in Excel.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Images inside cells.&lt;/strong&gt; The code extracts paragraph text only. Images and other non-text objects embedded in cells are ignored and will not appear in the Excel output.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Text formatting.&lt;/strong&gt; Only plain text strings are written to Excel. Font names, font sizes, bold formatting, colors, alignment, and other formatting from Word cells are not transferred. Paragraph breaks are also lost because the code joins paragraphs with spaces. The Excel cells contain the text values without the original visual styling.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context outside tables.&lt;/strong&gt; Table captions, explanatory paragraphs, and cross-references outside the tables are not included in the Excel output. Each table is placed on a separate worksheet, and the numbering in the worksheet names provides the only indication of their positions in the source document.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When to Use This Approach
&lt;/h2&gt;

&lt;p&gt;This code is suitable for consistently structured Word tables with no merged cells, plain-text cell contents, and no requirement to preserve formatting. Under these conditions, it can quickly export multiple tables and save the time needed to copy them individually.&lt;/p&gt;

&lt;p&gt;If the source tables contain the more complex elements described above, you will need to add detection and handling logic to the code. Otherwise, the exported data may differ from the original tables.&lt;/p&gt;

</description>
      <category>automation</category>
      <category>python</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Add Page Numbers to PDF Footers Using Python</title>
      <dc:creator>Jack9012 </dc:creator>
      <pubDate>Thu, 22 Oct 2026 01:29:16 +0000</pubDate>
      <link>https://dev.to/jack_du_64a902eb1614b3933/add-page-numbers-to-pdf-footers-using-python-12bg</link>
      <guid>https://dev.to/jack_du_64a902eb1614b3933/add-page-numbers-to-pdf-footers-using-python-12bg</guid>
      <description>&lt;p&gt;When processing PDF documents in batches, you often need to add consistent page numbering to existing files. With Spire.PDF for Python, you can draw dynamic page numbers directly on the canvas of an existing PDF, without creating a separate PDF and merging it afterward.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2i69w3qmx2159yc07i3u.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2i69w3qmx2159yc07i3u.png" alt="Add Page Numbers to PDF"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This article demonstrates how to center page numbers in a PDF footer, with a focus on  &lt;strong&gt;customizing the page number format&lt;/strong&gt; —such as &lt;code&gt;Page X&lt;/code&gt; or &lt;code&gt;X / Y&lt;/code&gt;—and using coordinates to position them anywhere on the page, including the left, center, or right of the footer, or any location in the header.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Coordinate units: PDF coordinates are measured in points, where 1 point = 1/72 inch. The page coordinate system starts at the upper-left corner, with X increasing to the right and Y increasing downward.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Install the required library:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;spire.pdf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Understanding the Two Dynamic Fields
&lt;/h2&gt;

&lt;p&gt;Page numbering relies on two dynamic fields:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PdfPageNumberField&lt;/strong&gt; : Represents the current page number and resolves to the actual number when rendered.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PdfPageCountField&lt;/strong&gt; : Represents the total number of pages and resolves to the page count when rendered.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These fields are dynamic rather than hardcoded strings, allowing page numbering to reflect the document’s page count when rendered.&lt;/p&gt;

&lt;h2&gt;
  
  
  Control the Format with PdfCompositeField
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;PdfCompositeField&lt;/code&gt; combines a static text template with dynamic fields to produce a complete line of text.  &lt;strong&gt;The template string determines the page number format&lt;/strong&gt; , so changing the format only requires updating this string.&lt;/p&gt;

&lt;p&gt;Basic syntax:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;compositeField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PdfCompositeField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;font&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;brush&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Template text&lt;/span&gt;&lt;span class="sh"&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;field1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field2&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;Here, &lt;code&gt;{0}&lt;/code&gt; refers to the first field in the list, &lt;code&gt;{1}&lt;/code&gt; refers to the second field, and so on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Complete Code: Centered Footer with “Page X of Y”
&lt;/h2&gt;

&lt;p&gt;The following example uses the &lt;code&gt;Page {0} of {1}&lt;/code&gt; format, centers the page numbers in the footer, and adds a separator line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;spire.pdf.common&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;spire.pdf&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;

&lt;span class="c1"&gt;# Create a PdfDocument object
&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PdfDocument&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;# Load a local PDF file
&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;LoadFromFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input.pdf&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Define the font, brush, and pen
&lt;/span&gt;&lt;span class="n"&gt;font&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PdfTrueTypeFont&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Arial&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;14.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PdfFontStyle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Regular&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;brush&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PdfBrushes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_Black&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;pen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PdfPen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;brush&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Set margins
&lt;/span&gt;&lt;span class="n"&gt;leftMargin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;54.0&lt;/span&gt;
&lt;span class="n"&gt;rightMargin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;54.0&lt;/span&gt;
&lt;span class="n"&gt;bottomMargin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;72.0&lt;/span&gt;

&lt;span class="c1"&gt;# Create fields for the current page number and total page count
&lt;/span&gt;&lt;span class="n"&gt;pageNumberField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PdfPageNumberField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;pageCountField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PdfPageCountField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;# Combine the fields using an English page number template
&lt;/span&gt;&lt;span class="n"&gt;compositeField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PdfCompositeField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;font&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;brush&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Page {0} of {1}&lt;/span&gt;&lt;span class="sh"&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;pageNumberField&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pageCountField&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Iterate through all pages
&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&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;Pages&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;page&lt;/span&gt; &lt;span class="o"&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;Pages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_Item&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;pageSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Size&lt;/span&gt;

    &lt;span class="c1"&gt;# Draw a separator line in the footer
&lt;/span&gt;    &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DrawLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;pen&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="n"&gt;pageSize&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Height&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;bottomMargin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;15.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;pageSize&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Width&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;rightMargin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;pageSize&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Height&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;bottomMargin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;15.0&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Measure the page number text to calculate its centered position
&lt;/span&gt;    &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Page {} of {}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&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;Pages&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;pageNumberSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;font&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;MeasureString&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="c1"&gt;# Set the page number position: horizontally centered
&lt;/span&gt;    &lt;span class="n"&gt;compositeField&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PointF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pageSize&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Width&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;pageNumberSize&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Width&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;pageSize&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Height&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;bottomMargin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;18.0&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Draw the page number
&lt;/span&gt;    &lt;span class="n"&gt;compositeField&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Draw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Canvas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Save the new file
&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SaveToFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Output/CenterAlignedPageNumbers.pdf&lt;/span&gt;&lt;span class="sh"&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;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Dispose&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Customize the Page Number Format
&lt;/h2&gt;

&lt;p&gt;The page number format depends on the template string and the dynamic fields supplied. Below are several common formats. Replace the third argument of &lt;code&gt;PdfCompositeField&lt;/code&gt; and adjust the field list as needed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Format 1: Current Page Only — &lt;code&gt;Page X&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This format requires only the page number field:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;compositeField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PdfCompositeField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;    
    &lt;span class="n"&gt;font&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;brush&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Page {0}&lt;/span&gt;&lt;span class="sh"&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;pageNumberField&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Format 2: Compact Numeric Format — &lt;code&gt;X / Y&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;compositeField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PdfCompositeField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;    
    &lt;span class="n"&gt;font&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;brush&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{0} / {1}&lt;/span&gt;&lt;span class="sh"&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;pageNumberField&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pageCountField&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Format 3: Current Page and Total Pages — &lt;code&gt;Page X / Total Y Pages&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;compositeField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PdfCompositeField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;    
    &lt;span class="n"&gt;font&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;brush&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Page {0} / Total {1} Pages&lt;/span&gt;&lt;span class="sh"&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;pageNumberField&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pageCountField&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Format 4: Total Page Count Only — &lt;code&gt;Total Y Pages&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;compositeField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PdfCompositeField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;    
    &lt;span class="n"&gt;font&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;brush&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Total {1} Pages&lt;/span&gt;&lt;span class="sh"&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;pageNumberField&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pageCountField&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note: A field can be included in the list without appearing in the template; it simply will not be displayed. You can combine field placeholders in any order and customize surrounding text or separators, such as “Page,” “of,” “Total,” or “/,” to suit your layout.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Customize the Position with Coordinates
&lt;/h2&gt;

&lt;p&gt;The page number position is controlled by &lt;code&gt;compositeField.Location = PointF(x, y)&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Center: &lt;code&gt;x = (page width - text width) / 2&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Left: &lt;code&gt;x = leftMargin&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Right: &lt;code&gt;x = pageSize.Width - pageNumberSize.Width - rightMargin&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Vertical position: Adjust Y to move the text up or down. Smaller Y values move it toward the top of the page, allowing placement in the header.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The following example combines a custom format and position: a right-aligned footer using &lt;code&gt;X / Y&lt;/code&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="n"&gt;compositeField&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;PdfCompositeField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;font&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;brush&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"{0} / {1}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pageNumberField&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pageCountField&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="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&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;Pages&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;page&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;Pages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_Item&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;pageSize&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Size&lt;/span&gt;

    &lt;span class="n"&gt;text&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="nf"&gt;format&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;1&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;Pages&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;pageNumberSize&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;font&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MeasureString&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="err"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Right&lt;/span&gt; &lt;span class="n"&gt;alignment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;X&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="n"&gt;margin&lt;/span&gt;
    &lt;span class="n"&gt;compositeField&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Location&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;PointF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;pageSize&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Width&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;pageNumberSize&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Width&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="n"&gt;pageSize&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Height&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;18.0&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;compositeField&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Draw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Canvas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Things to Keep in Mind
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The example uses Arial. If it is unavailable in your environment, replace it with another installed font. For Chinese or other multilingual text, choose a font that supports the required characters.&lt;/li&gt;
&lt;li&gt;If page numbers overlap existing content, adjust their coordinates to place them in an unused area. The margin variables in these examples control drawing positions; they do not create additional blank space or reflow existing content.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This approach lets you customize the page number format and its position independently. Change the template string to switch between formats such as &lt;code&gt;Page X&lt;/code&gt;, &lt;code&gt;X / Y&lt;/code&gt;, and &lt;code&gt;Page X of Y&lt;/code&gt;, then adjust the &lt;code&gt;Location&lt;/code&gt; coordinates to place the text anywhere in the footer or header. This flexibility makes it useful for applying consistent page numbering when processing PDFs in batches.&lt;/p&gt;

</description>
      <category>automation</category>
      <category>python</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Convert HTML to PDF in C#: A Chrome-Based Approach</title>
      <dc:creator>Jack9012 </dc:creator>
      <pubDate>Tue, 20 Oct 2026 01:25:44 +0000</pubDate>
      <link>https://dev.to/jack_du_64a902eb1614b3933/convert-html-to-pdf-in-c-a-chrome-based-approach-282p</link>
      <guid>https://dev.to/jack_du_64a902eb1614b3933/convert-html-to-pdf-in-c-a-chrome-based-approach-282p</guid>
      <description>&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwmong3vs4hrjc6nzeu3k.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwmong3vs4hrjc6nzeu3k.png" alt="Convert HTML to PDF" width="799" height="420"&gt;&lt;/a&gt;&lt;br&gt;
Converting HTML or web pages to PDF is a common requirement in business applications, from exporting reports and printing invoices to archiving web pages and generating PDFs from dynamic HTML content.&lt;/p&gt;

&lt;p&gt;Common approaches include:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;HTML rendering libraries that work without a browser but offer limited support for complex CSS and JavaScript.&lt;/li&gt;
&lt;li&gt;Headless browser solutions such as PhantomJS or Puppeteer, which require deploying a browser engine.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rendering with a locally installed Chrome browser&lt;/strong&gt; , which reuses Google Chrome on the target machine and uses its native rendering engine to handle modern CSS, JavaScript, images, fonts, and complex page layouts.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This article introduces the &lt;code&gt;Spire.Additions.Chrome&lt;/code&gt; module included with  &lt;strong&gt;Spire.PDF for Python&lt;/strong&gt; . It wraps the process of exporting PDFs through a local Chrome installation, allowing you to convert a web page URL to PDF with just a few lines of C# code.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Important prerequisites&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install &lt;strong&gt;Google Chrome&lt;/strong&gt; on the machine that will run the code, and locate the &lt;code&gt;chrome.exe&lt;/code&gt; executable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Close all Chrome windows before running the application&lt;/strong&gt; ; otherwise, the conversion will fail.&lt;/li&gt;
&lt;li&gt;Both web page URLs and local HTML files are supported.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Environment Setup
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Install Spire.PDF for Python
&lt;/h3&gt;

&lt;p&gt;The project requires Spire.PDF for Python. The &lt;code&gt;Spire.Additions.Chrome&lt;/code&gt; namespace is included with the product, so no separate component needs to be installed.&lt;/p&gt;
&lt;h3&gt;
  
  
  Locate the Chrome Executable
&lt;/h3&gt;

&lt;p&gt;The default Chrome installation path on Windows is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;C:\Program Files\Google\Chrome\Application\chrome.exe
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If Chrome is installed in a different directory, update the &lt;code&gt;chromeLocation&lt;/code&gt; value in the code accordingly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Complete C# Code Example
&lt;/h2&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.Additions.Chrome&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;ConvertHtmlToPdfUsingChrome&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;internal&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="c1"&gt;// Input web page URL&lt;/span&gt;
            &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;inputUrl&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;@"https://www.example.com/webpage.html"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="c1"&gt;// Output PDF file path&lt;/span&gt;
            &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;outputFile&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;@"HtmlToPDF.pdf"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="c1"&gt;// Path to the local Chrome executable&lt;/span&gt;
            &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;chromeLocation&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;@"C:\Program Files\Google\Chrome\Application\chrome.exe"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="c1"&gt;// Create a Chrome HTML converter instance&lt;/span&gt;
            &lt;span class="n"&gt;ChromeHtmlConverter&lt;/span&gt; &lt;span class="n"&gt;converter&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;ChromeHtmlConverter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chromeLocation&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// Configure conversion options&lt;/span&gt;
            &lt;span class="n"&gt;ConvertOptions&lt;/span&gt; &lt;span class="n"&gt;options&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;ConvertOptions&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

            &lt;span class="c1"&gt;// Set the conversion timeout in milliseconds&lt;/span&gt;
            &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Timeout&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="m"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="c1"&gt;// Set the PDF page size and margins:&lt;/span&gt;
            &lt;span class="c1"&gt;// A4 paper in inches, with no margins&lt;/span&gt;
            &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PageSettings&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;PageSettings&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;PaperWidth&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;8.27&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;PaperHeight&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;11.69&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;MarginTop&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;MarginLeft&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;MarginRight&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;MarginBottom&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;// Convert the web page URL to PDF&lt;/span&gt;
            &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="p"&gt;.&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;inputUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;outputFile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Understanding the Code
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. ChromeHtmlConverter
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;ChromeHtmlConverter&lt;/code&gt; is the main conversion class in the &lt;code&gt;Spire.Additions.Chrome&lt;/code&gt; namespace, which is  &lt;strong&gt;included with Spire.PDF for Python&lt;/strong&gt; .&lt;/p&gt;

&lt;p&gt;Its constructor accepts the path to &lt;code&gt;chrome.exe&lt;/code&gt;, allowing the converter to launch the local Chrome browser in headless mode and render the page.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Chrome must be closed when you run the code. The library automatically launches a headless Chrome process and closes it when the conversion finishes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  2. ConvertOptions
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;ConvertOptions&lt;/code&gt; controls the conversion settings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Timeout&lt;/code&gt;: The conversion timeout in milliseconds. Increase this value for pages that take longer to load. In this example, &lt;code&gt;10 * 3000&lt;/code&gt; sets a timeout of 30 seconds.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PageSettings&lt;/code&gt;: The PDF page dimensions and margins, measured in inches. The example uses A4 paper, measuring 8.27 × 11.69 inches, with all margins set to zero for edge-to-edge output.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can change the width and height to use a custom paper size, or adjust the margin properties to add whitespace around the page content.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. ConvertToPdf()
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;ConvertToPdf()&lt;/code&gt; performs the conversion. It accepts a web page URL and saves the resulting PDF to the specified local file path.&lt;/p&gt;

&lt;p&gt;In addition to URLs, the API also supports local HTML file paths.&lt;/p&gt;

&lt;h2&gt;
  
  
  Advantages and Limitations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Advantages
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Native Chrome rendering&lt;/strong&gt;
Chrome’s rendering engine handles complex web pages, CSS3, dynamically rendered JavaScript content, SVG, and web fonts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No additional browser engine to bundle&lt;/strong&gt;
Reusing the Chrome installation on the target machine helps reduce the application’s package size.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simple configuration and API&lt;/strong&gt;
The converter avoids the need to write complex headless browser command-line arguments.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Included with the product&lt;/strong&gt;
&lt;code&gt;Spire.Additions.Chrome&lt;/code&gt; is built into Spire.PDF for Python and is available after installing the product.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ol&gt;
&lt;li&gt;Google Chrome must be installed on the target machine, and all Chrome windows must be closed during conversion.&lt;/li&gt;
&lt;li&gt;Server deployments require Chrome to be installed on the server. Container environments also require additional Chrome configuration.&lt;/li&gt;
&lt;li&gt;The solution depends on the locally installed browser version, so Chrome updates may affect compatibility.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Common Issues and Troubleshooting
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Conversion Fails Because of a Chrome Process Conflict
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Cause:&lt;/strong&gt; One or more Chrome windows are still open.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Close all Chrome windows, then run the application again.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The Application Cannot Find chrome.exe
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Cause:&lt;/strong&gt; The configured path is incorrect, or Chrome is installed in a user-specific directory such as &lt;code&gt;AppData\Local&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Locate &lt;code&gt;chrome.exe&lt;/code&gt; in File Explorer and copy its actual path into the code.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. The Output Is Blank or Images Are Missing
&lt;/h3&gt;

&lt;p&gt;Check for network problems or page-loading timeouts. Increase &lt;code&gt;options.Timeout&lt;/code&gt; if necessary.&lt;/p&gt;

&lt;p&gt;Also check whether the page requires a login session or cookie-based authentication.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. The Page Layout Looks Incorrect
&lt;/h3&gt;

&lt;p&gt;Adjust the paper dimensions and margins in &lt;code&gt;PageSettings&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Responsive layouts may also affect the output. If you control the HTML, consider adding print-specific CSS to improve the PDF layout.&lt;/p&gt;

&lt;h2&gt;
  
  
  Additional Notes on Spire.PDF for Python
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Although the example above is written in C#, this Chrome-based HTML-to-PDF capability is provided by  &lt;strong&gt;Spire.PDF for Python&lt;/strong&gt; , with &lt;code&gt;Spire.Additions.Chrome&lt;/code&gt; included as a built-in namespace. The Python API follows the same general design as the C# API.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The overall workflow is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install the Spire.PDF for Python package to access the namespace.&lt;/li&gt;
&lt;li&gt;Install Chrome locally and close it before running the script.&lt;/li&gt;
&lt;li&gt;Configure the Chrome path, page dimensions, and timeout, then call the conversion method to export the PDF.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Recommended Use Cases
&lt;/h2&gt;

&lt;p&gt;This approach is suitable for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Exporting web-based reports and preview pages from internal management systems.&lt;/li&gt;
&lt;li&gt;Archiving static web pages, invoices, and other business documents as PDFs.&lt;/li&gt;
&lt;li&gt;Generating PDFs that need to retain complex front-end layouts and web styling.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Combining the &lt;code&gt;Spire.Additions.Chrome&lt;/code&gt; namespace included with Spire.PDF for Python and a local Google Chrome installation provides a straightforward way to convert HTML to PDF with minimal C# code.&lt;/p&gt;

&lt;p&gt;Chrome’s rendering engine offers strong support for complex web pages compared with custom HTML rendering engines. The converter also provides a higher-level interface than Puppeteer, reducing the need to manage browser processes manually.&lt;/p&gt;

</description>
      <category>html</category>
      <category>pdf</category>
      <category>google</category>
    </item>
    <item>
      <title>Splitting Word Documents by Page or Section Using Python</title>
      <dc:creator>Jack9012 </dc:creator>
      <pubDate>Thu, 15 Oct 2026 01:55:21 +0000</pubDate>
      <link>https://dev.to/jack_du_64a902eb1614b3933/splitting-word-documents-by-page-or-section-using-python-5c8m</link>
      <guid>https://dev.to/jack_du_64a902eb1614b3933/splitting-word-documents-by-page-or-section-using-python-5c8m</guid>
      <description>&lt;p&gt;In document processing, it is often necessary to split a large Word document into multiple separate files. For example, splitting a hundreds-page report by chapters, or extracting a specified page range for separate distribution.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbpsy79o39cbcedop6jhu.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbpsy79o39cbcedop6jhu.png" alt="Split Word Documents in Python" width="799" height="418"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Common libraries for processing Word documents with Python include &lt;code&gt;python-docx&lt;/code&gt; and &lt;code&gt;Spire.Doc for Python&lt;/code&gt;. &lt;code&gt;python-docx&lt;/code&gt; excels at paragraph-level reading and writing, but it does not natively support direct access to page break information after document rendering. In contrast, &lt;strong&gt;Spire.Doc for Python&lt;/strong&gt; can directly read a document’s page layout and Section structure. It supports two widely used splitting strategies:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Split by page range&lt;/strong&gt; : Extract a specified page range to generate a new document based on the final rendered page layout of the file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Split by document section&lt;/strong&gt; : Save each section as an individual file according to Word section breaks, ideal for documents divided by chapters.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: Spire.Doc for Python is a standalone document processing library. Microsoft Word does not need to be installed in the runtime environment.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Environment Setup
&lt;/h2&gt;

&lt;p&gt;Install the dependency package directly via pip:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install Spire.Doc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Method 1: Split Word Documents by Page Range
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;ExtractPages(startIndex, count)&lt;/code&gt; method extracts a continuous range of pages according to the rendered page layout and creates a new document.  &lt;strong&gt;Page indices start at 0&lt;/strong&gt; . &lt;code&gt;startIndex&lt;/code&gt; refers to the starting page index, and &lt;code&gt;count&lt;/code&gt; is the number of pages to extract.&lt;/p&gt;

&lt;p&gt;Example requirement: Extract the first 3 pages into one file and save the remaining pages as another file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;spire.doc&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="c1"&gt;# Load the Word document
&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&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="nc"&gt;LoadFromFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input.docx&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# Extract pages 1-3
&lt;/span&gt;&lt;span class="n"&gt;first_part&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ExtractPages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&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;PageCount&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;first_part&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SaveToFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Page1-3.docx&lt;/span&gt;&lt;span class="sh"&gt;"&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;first_part&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Dispose&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;# Extract the remaining pages if the document has more than 3 pages
&lt;/span&gt;&lt;span class="k"&gt;if&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;PageCount&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;second_part&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ExtractPages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&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;PageCount&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;second_part&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SaveToFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Page4-End.docx&lt;/span&gt;&lt;span class="sh"&gt;"&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;second_part&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Dispose&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="nc"&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;
  
  
  Code Explanation
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;doc.LoadFromFile("input.docx")&lt;/code&gt;: Load the source Word document.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;doc.PageCount&lt;/code&gt;: Get the total number of pages in the document.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;min(3, doc.PageCount)&lt;/code&gt;: Boundary protection to prevent errors when the document has fewer than 3 pages.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ExtractPages&lt;/code&gt; returns a new Document object. Call &lt;code&gt;SaveToFile&lt;/code&gt; to export it as a docx file.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Dispose()&lt;/code&gt;: Release document resources to avoid memory consumption.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;Attention: Page-based splitting relies on the final typesetting layout of the document. Changes to fonts, images or page margins will affect pagination results, which match the pages you see when opening the file in Word.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Method 2: Split Word Documents by Document Section
&lt;/h2&gt;

&lt;p&gt;A Word &lt;strong&gt;Section&lt;/strong&gt; is defined by section breaks. Sections are commonly used to separate different chapters, distinct headers and footers, mixed portrait and landscape orientation, and other content. This approach works well for books, papers, manuals and other documents naturally divided into chapters with section breaks.&lt;/p&gt;

&lt;p&gt;Logic: Iterate over all Sections of the document, clone each Section, add it to a new Document object, and save each as a separate file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;spire.doc&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="c1"&gt;# Load the Word document
&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&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="nc"&gt;LoadFromFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Sample.docx&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# Split the document by section
&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&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;Sections&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;new_doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="c1"&gt;# Clone the current section and add it to the new document
&lt;/span&gt;    &lt;span class="n"&gt;new_doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sections&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Add&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;Sections&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_Item&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="nc"&gt;Clone&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="c1"&gt;# Save the section as an individual Word document
&lt;/span&gt;    &lt;span class="n"&gt;new_doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SaveToFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;output/SplitDocument/SplitBySectionBreak_&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.docx&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;new_doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Close&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="nc"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Code Explanation
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;doc.Sections.Count&lt;/code&gt;: Get the total number of sections inside the document.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;doc.Sections.get_Item(i).Clone()&lt;/code&gt;: Clone the current section. Cloning fully preserves page settings, headers, footers and styles of that section.&lt;/li&gt;
&lt;li&gt;Create a blank document in each loop iteration, add the cloned section, then save it as an independent file.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;new_doc.Close()&lt;/code&gt;: Close the sub-document and release resources.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;Tip: You need to manually create the &lt;code&gt;output/SplitDocument&lt;/code&gt; directory before running the code; otherwise, a path-not-found exception will be thrown when saving files.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Comparison of the Two Splitting Approaches
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Splitting Method&lt;/th&gt;
&lt;th&gt;Basis for Splitting&lt;/th&gt;
&lt;th&gt;Applicable Scenarios&lt;/th&gt;
&lt;th&gt;Limitations&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Split by page range&lt;/td&gt;
&lt;td&gt;Rendered page layout of the document&lt;/td&gt;
&lt;td&gt;Extract fixed page ranges, cut documents by page count&lt;/td&gt;
&lt;td&gt;Pagination positions will change after document content is modified&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Split by section&lt;/td&gt;
&lt;td&gt;Word section breaks&lt;/td&gt;
&lt;td&gt;Documents with chapters divided by section breaks and independent headers/footers&lt;/td&gt;
&lt;td&gt;If no section breaks are inserted in the document, there will only be one section and splitting cannot be performed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  General Notes
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Resource release&lt;/strong&gt; : Always call &lt;code&gt;Dispose()&lt;/code&gt; / &lt;code&gt;Close()&lt;/code&gt; after finishing using Document objects to prevent memory leaks during long-running batch document processing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pre-create directories&lt;/strong&gt; : When outputting files to subfolders, use &lt;code&gt;os.makedirs()&lt;/code&gt; to create folders.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trial version limitations&lt;/strong&gt; : The trial version of Spire.Doc adds watermarks to output documents and has partial function restrictions. Refer to the official licensing instructions for production environment use.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;File encoding and paths&lt;/strong&gt; : On Windows, English paths are recommended for files containing Chinese characters to avoid encoding issues.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Extended Ideas
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Batch splitting: Traverse all &lt;code&gt;.docx&lt;/code&gt; files in a folder and run the splitting script in a loop.&lt;/li&gt;
&lt;li&gt;Single-page splitting: Call &lt;code&gt;ExtractPages(i,1)&lt;/code&gt; in a loop to save each page as a separate document.&lt;/li&gt;
&lt;li&gt;Hybrid strategy: Split by sections first, then extract pages from each split document.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Spire.Doc for Python provides two straightforward ways to split Word documents. &lt;strong&gt;Splitting by page range&lt;/strong&gt; suits visual page cutting, while &lt;strong&gt;splitting by section&lt;/strong&gt; is designed for chapter splitting based on document structure (section breaks). Automated document splitting can be achieved with just dozens of lines of code, eliminating repetitive manual copy work. It can be integrated into office automation scripts and data processing pipelines.&lt;/p&gt;

&lt;p&gt;If needed, I can also provide a  &lt;strong&gt;fully runnable batch single-page splitting script&lt;/strong&gt; , or an enhanced version with automatic output directory creation and exception handling.&lt;/p&gt;

</description>
      <category>automation</category>
      <category>python</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to Print PDFs in Java: Silent Printing and Print Dialog Selection</title>
      <dc:creator>Jack9012 </dc:creator>
      <pubDate>Tue, 13 Oct 2026 05:56:35 +0000</pubDate>
      <link>https://dev.to/jack_du_64a902eb1614b3933/how-to-print-pdfs-in-java-silent-printing-and-print-dialog-selection-3h9h</link>
      <guid>https://dev.to/jack_du_64a902eb1614b3933/how-to-print-pdfs-in-java-silent-printing-and-print-dialog-selection-3h9h</guid>
      <description>&lt;p&gt;PDF printing is a common business requirement in daily Java backend and desktop development scenarios, such as report printing, document slip printing, and document archiving printing. Java natively provides the core printing API &lt;code&gt;java.awt.print&lt;/code&gt;. However, this native API cannot directly parse and render PDF documents. Third-party PDF parsing libraries are required to work alongside it to achieve full printing capabilities.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flrowy59848d6546v7ejp.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flrowy59848d6546v7ejp.png" alt="Print PDF Documents with Java" width="799" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This article combines two technologies: &lt;strong&gt;Spire.PDF for Java&lt;/strong&gt; (for PDF parsing and rendering) and &lt;strong&gt;java.awt.print&lt;/strong&gt; (for system print job scheduling). It implements two mainstream PDF printing scenarios: silent printing to the default printer without pop-up windows, and printing with a dialog for manual printer selection. Complete runnable code and detailed explanations of underlying principles are provided.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Tech Stack and Environment Setup
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1.1 Core Dependency Overview
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;java.awt.print&lt;/strong&gt; : The built-in JDK printing toolkit with no extra dependencies required. It is mainly responsible for retrieving system printers, configuring print page formats, scheduling print jobs, and catching printing exceptions. It serves as the foundational core for Java printing functions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Spire.PDF for Java&lt;/strong&gt; : A professional PDF parsing library used to load local PDF files and render PDF page content. It adapts to the print rendering interface of the native printing API and addresses the limitation that vanilla Java cannot print PDFs directly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  1.2 Project Dependency Configuration
&lt;/h3&gt;

&lt;p&gt;For Maven projects, add the Spire.PDF dependency in &lt;code&gt;pom.xml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;repositories&amp;gt;
    &amp;lt;repository&amp;gt;
        &amp;lt;id&amp;gt;com.e-iceblue&amp;lt;/id&amp;gt;
        &amp;lt;name&amp;gt;e-iceblue&amp;lt;/name&amp;gt;
        &amp;lt;url&amp;gt;https://repo.e-iceblue.com/nexus/content/groups/public/&amp;lt;/url&amp;gt;
    &amp;lt;/repository&amp;gt;
&amp;lt;/repositories&amp;gt;
&amp;lt;dependencies&amp;gt;
    &amp;lt;dependency&amp;gt;
        &amp;lt;groupId&amp;gt;e-iceblue&amp;lt;/groupId&amp;gt;
        &amp;lt;artifactId&amp;gt;spire.pdf&amp;lt;/artifactId&amp;gt;
        &amp;lt;version&amp;gt;12.9.0&amp;lt;/version&amp;gt;
    &amp;lt;/dependency&amp;gt;
&amp;lt;/dependencies&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Scenario 1: Silent Printing to Default Printer (No Print Dialog)
&lt;/h2&gt;

&lt;p&gt;This scenario applies to  &lt;strong&gt;background automatic printing with no manual intervention&lt;/strong&gt; . The system directly sends print jobs to the default printer without popping up any print configuration windows, running silently throughout the process.&lt;/p&gt;

&lt;p&gt;Core implementation logic: Obtain the system default print job via &lt;code&gt;PrinterJob&lt;/code&gt;, customize the printable area of the print page, load the PDF document and bind it to the print job, then trigger printing directly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Full Runnable Code
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import com.spire.pdf.PdfDocument;
import java.awt.print.PageFormat;
import java.awt.print.Paper;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
/**
 * PDF Silent Printing (Default Printer, No Dialog)
 */
public class PrintWithDefaultPrinter {
    public static void main(String[] args) {
        // 1. Get print job object for the system default printer
        PrinterJob printerJob = PrinterJob.getPrinterJob();
        // 2. Initialize page format and retrieve default page size and orientation
        PageFormat pageFormat = printerJob.defaultPage();
        // 3. Get paper object for the page and customize printable area
        Paper paper = pageFormat.getPaper();
        // Set full printable area of the paper (remove default margins to fit full PDF page)
        paper.setImageableArea(0, 0, pageFormat.getWidth(), pageFormat.getHeight());
        // Bind customized paper settings to page format
        pageFormat.setPaper(paper);
        // 4. Load local PDF document
        PdfDocument pdf = new PdfDocument();
        // Replace with your actual local PDF file path
        pdf.loadFromFile("input.pdf");
        // 5. Bind PDF document with page format to prepare print rendering
        printerJob.setPrintable(pdf, pageFormat);
        // 6. Execute silent print job
        try {
            printerJob.print();
            System.out.println("PDF silent print job completed");
        } catch (PrinterException e) {
            e.printStackTrace();
            System.err.println("PDF silent printing failed: " + e.getMessage());
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Key Code Explanations
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;PrinterJob.getPrinterJob()&lt;/code&gt;: Retrieves the system default print job instance and automatically binds to the machine’s default printer.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;paper.setImageableArea()&lt;/code&gt;: Resets the paper printable area and removes default margin padding, preventing incomplete PDF output or excessive white space on printed pages.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;printerJob.print()&lt;/code&gt;: Triggers the print job directly with no interactive dialog, ideal for automated printing workflows.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. Scenario 2: Printing with Dialog Popup (Manual Printer Configuration)
&lt;/h2&gt;

&lt;p&gt;This scenario is suitable for cases where  &lt;strong&gt;users need to select printers and adjust print parameters&lt;/strong&gt; . After the program starts, the native system print dialog opens. Users may switch printers, set print copies, page ranges, portrait/landscape orientation and other parameters before confirming to print.&lt;/p&gt;

&lt;p&gt;Core implementation logic: Based on the silent printing logic, add a print dialog check. The print job will only execute after user confirmation, offering greater flexibility.&lt;/p&gt;

&lt;h3&gt;
  
  
  Full Runnable Code
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import com.spire.pdf.PdfDocument;
import java.awt.print.PageFormat;
import java.awt.print.Paper;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
/**
 * PDF Printing with Dialog (Manual Printer Selection &amp;amp; Parameter Configuration)
 */
public class PrintWithPrintDialog {
    public static void main(String[] args) {
        // 1. Initialize print job
        PrinterJob printerJob = PrinterJob.getPrinterJob();
        // 2. Configure print page and paper format
        PageFormat pageFormat = printerJob.defaultPage();
        Paper paper = pageFormat.getPaper();
        paper.setImageableArea(0, 0, pageFormat.getWidth(), pageFormat.getHeight());
        pageFormat.setPaper(paper);
        // 3. Load local PDF file
        PdfDocument pdf = new PdfDocument();
        pdf.loadFromFile("input.pdf");
        // 4. Bind PDF document and print format
        printerJob.setPrintable(pdf, pageFormat);
        // 5. Bring up system print dialog and check user confirmation
        if (printerJob.printDialog()) {
            try {
                // Execute printing after user confirmation
                printerJob.print();
                System.out.println("PDF print job with dialog completed");
            } catch (PrinterException e) {
                e.printStackTrace();
                System.err.println("PDF printing with dialog failed: " + e.getMessage());
            }
        } else {
            System.out.println("User cancelled the print job");
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Key Code Explanations
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;printerJob.printDialog()&lt;/code&gt;: Launches the native system print dialog and returns a boolean value. &lt;code&gt;true&lt;/code&gt; means the user confirms printing; &lt;code&gt;false&lt;/code&gt; means the user cancels.&lt;/li&gt;
&lt;li&gt;All print parameters (printer selection, copy count, page numbers) are configured via the system dialog instead of hardcoding in code, accommodating diverse printing requirements.&lt;/li&gt;
&lt;li&gt;Custom printable area configuration is retained to ensure complete and intact PDF print layout.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  4. Common Issues and Notes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  4.1 File Path Compatibility
&lt;/h3&gt;

&lt;p&gt;The example uses Windows absolute paths. When deployed on Linux or macOS, modify the paths to match the target operating system and ensure the application has read permissions for the PDF file.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.2 Incomplete Printed Content / Excessive White Margins
&lt;/h3&gt;

&lt;p&gt;The primary solution is the &lt;code&gt;paper.setImageableArea()&lt;/code&gt; method shown in code. It resets the printable area and overrides default system margin restrictions to match the original PDF dimensions.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.3 Print Exception Handling
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;PrinterException&lt;/code&gt; is caught in the code, which can capture printing failures such as offline printers, out-of-paper status, or corrupted files, enabling business-layer alerting and logging.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Summary
&lt;/h2&gt;

&lt;p&gt;This article implements two core PDF printing solutions in Java using the native &lt;code&gt;java.awt.print&lt;/code&gt; API together with Spire.PDF:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Silent Printing&lt;/strong&gt; : No dialog window, fully automatic execution for background automated printing tasks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dialog-Based Printing&lt;/strong&gt; : Supports manual parameter configuration with high flexibility for user-initiated printing on the frontend.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Both code samples are concise, reusable and free of redundant logic. They can be directly adopted in Java desktop and backend projects. The implementation solves the core limitation that native Java cannot print PDFs directly while ensuring reliable and complete print layout rendering.&lt;/p&gt;

</description>
      <category>backend</category>
      <category>java</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Extracting Table Data from PDFs Using Python</title>
      <dc:creator>Jack9012 </dc:creator>
      <pubDate>Sat, 10 Oct 2026 01:33:31 +0000</pubDate>
      <link>https://dev.to/jack_du_64a902eb1614b3933/extracting-table-data-from-pdfs-using-python-149m</link>
      <guid>https://dev.to/jack_du_64a902eb1614b3933/extracting-table-data-from-pdfs-using-python-149m</guid>
      <description>&lt;p&gt;PDF is one of the most common document formats in daily work, but its "tables" are a different thing from tables in Excel: a PDF file itself does not store structured table data. A table on a page is merely a visual effect presented by text and lines according to their positional relationships. Therefore, reading PDF tables programmatically is essentially about letting a library analyze the page layout and identify the structure of rows and columns.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftc0itm4vgmwhq486t21u.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftc0itm4vgmwhq486t21u.png" alt="Extract PDF Table Data" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This article uses Spire.PDF for Python's &lt;code&gt;PdfTableExtractor&lt;/code&gt; to demonstrate how to detect tables in a PDF page by page and read each cell as text. The overall process has only three steps: load the document → extract tables page by page → iterate through rows and columns to read cells.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Environment Setup
&lt;/h2&gt;

&lt;p&gt;Install the library via pip:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install Spire.PDF
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you are just learning or handling small documents, you can also install the free version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install Spire.Pdf.Free
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that the free version has a page limit when loading and processing PDFs (at most the first 10 pages). Documents longer than 10 pages need to be split before processing, or you need to use the full PyPI version.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Core Code
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;spire.pdf&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;PdfDocument&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PdfTableExtractor&lt;/span&gt;

&lt;span class="c1"&gt;# Load PDF document
&lt;/span&gt;&lt;span class="n"&gt;pdf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PdfDocument&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="nc"&gt;LoadFromFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input.pdf&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Create a PdfTableExtractor object
&lt;/span&gt;&lt;span class="n"&gt;table_extractor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PdfTableExtractor&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;# Extract tables from each page
&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&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;Pages&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;tables&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;table_extractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ExtractTable&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="n"&gt;table_index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tables&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Table &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;table_index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; on page &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;GetRowCount&lt;/span&gt;&lt;span class="p"&gt;()):&lt;/span&gt;
            &lt;span class="n"&gt;row_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;GetColumnCount&lt;/span&gt;&lt;span class="p"&gt;()):&lt;/span&gt;
                &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;GetText&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="n"&gt;col&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="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;row_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&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;strip&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row_data&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Step-by-Step Code Explanation
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Load the PDF document&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pdf = PdfDocument()
pdf.LoadFromFile("input.pdf")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;PdfDocument&lt;/code&gt; is the document entry object. &lt;code&gt;LoadFromFile()&lt;/code&gt; loads the PDF by passing in the file path. The file path can be either relative or absolute.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Create the table extractor&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;table_extractor = PdfTableExtractor(pdf)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;PdfTableExtractor&lt;/code&gt; is responsible for analyzing the layout page by page and detecting table structures. When constructing it, you need to pass in the already loaded &lt;code&gt;PdfDocument&lt;/code&gt; object.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Extract tables page by page&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for i in range(pdf.Pages.Count):
    tables = table_extractor.ExtractTable(i)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;pdf.Pages.Count&lt;/code&gt; is the total number of pages in the document, and &lt;code&gt;ExtractTable(i)&lt;/code&gt; returns all tables detected on page &lt;code&gt;i&lt;/code&gt;. Note that it returns a list — a single page may contain multiple tables at the same time; if there are no tables on that page, it returns an empty list.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Iterate through the table's rows and columns&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for row in range(table.GetRowCount()):
    row_data = []
    for col in range(table.GetColumnCount()):
        text = table.GetText(row, col).replace("\n", " ")
        row_data.append(text.strip())
    print("\t".join(row_data))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;GetRowCount()&lt;/code&gt; / &lt;code&gt;GetColumnCount()&lt;/code&gt; get the number of rows and columns in the table;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GetText(row, col)&lt;/code&gt; reads the text of the specified cell. A cell may contain line breaks; here &lt;code&gt;\n&lt;/code&gt; is replaced with a space, and then &lt;code&gt;strip()&lt;/code&gt; removes leading and trailing whitespace to avoid extra blank lines in the output;&lt;/li&gt;
&lt;li&gt;Finally, &lt;code&gt;\t&lt;/code&gt; (tab) is used to join the cells of a row for output, making it easy to copy into Excel or use as TSV.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  4. Running Effect
&lt;/h2&gt;

&lt;p&gt;Running the above code on a PDF containing a simple table produces output roughly as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Table 1 on page 1:
Name    Department  Salary
Zhang San   Technical Department    12000
Li Si   Marketing Department    9800
Wang Wu Finance Department  10500
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each line corresponds to a row in the table, and cells are separated by tabs, so it can be pasted directly into spreadsheet software.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Advanced: Save the Results as CSV
&lt;/h2&gt;

&lt;p&gt;Printing to the console is only suitable for quick viewing. If you want to save the results, you can use Python's built-in &lt;code&gt;csv&lt;/code&gt; module, with no need to install an additional library:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;spire.pdf&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;PdfDocument&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PdfTableExtractor&lt;/span&gt;

&lt;span class="n"&gt;pdf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PdfDocument&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="nc"&gt;LoadFromFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input.pdf&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;extractor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PdfTableExtractor&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="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;output.csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;w&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;newline&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;writer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&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;Pages&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="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;extractor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ExtractTable&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="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;GetRowCount&lt;/span&gt;&lt;span class="p"&gt;()):&lt;/span&gt;
                &lt;span class="n"&gt;row_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                    &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;GetText&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="n"&gt;col&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="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;GetColumnCount&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
                &lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writerow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row_data&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="nc"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When writing with &lt;code&gt;csv.writer&lt;/code&gt;, the program automatically handles special characters such as commas and quotation marks contained in cells, which is more reliable than manually concatenating strings.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Notes and Limitations
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Suitable for tables with clear borders.&lt;/strong&gt; &lt;code&gt;PdfTableExtractor&lt;/code&gt; relies on analyzing the page layout. It recognizes tables with clear borders and regular structures relatively well; for tables without visible borders, cells spanning multiple lines, or complex layouts where headers are not clearly identified, it may fail to detect them or produce incomplete structures.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Scanned documents require OCR first.&lt;/strong&gt; If the PDF is a scanned image (without a text layer), no text-based tool can directly read the content. You need to use OCR first to convert the image into text. This library does not provide OCR functionality.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Page limit of the free version.&lt;/strong&gt; The free version can process at most the first 10 pages. When handling long documents, it is recommended to add a &lt;code&gt;min(pdf.Pages.Count, 10)&lt;/code&gt; limit in the loop, or split the document by page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Release resources after use.&lt;/strong&gt; When batch processing a large number of files, remember to call &lt;code&gt;pdf.Close()&lt;/code&gt; at the end to release resources.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Other Optional Libraries
&lt;/h2&gt;

&lt;p&gt;If you encounter complex tables that Spire.PDF cannot handle, you can try the following open-source libraries as supplements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;pdfplumber&lt;/strong&gt; : Based on pdfminer.six, it provides finer-grained access to page objects and is suitable for handling complex layouts;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;camelot&lt;/strong&gt; : Identifies structures by detecting table lines and works well for bordered tables;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;tabula-py&lt;/strong&gt; : Wraps Java's tabula, and its output is directly compatible with pandas DataFrame.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In actual projects, you can combine them according to the table type: regular tables can be handled by any library, while for complex ones, it is better to test on a small sample first before choosing.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Summary
&lt;/h2&gt;

&lt;p&gt;The process of extracting PDF tables with Spire.PDF for Python is very straightforward: load the document → create &lt;code&gt;PdfTableExtractor&lt;/code&gt; → call &lt;code&gt;ExtractTable()&lt;/code&gt; page by page → iterate through rows and columns to read cells. It is suitable for handling bordered, clearly structured tables, and with the &lt;code&gt;csv&lt;/code&gt; module, the results can be saved to disk. For special cases such as borderless tables and scanned documents, OCR or other libraries are needed as supplements.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>python</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Downloading PDF Documents from URLs with Python</title>
      <dc:creator>Jack9012 </dc:creator>
      <pubDate>Thu, 08 Oct 2026 01:31:01 +0000</pubDate>
      <link>https://dev.to/jack_du_64a902eb1614b3933/downloading-pdf-documents-from-urls-with-python-4m9b</link>
      <guid>https://dev.to/jack_du_64a902eb1614b3933/downloading-pdf-documents-from-urls-with-python-4m9b</guid>
      <description>&lt;p&gt;In scenarios such as automated office work, document collection, and batch resource fetching, it is often necessary to download PDF files from the network programmatically. Directly writing the binary stream returned by an interface to a local file can easily lead to corrupted files or format anomalies. This article uses &lt;code&gt;requests&lt;/code&gt; to handle network requests, combined with Spire.PDF for .NET to complete PDF stream validation and persistent saving, providing a ready-to-run download solution with built-in file validity checking.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffnoqnsyplozm6rcbu0o3.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffnoqnsyplozm6rcbu0o3.png" alt="Download PDF from URL" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Installing Environment Dependencies
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1.1 Network Request Library: requests
&lt;/h3&gt;

&lt;p&gt;Used to make HTTP requests and retrieve remote PDF binary data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install requests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1.2 PDF Processing Library
&lt;/h3&gt;

&lt;p&gt;This example relies on this library for in-memory stream loading, PDF validation, and local file export. The installation command is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install spire.pdf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Complete Runnable Code
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import requests
from spire.pdf import *

def download_pdf_from_url():
    # Specify the remote PDF resource URL
    url = "resource/sample.pdf"

    # Send a GET request to fetch the file's binary data
    response = requests.get(url)
    # Automatically raise 4xx/5xx HTTP errors to catch dead links and server issues in advance
    response.raise_for_status()
    # Wrap the downloaded byte data as an in-memory stream
    stream = Stream(response.content)
    # Load the PDF document from the memory stream, automatically validating whether the file is a legitimate PDF
    document = PdfDocument(stream)
    # Save the validated PDF to a local file
    document.SaveToFile("Downloaded.pdf")
    # Close the document to release memory resources
    document.Close()
    print("PDF downloaded and saved successfully!")

if __name__ == "__main__":
    download_pdf_from_url()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Line-by-Line Code Breakdown
&lt;/h2&gt;

&lt;h3&gt;
  
  
  3.1 Module Imports
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import requests
from spire.pdf import *
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;requests&lt;/code&gt;: a general-purpose Python HTTP library, responsible for remote file downloads;&lt;/li&gt;
&lt;li&gt;The PDF-related namespace: from Spire.PDF for .NET, providing full capabilities for stream reading and PDF document operations.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3.2 Remote Request and Exception Handling
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;response = requests.get(url)
response.raise_for_status()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;raise_for_status()&lt;/code&gt; is crucial fault-tolerant logic: when a link returns 404, the server returns 500, or access is denied, the program throws an exception directly instead of generating a corrupted blank file.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.3 Loading the PDF from a Memory Stream (Core Advantage)
&lt;/h3&gt;

&lt;p&gt;The conventional download logic directly uses &lt;code&gt;open&lt;/code&gt; to write the byte stream to a file, without being able to determine whether the returned data is a valid PDF.&lt;/p&gt;

&lt;p&gt;This solution first converts the binary data returned by the interface into a &lt;code&gt;Stream&lt;/code&gt;, then hands it to &lt;code&gt;PdfDocument&lt;/code&gt; for loading:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Automatically validates whether the data stream conforms to the PDF standard;&lt;/li&gt;
&lt;li&gt;Filters out anomalies such as interrupted downloads and HTML error pages returned in place of the file;&lt;/li&gt;
&lt;li&gt;Processes everything in memory, without generating temporary cache files.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  3.4 Saving the File and Releasing Resources
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;SaveToFile&lt;/code&gt; supports custom output locations via relative or absolute paths. After processing, call &lt;code&gt;Close()&lt;/code&gt; to release the memory occupied by the document, which effectively prevents memory overflow during long batch download loops.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Practical Usage Notes
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;URL requirements&lt;/strong&gt;
In the example, &lt;code&gt;resource/sample.pdf&lt;/code&gt; is a relative resource path; in production, replace it with a full http/https public PDF link. For private links requiring login authentication or Cookie validation, add the &lt;code&gt;headers&lt;/code&gt; and &lt;code&gt;cookies&lt;/code&gt; parameters to &lt;code&gt;requests.get&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extended exception handling&lt;/strong&gt;
The basic code only intercepts HTTP errors. In a production environment, it is recommended to wrap the logic in &lt;code&gt;try except&lt;/code&gt; to catch exceptions such as PDF parsing failures and insufficient file-write permissions:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   try:
       # Complete download logic
   except requests.exceptions.RequestException as http_err:
       print(f"Network request failed: {http_err}")
   except Exception as pdf_err:
       print(f"PDF processing failed, the file may be corrupted: {pdf_err}")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Batch download adaptation&lt;/strong&gt;
Store multiple PDF URLs in a list, call &lt;code&gt;download_pdf_from_url&lt;/code&gt; in a loop, and modify the output file names to avoid overwriting, enabling batch archiving of remote PDFs.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  5. Summary
&lt;/h2&gt;

&lt;p&gt;This download solution combines the network capabilities of &lt;code&gt;requests&lt;/code&gt; with Spire.PDF for .NET's PDF parsing capabilities. Its biggest highlight is validating file legitimacy at download time, solving the pain point of ordinary download methods easily producing corrupted PDFs. The code is compact and highly extensible, making it suitable for various development scenarios such as script automation, backend document synchronization, and crawler resource collection.&lt;/p&gt;

</description>
      <category>pdf</category>
      <category>python</category>
    </item>
  </channel>
</rss>
