冰蓝科技
|
028-81705109
|
|
微信扫一扫
|

Spire.Cloud 纯前端文档控件

我们很高兴地宣布发布 Spire.Presentation for JavaScript 11.4.1。本次更新引入了两项主要增强功能:将 PowerPoint 转换为 PDF/A,可直接从演示文稿中创建符合长期存档要求的文档;以及将带图片的 HTML 添加到 PowerPoint,可将包括图片在内的丰富 HTML 内容无缝嵌入到幻灯片中。

新功能:


获取 Spire.Presentation for JavaScript 11.4.1 请点击:

https://www.e-iceblue.cn/Downloads/Spire-Presentation-JavaScript.html

Spire.PDF for Android via Java 10.8.0 现已正式发布。该版本引入了用于处理 PDF 图片的 PdfImageHelper 类,并优化了获取签名证书名称、注册表格布局事件的相关方法。详情请查阅以下内容。

优化:


获取 Spire.PDF for Android via Java 10.8.0 请点击:

https://www.e-iceblue.cn/Downloads/pdf-for-android-via-java.html

使用 Spire.XLS for .NET 将数据库转换为 PDF 的教程

将数据库查询结果导出为 PDF 是报告生成、数据归档和文档创建等常见应用场景中的需求。通常需要将 SQL 查询结果转换为结构清晰、易于阅读的文档,以便后续共享、存档或打印使用。

由于数据库中的数据通常以表格形式存储,因此在导出过程中保持其结构完整性对于确保文档的清晰度和可读性至关重要。若缺乏合理的布局控制,生成的 PDF 很容易变得杂乱,尤其是在处理大规模数据集时,这一问题会更加明显。

本文将演示如何使用 Spire.XLS for .NET 在 C# 中将数据库转换为 PDF,包括如何检索查询结果、将其组织为结构化表格,以及最终导出为格式清晰的 PDF 文档的完整实现过程。

目录

1. 数据库转 PDF 的基本流程

将数据库内容转换为 PDF 通常包含以下几个关键步骤:

  • 数据检索:执行 SQL 查询并将结果加载到内存中
  • 数据结构化:对查询结果进行整理,转换为统一的表格格式
  • PDF 导出:生成保留结构与可读性的 PDF 文档

在实际应用中,这一流程通常用于生成报表、制作发票或归档查询结果等场景,其核心目标是确保数据以清晰、结构化的方式呈现,便于后续查看与使用。

2. 使用 C# 将数据库转换为 PDF(分步指南)

本节提供将数据库查询结果转换为 PDF 文档的完整工作流程,涵盖数据检索、表格构建、格式设置和导出等环节。

2.1 环境设置

在实现解决方案之前,请确保您的开发环境已准备就绪:

  • .NET 环境
    安装 Visual Studio 或使用具有兼容 .NET 版本(例如 .NET 6 或更高版本)的 .NET CLI。

  • 数据库访问
    准备一个 SQL Server 数据库(或任何关系型数据库),并确保您拥有有效的连接字符串。对于现代 .NET 应用程序,请使用推荐的 SQL 客户端库:

    dotnet add package Microsoft.Data.SqlClient
    

    此包提供了 SQL Server 的 ADO.NET 实现,并取代了传统的 System.Data.SqlClient。

  • Spire.XLS for .NET 通过 NuGet 安装 Spire.XLS 以处理表格格式化和 PDF 导出:

    dotnet add package Spire.XLS
    

    您也可以下载 Spire.XLS for .NET 包并手动将其添加到项目中。

配置完成后,您可以从数据库检索数据并使用 Spire.XLS 生成和导出 PDF 文档。

2.2 从数据库读取数据

第一步是执行 SQL 查询并将结果加载到 DataTable 中。这种数据结构能够保留查询结果的架构和数据类型,便于后续转换处理。

在本示例中,通过在 SQL 查询中为字段设置中文别名(如“订单编号”、“客户名称”等),可以使导出的报表更符合实际业务阅读习惯。这种方式避免了在导出阶段额外处理列名,同时提升了报表的可读性和专业性。

using System.Data;
using Microsoft.Data.SqlClient;

string connectionString = "Server=localhost\\SQLEXPRESS;Database=SalesDB;User ID=demouser;Password=YourPassword;Encrypt=true;TrustServerCertificate=true;";
string query = @"
    SELECT 
    o.OrderID AS 订单编号,
    c.CustomerName AS 客户名称,
    o.OrderDate AS 订单日期,
    o.TotalAmount AS 订单金额
    FROM Orders o
    JOIN Customers c ON o.CustomerID = c.CustomerID
    WHERE YEAR(o.OrderDate) = 2026;
";

DataTable dataTable = new DataTable();

using (SqlConnection conn = new SqlConnection(connectionString))
{
    SqlDataAdapter adapter = new SqlDataAdapter(query, conn);
    adapter.Fill(dataTable);
}

此示例使用 Microsoft.Data.SqlClient,这是适用于 .NET 的现代 SQL 客户端库,推荐使用它来替代传统的 System.Data.SqlClient。

SqlDataAdapter 充当数据库与内存数据之间的桥梁。它负责执行查询并填充 DataTable,无需手动管理读取操作的连接。

在实际开发中,可以对此步骤进行扩展,包括:

  • 参数化查询以避免 SQL 注入
  • 用于复杂数据检索的存储过程
  • 直接在 SQL 中进行数据过滤和聚合

通过在此阶段准备好干净且结构化的数据,可以降低后续格式处理的复杂度,并提升整体性能。

如需了解将数据库查询结果导出到 Excel(而非 PDF)的类似场景,可参考:在 C# 中将数据库导出到 Excel。

2.3 导入数据并以格式化方式导出为 PDF

获取数据后,下一步是将其映射到工作表、应用格式设置,然后导出为 PDF 文档。这里利用基于工作表的布局控制,确保输出内容保持结构化和可读性。

using Spire.Xls;
using System.Drawing;

// 创建工作簿和工作表
Workbook workbook = new Workbook();
Worksheet sheet = workbook.Worksheets[0];

// 导入包含标题的 DataTable
sheet.InsertDataTable(dataTable, true, 1, 1);

