PDF 的属性面板里除了标题、作者这几个固定字段,还留了一栏自定义属性:属性名和值都由使用者自己命名,部门、保密级别、来源模板这类内部标记就放在这里。合同、投标书、项目文档常靠它携带这些信息,可阅读器只能一个个手填——给一批文档补标记,或者核对某份文档带了哪些标记,都做不了;把文件传到服务端批处理,又意味着内容离开了用户的设备。
本文介绍用 Spire.PDF for JavaScript 添加、获取和删除 PDF 的自定义文档属性。它基于 WebAssembly 在浏览器端直接加载、修改与保存 PDF 文档,全过程在本地完成,通过虚拟文件系统(VFS)读写文件,无需后端配合。
本文介绍三个核心功能点:
有关安装和项目配置,请参考 React 项目中集成 Spire.PDF for JavaScript。以下示例默认已安装 Spire.PDF 并完成 WebAssembly 模块初始化。示例以一份不带自定义属性的 PDF 为输入,后两节直接读取第一节生成的 已添加自定义属性的文档.pdf,运行前请先执行第一节的代码。
添加自定义文档属性
Spire.PDF for JavaScript 提供 DocumentInformation.SetCustomProperty(),用于写入自定义文档属性:属性名自定,值按字符串保存,同名键直接覆盖。
function App() {
const addCustomProperties = async () => {
// 获取 Spire.PDF WASM 模块
const pdfModule = window.wasmModule?.spirepdf;
// 检查模块是否就绪
if (!pdfModule) {
alert('Spire.PDF is not ready yet');
return;
}
// 将待处理的 PDF 文件载入 VFS
const inputFileName = '产品介绍.pdf';
await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}/data/`);
// 创建 PdfDocument 对象并加载 PDF 文档
const doc = new pdfModule.PdfDocument();
doc.LoadFromFile(inputFileName);
// 写入自定义文档属性,属性名自定
doc.DocumentInformation.SetCustomProperty('Department', '研发部');
doc.DocumentInformation.SetCustomProperty('SecrecyLevel', '内部');
doc.DocumentInformation.SetCustomProperty('Company', '冰蓝科技');
const outputFileName = '已添加自定义属性的文档.pdf';
doc.SaveToFile(outputFileName);
doc.Close();
// 从 VFS 读取生成的文件,触发下载
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/pdf' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>添加自定义文档属性</h1>
<button onClick={addCustomProperties}>
开始添加
</button>
</div>
);
}
export default App;
产物在阅读器属性面板的自定义栏里多了 Department、SecrecyLevel 与 Company 三个属性

获取自定义文档属性
读取走同一个 DocumentInformation:GetCustomProperty() 按属性名取值,键不存在时返回 null 而不是抛异常。
function App() {
const getCustomProperties = async () => {
// 获取 Spire.PDF WASM 模块
const pdfModule = window.wasmModule?.spirepdf;
// 检查模块是否就绪
if (!pdfModule) {
alert('Spire.PDF is not ready yet');
return;
}
// 读取上一节生成的文档,它已在 VFS 中
const inputFileName = '已添加自定义属性的文档.pdf';
// 创建 PdfDocument 对象并加载 PDF 文档
const doc = new pdfModule.PdfDocument();
doc.LoadFromFile(inputFileName);
const info = doc.DocumentInformation;
// 按属性名逐个取值,键不存在时返回 null
const lines = [
`Department: ${info.GetCustomProperty('Department')}`,
`SecrecyLevel: ${info.GetCustomProperty('SecrecyLevel')}`,
`Company: ${info.GetCustomProperty('Company')}`,
`Owner: ${info.GetCustomProperty('Owner') ?? '(未设置)'}`,
];
// 把结果写成文本文件
const outputFileName = '自定义属性.txt';
window.dotnetRuntime.Module.FS.writeFile(outputFileName, lines.join('\n'));
doc.Close();
// 从 VFS 读取生成的文件,触发下载
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>获取自定义文档属性</h1>
<button onClick={getCustomProperties}>
开始获取
</button>
</div>
);
}
export default App;
导出的文本文件,逐行列出读到的属性值:

删除自定义文档属性
删除用 RemoveCustomProperty(),按属性名移除单个键。
function App() {
const removeCustomProperties = async () => {
// 获取 Spire.PDF WASM 模块
const pdfModule = window.wasmModule?.spirepdf;
// 检查模块是否就绪
if (!pdfModule) {
alert('Spire.PDF is not ready yet');
return;
}
// 读取上一节生成的文档,它已在 VFS 中
const inputFileName = '已添加自定义属性的文档.pdf';
// 创建 PdfDocument 对象并加载 PDF 文档
const doc = new pdfModule.PdfDocument();
doc.LoadFromFile(inputFileName);
// 按属性名移除单个自定义属性,其余属性不受影响
doc.DocumentInformation.RemoveCustomProperty('SecrecyLevel');
doc.DocumentInformation.RemoveCustomProperty('Company');
const outputFileName = '已删除自定义属性的文档.pdf';
doc.SaveToFile(outputFileName);
doc.Close();
// 从 VFS 读取生成的文件,触发下载
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/pdf' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>删除自定义文档属性</h1>
<button onClick={removeCustomProperties}>
开始删除
</button>
</div>
);
}
export default App;
产物只剩 Department 一个自定义属性,被删掉的两个已从面板中消失:

常见问题
自定义属性和标题、作者这些字段有什么区别
原因:PDF 规范固定了 Title、Author、Subject、Keywords、Creator、Producer 这几个标准字段,规范之外的键值对都算自定义属性。阅读器把它们分两栏显示:标准字段在上半部分,自定义属性单独一栏。
解决:两类字段用两套写法。标题、作者这类标准字段赋给同名属性,业务标记走自定义属性:
// 标准字段
doc.DocumentInformation.Title = '2026 年度产品介绍';
doc.DocumentInformation.Author = '市场部';
// 自定义属性
doc.DocumentInformation.SetCustomProperty('Department', '市场部');
读的时候同样分开:标准字段用 info.Title 这样的属性取值,自定义属性只能用 info.GetCustomProperty('Department')。
删除之后读回是什么,键名写错了怎么办
原因:GetCustomProperty() 对不存在的键一律返回 null,删掉的和从来没有写过的看不出区别;RemoveCustomProperty() 传入不存在的键既不报错,也不改动文档。
解决:删完重新打开产物复核一次,返回 null 表示已移除,同时确认其他属性仍读得到。键名传错没有副作用,按正确的键名再删一次即可:
doc.DocumentInformation.RemoveCustomProperty('SecrecyLevel');
doc.SaveToFile(outputFileName);
// 重新打开产物复核:null 即已删除
const check = new pdfModule.PdfDocument();
check.LoadFromFile(outputFileName);
console.log(check.DocumentInformation.GetCustomProperty('SecrecyLevel'));
获取免费许可证
如果您希望删除结果文档中的评估消息,或者摆脱功能限制,请该Email地址已收到反垃圾邮件插件保护。要显示它您需要在浏览器中启用JavaScript。获取有效期 30 天的临时许可证。