// 格式化表头
CellRange headerRange = sheet.Range[1, 1, 1, dataTable.Columns.Count];
headerRange.Style.Font.IsBold = true;
headerRange.Style.Font.Size = 11;
headerRange.Style.Color = Color.LightGray;

// 应用边框以增强表格结构
CellRange dataRange = sheet.AllocatedRange;
dataRange.BorderAround(LineStyleType.Thin);
dataRange.BorderInside(LineStyleType.Thin);

// 对齐内容以保持一致性
dataRange.Style.HorizontalAlignment = HorizontalAlignType.Center;
dataRange.Style.VerticalAlignment = VerticalAlignType.Center;

// 应用全局字体
sheet.AllocatedRange.Style.Font.FontName = "微软雅黑";

// 自动调整列宽以获得更好的布局
sheet.AllocatedRange.AutoFitColumns();

// 在页面中水平居中内容
sheet.PageSetup.CenterHorizontally = true;

// 导出为 PDF
workbook.SaveToFile("SalesReport_2026.pdf", FileFormat.PDF);

此步骤将布局控制和 PDF 生成整合到一个工作流程中。

需要注意的关键点:

  • 工作表作为布局引擎 工作表充当结构化画布,数据库数据在其中按行和列排列。这确保了原始表格结构在最终文档中得以完整保留。

  • 格式化直接影响 PDF 输出效果 列宽、字体样式和边框等调整不仅仅是视觉美化——它们直接决定了内容在 PDF 中的渲染效果。不当的格式设置可能导致文本被截断或布局难以阅读。

  • 自动分页功能 导出时,工作表内容会根据布局和纸张大小自动进行分页处理,这对于处理大型数据集特别有用。

如需进一步优化布局,可以通过以下方式增强表格格式:

如果您的项目需要更灵活的 PDF 结构控制,还可以探索使用 Spire.PDF for .NET 直接将 DataTable 转换为 PDF,为复杂的报表需求提供更高级的文档级操作功能。

3. 将数据库转换为 PDF 的完整 C# 示例

以下是将数据库检索、数据格式化和 PDF 导出整合到单一工作流中的完整实现代码。

using System;
using System.Data;
using Microsoft.Data.SqlClient;
using Spire.Xls;
using System.Drawing;

class Program
{
    static void Main()
    {
        // 步骤 1:从数据库检索数据
        string connectionString = "Server=localhost\\SQLEXPRESS;Database=SalesDB;User ID=demouser;Password=YourPassword;Encrypt=true;TrustServerCertificate=true;";
        string query = @"
            SELECT 
            o.OrderID AS 订单编号,
            c.CustomerName AS 客户名称,
            o.OrderDate AS 订单日期,
            o.TotalAmount AS 订单金额
            FROM Orders o
            JOIN Customers c ON o.CustomerID = c.CustomerID
            WHERE YEAR(o.OrderDate) = 2026;
        ";

        DataTable dataTable = new DataTable();

        using (SqlConnection conn = new SqlConnection(connectionString))
        {
            SqlDataAdapter adapter = new SqlDataAdapter(query, conn);
            adapter.Fill(dataTable);
        }

        // 步骤 2:创建工作簿并导入数据
        Workbook workbook = new Workbook();
        Worksheet sheet = workbook.Worksheets[0];
        sheet.InsertDataTable(dataTable, true, 1, 1);

        // 步骤 3:应用专业格式化
        // 格式化表头
        CellRange headerRange = sheet.Range[1, 1, 1, dataTable.Columns.Count];
        headerRange.Style.Font.IsBold = true;
        headerRange.Style.Font.Size = 11;
        headerRange.Style.Color = Color.LightGray;

        // 应用边框
        CellRange dataRange = sheet.AllocatedRange;
        dataRange.BorderAround(LineStyleType.Thin);
        dataRange.BorderInside(LineStyleType.Thin);

        // 设置对齐方式
        dataRange.Style.HorizontalAlignment = HorizontalAlignType.Center;
        dataRange.Style.VerticalAlignment = VerticalAlignType.Center;

        // 应用全局字体
        sheet.AllocatedRange.Style.Font.FontName = "微软雅黑";

        // 自动调整列宽
        sheet.AllocatedRange.AutoFitColumns();

        // 在页面中水平居中内容
        sheet.PageSetup.CenterHorizontally = true;

        // 步骤 4:导出为 PDF
        workbook.SaveToFile("SalesReport_2026.pdf", FileFormat.PDF);

        Console.WriteLine("数据库查询结果已成功导出为 PDF。");
    }
}

以下是生成的 PDF 预览:

使用 C# 将数据库查询结果转换为 PDF

此示例展示了从执行 SQL 查询到生成 PDF 的完整工作流程。

4. 高级场景

在实际应用中,将数据库数据导出为 PDF 往往不止于基本转换,还可能需要处理批量导出、提升文档可读性,或调整布局设置以获得更好的展示效果。以下示例展示了实际使用中常见的增强功能。

导出多个查询结果

对于批量报表生成或定时任务等场景,可能需要执行多个查询,并将每个查询结果导出为独立的 PDF 文档:

string[] queries = {
    "SELECT * FROM Orders WHERE Status = 'Pending'",
    "SELECT * FROM Customers WHERE Region = 'North'"
};

for (int i = 0; i < queries.Length; i++)
{
    DataTable dt = ExecuteQuery(queries[i]);
    Workbook wb = new Workbook();
    Worksheet ws = wb.Worksheets[0];
    ws.InsertDataTable(dt, true, 1, 1);
    ws.AllocatedRange.AutoFitColumns();
    wb.SaveToFile($"Report_{i + 1}.pdf", FileFormat.PDF);
}

这种方法适用于需要独立导出多个数据集的自动化报表生成场景。

添加标题和更多信息

为提升可读性并提供上下文信息,可以在导出数据之前,在数据上方添加标题行:

// 插入标题行
sheet.InsertRow(1);
sheet.Range[1, 1].Text = "销售报告 - 2026";
sheet.Range[1, 1].Style.Font.IsBold = true;
sheet.Range[1, 1].Style.Font.Size = 14;

// 合并标题单元格
sheet.Range[1, 1, 1, dataTable.Columns.Count].Merge();

// 自动调整标题行
sheet.AutoFitRow(1);

下图显示了应用标题行后生成的 PDF:

使用 C# 将数据库查询结果转换为 PDF

添加标题有助于用户快速理解文档内容,尤其在共享或打印报表时非常实用。

设置页面大小、方向和边距

为确保 PDF 布局能够很好地适配您的数据,可以在导出前配置页面大小、方向和边距:

// 设置页面大小和方向
sheet.PageSetup.PaperSize = PaperSizeType.PaperA4;
sheet.PageSetup.Orientation = PageOrientationType.Portrait;

// 设置页面边距
sheet.PageSetup.TopMargin = 0.5f;
sheet.PageSetup.BottomMargin = 0.2f;
sheet.PageSetup.LeftMargin = 0.2f;
sheet.PageSetup.RightMargin = 0.2f;

调整这些设置有助于防止内容溢出,并确保不同报表之间的布局保持一致。

控制页面布局和缩放

处理大型表格时,可能需要控制内容在页面间的分布方式。默认情况下,内容会自动分页,但也可以调整缩放行为,以便在单个页面中容纳更多数据。

// 将内容适应页面宽度
workbook.ConverterSetting.SheetFitToWidth = true;

// 将整个工作表适应单个页面(可能会降低可读性)
workbook.ConverterSetting.SheetFitToPage = true;
  • SheetFitToWidth 确保表格宽度适应页面,同时允许垂直方向的分页
  • SheetFitToPage 将整个工作表缩放到单个页面内

这些设置在生成紧凑报表时很有用,但应谨慎使用,避免文字过小影响阅读。

添加页眉和页脚

页眉和页脚可用于添加上下文信息,如报表标题、生成时间或页码:

sheet.PageSetup.LeftHeader = "&\"微软雅黑,Bold\"&16 销售报告 - 2026";
sheet.PageSetup.RightHeader = "&\"微软雅黑,Italic\"&10 生成日期:&D";
sheet.PageSetup.CenterFooter = "&\"微软雅黑,Regular\"&16 第 &P 页,共 &N 页";

下图显示了应用页眉和页脚后生成的 PDF:

使用 C# 将数据库查询结果转换为 PDF

这些元素提升了文档的导航体验,对于多页报表尤其有价值。

加密 PDF

为了保护敏感数据,您可以对导出的 PDF 应用加密:

workbook.ConverterSetting.PdfSecurity.Encrypt("openpsd");

加密功能确保只有授权用户才能访问文档,这对于包含机密信息或关键业务数据的报表非常重要。

如需了解更多关于文档导出和 PDF 自定义的相关场景,还可探索C# 中的 Excel 转 PDF 转换。

5. 常见问题

数据库连接问题

请确保连接字符串正确无误,且数据库服务器可正常访问。验证身份验证设置(如 SQL 身份验证或集成安全性),并确认加密相关参数与环境配置相匹配。

空查询结果

在继续处理之前,请检查 DataTable 是否包含数据。空结果集可能导致生成空白 PDF 或出现意外的格式问题。

if (dataTable.Rows.Count == 0)
{
    Console.WriteLine("未找到指定查询的数据。");
    return;
}

在生产环境中,您还可以选择生成占位符 PDF 或记录错误日志,而不是直接退出程序。

列宽溢出

处理长文本字段时,AutoFitColumns() 可能会产生过宽的列,从而对 PDF 布局造成负面影响。

为提升可读性,建议考虑以下方案:

  • 设置最大列宽
  • 为长内容启用文本换行
  • 根据数据类型手动调整关键列

这在导出包含可变长度文本的大型数据集时尤为重要。

缺少字体支持

如果导出的 PDF 包含特殊字符(如非拉丁文字)或自定义字体,请确保所需字体已安装且在运行时可访问。

缺少字体会导致文本渲染异常或使用替代字体,从而影响文档的外观和可读性。

意外的 PDF 布局

如果导出的 PDF 布局显得拥挤或缩放不当,请检查页面设置和缩放选项,如 SheetFitToWidth 或 SheetFitToPage。

不正确的缩放设置可能导致内容显示过小,或使原始表格结构变形。

总结

本文介绍了在 C# 中将数据库查询结果转换为 PDF 的实用方法。通过将结构化数据检索与基于工作表的格式设置相结合,您可以直接从 SQL 数据生成清晰、专业的文档。

这种方法在报表生成和数据呈现场景中特别有效,尤其是在需要保持表格结构和可读性的情况下。

如果您正在评估 Spire.XLS,可以申请免费临时许可证,以便在开发过程中去除评估限制。

常见问题解答

Spire.XLS 可以在不使用第三方工具的情况下将数据库数据导出为 PDF 吗?

是的。Spire.XLS 独立执行所有操作,不需要 Microsoft Office 或任何其他外部工具。

导出大型数据集到 PDF 时应如何处理?

对于大型数据集,建议对结果进行分页处理,或通过过滤查询仅检索必要的数据。此外,还可以调整 PDF 页面设置以优化输出文件大小。

我可以自定义 PDF 页面布局吗?

是的。Spire.XLS 允许您在导出为 PDF 之前配置页面设置,包括方向、边距和纸张大小等。

此方法是否适用于 SQL Server 以外的数据库?

是的。该方法适用于支持 ADO.NET 数据提供程序的任何数据库,包括 MySQL、PostgreSQL 和 Oracle。只需使用相应的连接类和数据适配器即可。

我应该使用 Microsoft.Data.SqlClient 还是 System.Data.SqlClient?

对于现代 .NET 应用程序,建议使用 Microsoft.Data.SqlClient。该库持续更新维护,并为新版 SQL Server 功能提供更好的支持;而 System.Data.SqlClient 被视为遗留库,不再接收主要功能更新。

Spire.XLS for JavaScript 16.4.0 现已正式发布。该版本支持转换 Excel 和 ChartSheet 到 SVG。此外,还支持在克隆单元格范围时,通过配置 OnlyCopyFormulaValue 仅拷贝公式值。更多详情如下。

新功能:


获取 Spire.XLS for JavaScript 16.4.0 请点击:

https://www.e-iceblue.cn/Downloads/Spire-XLS-Javascript.html

我们很高兴宣布发布 Spire.Doc for Java 14.4.0。本次更新修复了在调用 appendHTML 方法时导致 NoClassDefFoundError 的缺陷。

问题修复:


获取 Spire.Doc for Java 14.4.0 请点击:

https://www.e-iceblue.cn/Downloads/Spire-Doc-JAVA.html

Spire.XLS for Python 16.4.0 现已发布。本版本支持隐藏数据透视表字段列表,并支持 Excel 与 Markdown 之间的相互转换。同时修复了一些问题,例如设置单元格的 Value2 值时程序抛异常的问题。更新内容如下。

新功能:

问题修复:


获取 Spire.XLS for Python 16.4.0 请点击:

https://www.e-iceblue.cn/Downloads/Spire-XLS-Python.html

在查看大型 Excel 工作表中的数据时,当你滚动到工作表的其他位置,往往会丢失对标题行或标题列的视线。在这种情况下,Microsoft Excel 提供了"冻结窗格(Freeze Panes)"功能,帮助你锁定必要的行和/或列,使其始终保持可见。

在本文中,你将学习如何使用 Spire.XLS for C++ 以编程方式冻结 Excel 工作表中的行或列。

Spire.XLS for C++ 提供了 Worksheet->FreezePanes(int rowIndex, int columnIndex) 方法,用于冻结该单元格上方的所有行和左侧的所有列,其中 rowIndex 和 columnIndex 用于指定目标单元格的位置。

本教程将针对以下几种情况提供代码示例:

安装 Spire.XLS for C++

将 Spire.XLS for C++ 集成到你的应用程序中有两种方式:

  • 一种是通过 NuGet 安装。
  • 另一种是从官网下载包并将相关库文件手动复制到项目中。

通过 NuGet 安装更加简单,也更推荐使用。

你也可以通过访问以下链接了解更详细的集成步骤:

如何将 Spire.XLS for C++ 集成到 C++ 程序中

在 C++ 中冻结 Excel 的首行

要冻结首行,需要将选定单元格设置为 (2, 1),即 "A2"。具体步骤如下:

  • 创建一个 Workbook 对象。
  • 使用 Workbook->LoadFromFile() 方法加载 Excel 文档。
  • 通过 Workbook->GetWorksheets()->Get() 方法获取指定的工作表。
  • 使用 Worksheet->FreezePanes(2, 1) 方法冻结首行。
  • 调用 Workbook->SaveToFile() 方法将工作簿保存为另一个 Excel 文件。
#include "Spire.Xls.o.h"

using namespace Spire::Xls;
using namespace std;

int main()
{
	//指定输入文件路径
	std::wstring data_path = L"Data\\";
	std::wstring inputFile = data_path + L"sample.xlsx";

	//指定输出文件路径
	std::wstring outputPath = L"Output\\";
	std::wstring outputFile = outputPath + L"FreezeFirstRow.xlsx";

	//创建工作簿
	intrusive_ptr<Workbook> workbook = new Workbook();

	//从磁盘加载 Excel 文档
	workbook->LoadFromFile(inputFile.c_str());

	//获取第一个工作表
	intrusive_ptr<Worksheet> sheet = dynamic_pointer_cast<Worksheet>(workbook->GetWorksheets()->Get(0));

	//冻结第一行
	sheet->FreezePanes(2, 1);

	//保存文件
	workbook->SaveToFile(outputFile.c_str(), ExcelVersion::Version2013);
	workbook->Dispose();
}

在 C++ 中冻结 Excel 首行

在 C++ 中冻结 Excel 的首列

要冻结首列,需要将选定单元格设置为 (1, 2),即 "B1"。以下是在 Excel 工作表中冻结首列的具体步骤:

  • 创建一个 Workbook 对象。
  • 使用 Workbook->LoadFromFile() 方法加载 Excel 文档。
  • 通过 Workbook->GetWorksheets()->Get() 方法获取指定的工作表。
  • 使用 Worksheet->FreezePanes(1, 2) 方法冻结首列。
  • 调用 Workbook->SaveToFile() 方法将工作簿保存为另一个 Excel 文件。
#include "Spire.Xls.o.h"

using namespace Spire::Xls;
using namespace std;

int main()
{
	//指定输入文件路径
	std::wstring data_path = L"Data\\";
	std::wstring inputFile = data_path + L"sample.xlsx";

	//指定输出文件路径
	std::wstring outputPath = L"Output\\";
	std::wstring outputFile = outputPath + L"FreezeFirstColumn.xlsx";

	//创建工作簿
	intrusive_ptr<Workbook> workbook = new Workbook();

	//从磁盘加载 Excel 文档
	workbook->LoadFromFile(inputFile.c_str());

	//获取第一个工作表
	intrusive_ptr<Worksheet> sheet = dynamic_pointer_cast<Worksheet>(workbook->GetWorksheets()->Get(0));

	//冻结第一列
	sheet->FreezePanes(1, 2);

	//保存文件
	workbook->SaveToFile(outputFile.c_str(), ExcelVersion::Version2013);
	workbook->Dispose();
}

在 C++ 中冻结 Excel 首列

在 C++ 中同时冻结首行和首列

如果你希望同时冻结首行和首列,需要将选定单元格设置为 (2, 2),即 "B2"。具体步骤如下:

  • 创建一个 Workbook 对象。
  • 使用 Workbook->LoadFromFile() 方法加载 Excel 文档。
  • 通过 Workbook->GetWorksheets()->Get() 方法获取指定的工作表。
  • 使用 Worksheet->FreezePanes(2, 2) 方法同时冻结首行和首列。
  • 调用 Workbook->SaveToFile() 方法将工作簿保存为另一个 Excel 文件。
#include "Spire.Xls.o.h" 

using namespace Spire::Xls;
using namespace std;

int main()
{
	//指定输入文件路径
	std::wstring data_path = L"Data\\";
	std::wstring inputFile = data_path + L"sample.xlsx";

	//指定输出文件路径
	std::wstring outputPath = L"Output\\";
	std::wstring outputFile = outputPath + L"FreezeFirstRowAndColumn.xlsx";

	//创建工作簿
	intrusive_ptr<Workbook> workbook = new Workbook();

	//从磁盘加载 Excel 文档
	workbook->LoadFromFile(inputFile.c_str());

	//获取第一个工作表
	intrusive_ptr<Worksheet> sheet = dynamic_pointer_cast<Worksheet>(workbook->GetWorksheets()->Get(0));

	//冻结第一行和第一列
	sheet->FreezePanes(2, 2);

	//保存文件
	workbook->SaveToFile(outputFile.c_str(), ExcelVersion::Version2013);
	workbook->Dispose();
}

在 C++ 中冻结首行和首列

申请临时 License

如果您希望删除结果文档中的评估信息,或者摆脱功能限制,请联系销售获取有效期 30 天的临时许可证。

C++:Word 转 OFD

在服务于中国市场的 C++ 项目中,将 Word 文档转换为 OFD(开放版式文档,国家标准 GB/T 33190)是一项常见需求——无论是后端服务、报表生成器还是文档处理系统,都可能涉及这一场景。然而,传统方案通常依赖 Microsoft Office COM 自动化,部署繁琐,且难以适应服务器端环境。

本文将演示如何使用 Spire.Doc for C++ 通过编程方式实现 Word 转换为 OFD,涵盖基础文档转换与导出指定页面等进阶用法。

为什么要将 Word 转换为 OFD?

相比便于编辑的 Word 文档,OFD 更适合用于正式发布和长期存档。在中国的政务和企业应用中,将 Word 转换为 OFD 通常具有以下实际价值:

  • 排版一致性:可保留原始排版,在不同设备和系统上显示一致。
  • 文档标准化:确保在不同平台和阅读器中渲染效果统一。
  • 长期存档:适用于法律、财务等长期保存的文档。
  • 增强文档完整性:支持数字签名和防篡改机制。
  • 支持自动化:可在系统流程中实现程序化转换。

为什么选择 Spire.Doc for C++

在 C++ 中实现 Word 转 OFD,通常有几种方式:COM 自动化、调用外部工具,或手动解析 .docx 文件。但这些方式往往存在平台限制、环境依赖或实现复杂的问题。

相比之下,Spire.Doc for C++ 提供了一种更直接的实现方式:

  • 无需安装 Office:适合服务器环境,部署更简单。
  • 跨平台支持:兼容 Windows 和 Linux,适用于容器化应用。
  • 高度还原格式:输出的 OFD 文档能准确保留原文档中的表格、图片、页眉页脚和字体样式。
  • API 简洁易用:核心转换逻辑非常简练,无需深入了解复杂的文档对象模型。
  • 支持多种 Word 格式:包括 DOC、DOCX、DOCM、DOTX 和 DOTM。

虽然它不能覆盖所有极端场景,但对于大多数生产环境需求来说已经足够稳定可靠。

搭建 Spire.Doc for C++ 开发环境

在开始转换之前,请确保开发环境已正确配置。

通过 NuGet 安装

  1. 在 Visual Studio 中打开你的项目。
  2. 转到工具→ NuGet 包管理器→管理解决方案的 NuGet 程序包...。
  3. 搜索 “Spire.Doc.Cpp”。
  4. 点击安装。

手动安装

  1. 从官网下载最新版 Spire.Doc for C++。
  2. 解压安装包。
  3. 配置 Visual Studio 项目,添加 Include 目录的路径,并链接 lib 目录。确保 DLL 文件放置在可执行文件所在目录或系统路径下。

详细的安装教程可参考:如何将 Spire.Doc for C++ 集成到 C++ 程序中

基本项目配置

安装完成后,在代码中添加以下内容:

#include "Spire.Doc.o.h"
using namespace Spire::Doc;

基础转换:在 C++ 中将 Word 转为 OFD

项目准备就绪后,只需几行代码即可完成转换。

完整 C++ 示例:

#include "Spire.Doc.o.h"
#include <iostream>

using namespace Spire::Doc;

int main()
{
    // 创建 Document 对象
    Document* document = new Document();

    try {
        // 加载 Word 文档
        document->LoadFromFile(L"C:\\Users\\Desktop\\测试.docx");

        // 将文档转换并保存为 OFD 格式
        document->SaveToFile(L"C:\\Users\\Desktop\\测试.ofd", FileFormat::OFD);

        document->Close();
        std::cout << "转换成功!" << std::endl;
    }
    catch (const std::exception& ex) {
        std::cerr << "错误: " << ex.what() << std::endl;
        delete document;
        return -1;
    }

    delete document;
    return 0;
}

C++:Word 到 OFD 的基础转换

核心代码说明:

  • 创建 Document 对象。
  • 使用 LoadFromFile() 加载 Word 文件。
  • 使用 SaveToFile() 并指定 FileFormat::OFD 完成转换。

进阶:Word 转 OFD 的高级转换设置

除了基础转换,Spire.Doc for C++ 还支持更灵活的控制,例如导出指定页面、调整图片压缩、嵌入字体等。

将指定页面转换为 OFD

有些场景下,你只需要转换文档中的部分页面。以下示例演示了如何提取指定页面并保存为单独的 OFD 文件。

#include "Spire.Doc.o.h"
#include <iostream>

using namespace Spire::Doc;

int main()
{
    // 使用智能指针管理内存
    intrusive_ptr<Document> document = new Document();

    try {
        document->LoadFromFile(L"C:\\Users\\Desktop\\报告.docx");

        // 提取第 2 页(索引从 0 开始,所以第 2 页索引为 1)
        intrusive_ptr<Document> newDoc = document->ExtractPages(1, 1);

        newDoc->SaveToFile(L"C:\\Users\\Desktop\\报告_Partial.ofd", FileFormat::OFD);

        document->Close();
        newDoc->Close();

        std::cout << "第2页转换成功!" << std::endl;
    }
    catch (const std::exception& ex) {
        std::cerr << "错误: " << ex.what() << std::endl;
        return -1;
    }

    return 0;
}

Word 指定页面转换为 OFD

核心代码说明:

  • intrusive_ptr 是智能指针,可自动管理内存,防止内存泄漏并简化代码。
  • ExtractPages(1, 1):提取第 2 页(索引从 0 开始)。

自定义 OFD 转换设置

在一些场景中,你可能希望控制输出文件大小或字体显示效果,例如:

#include "Spire.Doc.o.h"
#include <iostream>

using namespace Spire::Doc;

int main()
{
    intrusive_ptr<Document> document = new Document();

    try {
        document->LoadFromFile(L"C:\\Users\\Desktop\\手册.docx");

        // 设置图片压缩质量 (0-100)
        document->SetJPEGQuality(75);

        // 设置嵌入文档中使用的所有字体
        intrusive_ptr<ToPdfParameterList> parameters = new ToPdfParameterList();
        parameters->SetIsEmbeddedAllFonts(true);

        document->SaveToFile(L"C:\\Users\\Desktop\\手册.ofd", FileFormat::OFD);
        document->Close();

        std::cout << "自定义转换成功!" << std::endl;
    }
    catch (const std::exception& ex) {
        std::cerr << "错误: " << ex.what() << std::endl;
        return -1;
    }
    return 0;
}

C++:自定义 OFD 转换

核心代码说明:

  • SetJPEGQuality(75) 设置图片压缩质量(0–100)。
  • SetIsEmbeddedAllFonts(true):嵌入所有字体,避免显示异常。

常见问题 (FAQs)

问题1:需要安装 Microsoft Word 吗?

答:不需要。Spire.Doc for C++ 独立运行,无需依赖 Office。

问题2:如何只转换部分页面?

答:如上文所述,你可以使用 document->ExtractPages() 提取指定页面后再转换,或者将内容复制到新文档中处理。

问题3:为什么转换后的 OFD 字体显示不一致?

答:这通常是因为系统缺少相应字体。建议在保存前调用 SetIsEmbeddedAllFonts(true) 启用字体嵌入。

问题4:如何减小生成的 OFD 文件体积?

答:可以通过 SetJPEGQuality(75) 开启图片压缩或降低图片质量来减小文件大小。

问题5:OFD 和 PDF 有什么区别?

答:OFD 是中国国家标准,主要用于政务系统。PDF 是国际标准。两者都支持固定版式,但 OFD 在中国政务和官方场景中更常见。

结语

对于需要满足中国国家标准、实现可靠文档共享或存档的应用程序来说,实现 C++ Word 转 OFD 至关重要。借助 Spire.Doc for C++,开发者无需依赖 Office 自动化即可完成高效转换。无论是全文转换、指定页面提取还是自定义转换参数设置,该库都提供了一个实用且强大的 C++ 文档处理方案。

如需了解更多文档转换和操作功能,请查阅官方文档。

C# 中添加和编辑 Excel VBA 宏的教程

VBA 宏为 Excel 内自动化数据处理、格式化和业务逻辑提供了便捷的方式,特别适用于重复性或基于规则的任务。然而,当需要大规模生成、更新或维护文件时,手动在 Excel 内部管理宏会变得效率低下。

对于.NET 开发者而言,更具扩展性的方法是使用 C# 以编程方式向 Excel 添加宏。通过在 C# 中处理 Excel VBA,可以实现跨多个文件编写 Excel 宏、读取现有宏代码以及修改 VBA 逻辑,而无需打开 Excel 并手动编辑 VBA 代码。

本文将介绍如何使用 C# 添加、读取、编辑和删除 Excel VBA 宏,以实现自动化工作流。此方案使用 Spire.XLS for .NET 直接操作嵌入在 Excel 文件中的 VBA 项目,而无需安装 Microsoft Excel。

快速导航


理解 Excel VBA 宏及如何使用 C# 处理宏

Excel 宏本质上是存储在 workbook 中的 VBA(Visual Basic for Applications)代码。在启用宏的文件(如 .xlsm)中,这些代码被组织在 VBA 项目 中,包含模块和过程。

在实践中,大多数编程宏操作都专注于标准模块,它们是 VBA 过程(Sub 或 Function)的简单容器。使用 C# 时,通常不会与 Excel UI 元素(如按钮或事件)交互。相反,您直接创建模块并注入 VBA 代码。

从技术角度来看,宏不仅仅是可执行脚本,它们是文件结构的一部分。这意味着只要工具支持 VBA 项目操作,就可以以编程方式访问和修改。

在 C# 中,处理 Excel 宏主要有两种方法:

  • 使用 Microsoft Excel Interop 自动化 Excel
  • 使用独立库(如 Spire.XLS for .NET)直接操作文件

第一种方法依赖已安装的 Excel,通常用于执行宏。第二种方法允许直接在 Excel 文件中创建、读取、编辑和删除 VBA 代码,使其更适合后端服务和自动化工作流。

在以下部分中,我们将重点关注第二种方法,并演示如何完全在 C# 中管理 Excel 宏。


环境设置:安装 Spire.XLS for .NET

在使用 C# 处理 Excel 宏之前,需要安装 Spire.XLS for .NET 来管理 Excel 文件并操作 VBA 项目。

通过 NuGet 安装

可使用 NuGet 包管理器安装 Spire.XLS for .NET:

Install-Package Spire.XLS

或通过.NET CLI:

dotnet add package Spire.XLS

也可以下载 Spire.XLS for .NET 包 并手动添加到项目中。

项目要求

  • .NET Framework、.NET Core、.NET Standard 或 .NET 5+
  • 无需 Microsoft Excel 或其他三方工具
  • 支持 Windows、Linux 和 macOS 环境

该库与运行时无关,可用于任何.NET 支持的环境,包括 Web、桌面和跨平台应用程序。

要导入的命名空间

安装后,在项目中包含以下命名空间:

using Spire.Xls;

设置完成后,就可以开始以编程方式创建、读取和修改 Excel VBA 宏。


如何在 C# 中向 Excel 添加宏(完整示例)

在编写宏代码之前,了解 Spire.XLS 如何处理 Excel 中的 VBA 结构非常重要:

  • Workbook.VbaProject 表示工作簿中嵌入的 VBA 项目
  • IVbaProject.Modules 提供对所有 VBA 模块的访问
  • IVbaModule 表示存储宏代码的单个模块

除了模块之外,VBA 项目还支持项目级配置,例如名称、描述、密码保护和条件编译设置。

以下示例展示如何创建 VBA 项目、配置其属性、添加模块并将宏写入 Excel 文件中。

using Spire.Xls;

// 创建工作簿实例
Workbook workbook = new Workbook();

// 创建 VBA 项目
IVbaProject vbaProject = workbook.VbaProject;

// 配置项目级属性(可选,但在实际场景中很重要)
vbaProject.Name = "报告生成器";
vbaProject.Description = "用于生成季度报告的 VBA 项目";
vbaProject.Password = "securepassword"; // 设置密码保护
vbaProject.LockProjectView = false; // 是否锁定项目视图
vbaProject.HelpFileName = "报告生成器帮助文档.chm";
vbaProject.ConditionalCompilation = "DEBUG_MODE=1"; // 外部编译时标志

// 设置编码方式(936 表示 GBK 编码)
vbaProject.CodePage = 936;

// 添加 VBA 模块
IVbaModule vbaModule = vbaProject.Modules.Add("报告模块", VbaModuleType.Module);

// 编写 VBA 宏代码
vbaModule.SourceCode = @"
Sub 生成季度报告 ()
    Dim ws As Worksheet
    Dim i As Integer
    Dim quarter As String

    Set ws = ActiveSheet
    ws.Cells.Clear

    ' 提示用户输入
    quarter = InputBox(""请输入季度(例如:2026 年第一季度):"",""报告生成"")

    With ws
        .Range(""A1:E1"").Value = Array(""日期"", ""产品"", ""区域"", ""销售额"", ""状态"")

        For i = 1 To 50
            .Cells(i + 1, 1).Value = DateSerial(2026, 1 + Int((i - 1) / 17), 1 + ((i - 1) Mod 17))
            .Cells(i + 1, 2).Value = ""产品 "" & (1 + Int((i - 1) / 10))
            .Cells(i + 1, 3).Value = Choose((i Mod 5) + 1, ""华北"", ""华南"", ""华东"", ""华西"", ""华中"")
            .Cells(i + 1, 4).Value = Round(Rnd() * 10000, 2)
            .Cells(i + 1, 5).Value = IIf(.Cells(i + 1, 4).Value > 5000, ""达标"", ""未达标"")
        Next i

        .Columns(""A:E"").AutoFit

        ' 条件编译示例
        #If DEBUG_MODE = 1 Then
            MsgBox ""调试:已生成报告(50 行)"", vbInformation, ""调试信息""
        #End If
    End With

    MsgBox ""季度报告 "" & quarter & "" 已成功生成!"", vbInformation, ""报告状态""
End Sub";

// 保存为启用宏的 Excel 文件
workbook.SaveToFile("季度报告生成器.xlsm", FileFormat.Version2016);
workbook.Dispose();

以下是生成的 Excel 宏的预览:

使用 C# 向 Excel 添加宏

在此流程中,Spire.XLS 以代码形式构建完整的 VBA 结构,包括模块和宏代码以及项目级元数据和编译行为。这在概念上类似于在编辑器中配置 VBA 项目,但完全自动化且不依赖 Excel。

关键 API 分解

  • workbook.VbaProject

    初始化或检索工作簿内的 VBA 项目。

  • vbaProject.Modules.Add(name, type)

    向项目添加新模块作为宏代码的容器。

  • vbaModule.SourceCode

    定义模块中的完整 VBA 脚本。

  • vbaProject.Description

    为 VBA 项目添加描述。

  • vbaProject.CodePage

    定义 VBA 项目中使用的字符编码。

    注意:写入 VBA 代码前请确保通过该属性正确设置字符编码,否则可能导致 Excel 文件无法打开。

  • vbaProject.Password / LockProjectView

    控制 VBA 项目的访问和可见性。

  • vbaProject.ConditionalCompilation

    在 VBA 中使用 #If 启用编译时标志(例如调试逻辑)。

包含宏的工作簿必须保存在启用宏的格式中,如 .xls、.xlsm、.xltm 或 .xlsb,其他格式(如 .xlsx)不支持 VBA。参考 FileFormat 枚举 以在保存时正确设置输出格式。


在 C# 中读取、编辑和删除 Excel 宏

对于存在 VBA 宏的项目,可以使用 Spire.XLS 直接检查和管理其内容,包括模块代码和项目级元数据。

在实际场景中,这样的能力可以分析现有宏、更新业务逻辑、实施安全设置或删除遗留 VBA 代码,而无需打开 Excel。无论是维护自动化报告系统还是处理第三方 Excel 文件,完全控制 VBA 项目都是必不可少的。

读取宏代码和项目级元数据

要从 Excel 文件读取宏,请加载工作簿并遍历 VBA 项目中的所有模块。

using Spire.Xls;

// 创建工作簿实例并加载文件
Workbook workbook = new Workbook();
workbook.LoadFromFile("季度报告生成器.xlsm");

// 获取 VBA 项目
IVbaProject vbaProject = workbook.VbaProject;

// 构建宏信息字符串
string macroInfo = "VBA 项目名称:" + vbaProject.Name + Environment.NewLine;
macroInfo += "代码页:" + vbaProject.CodePage + Environment.NewLine;
macroInfo += "是否受保护:" + vbaProject.IsProtected + Environment.NewLine;

// 遍历所有模块
foreach (IVbaModule module in vbaProject.Modules)
{
    macroInfo += Environment.NewLine + "模块:" + module.Name + Environment.NewLine;
    macroInfo += "源代码:" + Environment.NewLine;
    macroInfo += module.SourceCode + Environment.NewLine;
}

// 输出到控制台
Console.WriteLine(macroInfo);

// 保存到文本文件
System.IO.File.WriteAllText("宏分析.txt", macroInfo);
workbook.Dispose();

以下是控制台输出的预览:

使用 C# 从 Excel 读取宏

在这里,每个模块通过 SourceCode 属性公开其 VBA 代码,而 VBA 项目本身提供元数据,如名称、编码和保护状态。

示例中的 vbaProject.IsProtected 属性指示 VBA 项目是否受保护(锁定查看或编辑)。

编辑宏代码和项目级元数据

如果需要编辑宏代码,可访问特定模块并更新其 SourceCode;如果需要编辑宏项目的元数据,还可以通过 IVbaProject 类的其他属性修改项目级元数据。

需要注意的是,如果 VBA 项目受保护,则在进行此类更改之前必须提供正确的密码。

using Spire.Xls;

// 创建工作簿实例并加载文件
Workbook workbook = new Workbook();
workbook.LoadFromFile("季度报告生成器.xlsm");

// 获取 VBA 项目
IVbaProject vbaProject = workbook.VbaProject;

// 更新宏逻辑
IVbaModule module = vbaProject.Modules["报告模块"];  // 如果名称未知,也可以使用索引
module.SourceCode = module.SourceCode.Replace(
    "\"华北\", \"华南\", \"华东\", \"华西\", \"华中\"",
    "\"华北区\", \"华南区\", \"华东区\", \"华西区\", \"华中区\"");

// 更新项目元数据
// 对于受保护的 VBA 项目,需要密码才能修改项目级元数据
vbaProject.Password = "securepassword";
vbaProject.Description = "已更新报告逻辑,使用全球区域";
vbaProject.Name = "更新后的报告生成器";

// 保存文件
workbook.SaveToFile("更新后的报告生成器.xlsm", FileFormat.Version2016);
workbook.Dispose();

以下是更新后的宏代码预览:

使用 C# 编辑 Excel 中的宏

Spire.XLS 将宏代码视为模块中的可编辑文本,因此更新通常通过修改现有源代码并保存工作簿来完成。

删除宏

要删除宏,请从 VBA 项目中删除模块。这将有效地从工作簿中删除所有宏逻辑。

using Spire.Xls;

// 创建工作簿实例并加载文件
Workbook workbook = new Workbook();
workbook.LoadFromFile("季度报告生成器.xlsm");

// 获取 VBA 项目
IVbaProject vbaProject = workbook.VbaProject;

// 按名称删除特定模块
vbaProject.Modules.Remove("报告生成器");

// 或按索引删除模块
vbaProject.Modules.RemoveAt(0);

// 删除所有模块
vbaProject.Modules.Clear();

// 保存文件
workbook.SaveToFile("清理后的文档.xlsm", FileFormat.Version2016);
workbook.Dispose();

删除所有模块后,工作簿不再包含可执行的 VBA 代码,使其等同于无宏的 Excel 文件。

还可以将启用宏的 Excel 文件转换为 .xlsx从而直接删除所有宏,因为 .xlsm 文件保存为不支持 VBA 代码的 .xlsx 格式时,宏项目和模块代码都会被删除。


处理 Excel 宏时的常见陷阱

文件格式要求

宏需要启用宏的格式,如 .xlsm、.xls 或 .xlsb。.xlsx 格式不支持 VBA 代码。请务必使用适当的扩展名保存文件。

编码问题

VBA 代码使用特定的编码(通常包含简体中文字符时使用 CodePage 936),处理国际字符时,请在写入宏代码之前确保正确设置 CodePage,否则可能导致 Excel 文件无法打开。

常见的 CodePage 值包括:

  • 1252 – 英语/西欧(许多环境中的默认值)
  • 936 – 简体中文(GBK)
  • 950 – 繁体中文(Big5)
  • 932 – 日语(Shift-JIS)
  • 65001 – UTF-8(在 VBA 中支持有限,可能导致兼容性问题)

宏安全设置

Excel 宏安全设置可能会阻止宏运行。分发启用宏的文件时,请考虑添加启用宏的说明或对 VBA 项目进行数字签名。

Excel 版本兼容性

为新版本 Excel 编写的 VBA 代码可能在旧版本中无法正常工作,在目标 Excel 版本上测试宏以确保兼容性。


总结

C# 提供了强大的功能来以编程方式管理 Excel 宏。使用 Spire.XLS for .NET,开发者可以直接通过 C# 在 Excel 文件中添加、读取、编辑和删除 VBA 宏代码,而无需安装 Excel。

这种方法能够直接在开发环境中管理宏代码和项目属性,使其非常适合自动化工作流、后端服务和大规模文档处理。

如需测试宏相关操作能力而移除所有限制,可申请 Spire.XLS for .NET 的免费临时许可。


常见问题解答 (FAQ)

C# 可以在没有 Microsoft Excel 的情况下添加 VBA 宏吗?

可以。Spire.XLS for .NET 等库允许您直接操作 VBA 项目,而无需安装 Excel。

Excel 宏需要什么格式?

宏需要启用宏的格式,如 .xlsm、.xls、.xlsb 或 .xltm(用于模板)。.xlsx 格式不支持 VBA 代码。

我可以使用 C# 编辑 Excel 中的现有宏吗?

可以。您可以访问 VBA 模块并以编程方式修改其源代码,以更新业务逻辑或修复多个文件中的问题。

这种方法适合服务器环境吗?

可以。由于它不依赖 Microsoft Excel,因此非常适合后端服务、自动化系统和基于云的应用程序。

我可以从密码保护的 Excel 文件中读取宏代码吗?

这取决于应用于 Excel 文件的保护类型。

  • 工作簿保护(文件密码):加载文件时必须提供密码(例如通过 Workbook.OpenPassword)才能访问其内容。
  • VBA 项目保护:如果锁定了项目视图,则读取和编辑 VBA 代码和项目均需项目密码;如果仅设置密码未锁定项目视图,则可以读取和编辑 VBA 代码,但修改项目级属性(如名称或描述)需要项目密码。

此外,如果 Excel 文件具有数字签名,则对文档的任何修改(包括宏或元数据)都将使签名失效。

Spire.PDF 12.4.0 现已正式发布。该版本优化了 PDF 合并功能的内存占用并修复了一些在操作 PDF 文档时出现的问题。详情请查阅以下内容。

优化:

问题修复:


获取Spire.PDF 12.4.0,请点击:

https://www.e-iceblue.cn/Downloads/Spire-PDF-NET.html