Spire.PDF for Python 11.12.1 现已正式发布,本次更新带来了多项新功能,包括为数字签名添加时间戳、配置 PDF 转 Excel 时的多种布局选项、以及在 PDF 转 Markdown 时忽略图像。同时,本版本还修复了两个已知问题。更多详情如下。
新功能:
doc = PdfDocument ()
doc. LoadFromFile (inputFile)
# Create a digital signature
signature = Security_PdfSignature (doc, doc.Pages.get_Item(0), inputFile_pfx,"08100601", "signature")
signature.SignDetailsFont = PdfFont(PdfFontFamily.Courier,8.0)
# Set the bounds of the signature box
signature.Bounds = RectangleF(PointF(90.0, 550.0), SizeF (180.0, 90.0))
# Configure signature appearance and details
signature.NameLabel = "Digitally signed by:Gary"
signature.LocationInfoLabel ="Location:"
signature.LocationInfo = "CN"
signature.ReasonLabel = "Reaseon:"
signature.Reason = "Ensure authenticity"
signature.ContactInfoLabel = "Contact Number:"
signature.ContactInfo = "028-81705109"
# Set document permissions
signature.DocumentPermissions = PdfCertificationFlags.ForbidChanges.value
# Set graphic mode for the signature
signature.GraphicsMode = Security_GraphicMode.SignImageAndSignDetail
# Set the signature image
signature.SignImageSource = PdfImage.FromFile(inputImage)
#When setting “none", the Image and Detail are distributed on both sides, when setting “Stretch", the image extends to the entire signatu
signature.SignImageLayout = SignImageLayout.none
url = "https://freetsa.org/tsr"
signature.ConfigureTimestamp(url)
signature.ConfigureHttpOCSP (None, None)
signature.Certificated = True
doc.SaveToFile(outputFile)
doc.Close()
pdf = PdfDocument()
pdf.LoadFromFile(inputFile)
textOption = XlsxTextLayoutOptions(True, False, False)
pdf.ConvertOptions.SetPdfToXlsxOptions(textOption)
pdf.SaveToFile(outputFile, FileFormat.XLSX)
pdf.Dispose()
pdf = PdfDocument()
pdf.LoadFromFile(inputFile)
lineOption = XlsxLineLayoutOptions(False,False,False,False)
pdf.ConvertOptions.SetPdfToXlsxOptions(lineOption)
pdf.SaveToFile(outputFile, FileFormat.XLSX)
pdf.Dispose()
# Load the PDF document from the specified input file path
pdf.LoadFromFile(inputFile)
# Set the XlsxSpecialTableLayoutOptions as the conversion options for PDF to XLSX conversion
options = XlsxSpecialTableLayoutOptions(False, False, False)
# Save the PDF document as an Excel file using the specified format and options
pdf.SaveToFile(outputFile, FileFormat.XLSX)
pdf = PdfDocument ()
pdf. LoadFromFile (inputFile)
ofdOptions = OfdOptions()
ofdOptions.UseTempFileStorage = True
pdf.ConvertOptions.SetPdfToOfdOptions(ofdOptions)
pdf.SaveToFile(outputFile,FileFormat.OFD)
# Create an instance of PdfToMarkdownConverter with the input PDF file
converter = PdfToMarkdownConverter(inputFile)
# Configure the converter to skip processing images in the PDF
converter.MarkdownOptions.IgnoreImage = True
# Convert the PDF content to Markdown format and save to the output file
converter.ConvertToMarkdown(outputFile)
converter = PdfToSvgConverter(inputFile)
converter.SvgOptions.ScaleX = 1.0
converter.SvgOptions.ScaleY = 1.0
converter.Convert(outputFile)
问题修复:
Spire.OCR for Python 1.9.13 已发布。本次版本先进行了若干依赖与平台方面的调整,随后增强了错误处理与 OCR 识别能力。详细更新如下:
调整:
优化:
1.支持识别旋转图片。
configureOptions.AutoRotate = True
2.支持按图像中文字的原始位置顺序输出识别结果。
visualText = VisualTextAligner(scanner.Text)
text = visualText.ToString()
随着企业数字化转型加速,纯前端文档处理已成为 Web 应用的核心需求之一。Angular 作为成熟的企业级前端框架,以其强类型校验、组件化架构和高效的状态管理能力,广泛应用于 OA 系统、文档平台、教育管理系统等复杂场景。
本文将详细介绍如何将 Spire.OfficeJS 集成到 Angular 项目中,实现本地文件上传、在线编辑、格式转换与下载等核心功能。
内容概览
Spire.OfficeJS 是一款企业级在线文档处理与编辑解决方案,包含 Spire.WordJS、Spire.ExcelJS、Spire.PresentationJS、Spire.PDFJS 四个模块。该产品无需依赖本地 Office 软件,也无需安装任何插件,即可在浏览器中实现 Word、Excel、PPT、PDF 等主流格式文件的在线预览、实时编辑、批注、与格式转换等功能,同时具备云原生、跨平台、高安全性等企业级特性。
核心优势包括:
在开始集成前,需先完成以下准备工作,确保开发环境符合要求。
node -v
npm -v

Angular CLI 是快速构建 Angular 项目的工具,通过 npm 全局安装:
npm install -g @angular/cli
ng version

通过 Angular CLI 创建基础项目,为后续集成 Spire.OfficeJS 搭建框架。
F:\angular):ng new spireOfficeJS --skip-git


使用 VS Code 打开项目,在终端执行以下命令启动开发服务器:
npm run start
浏览器打开 http://localhost:4200/,若显示 “Hello, spireOfficeJS” 及 Angular 默认页面,则项目初始化成功。
下载 Spire.OfficeJS 产品包,解压后需将产品包中的核心 Web 资源复制到 Angular 项目的静态目录,确保编辑器脚本可被访问。具体操作如下:
public/spire.cloud)。public/spire.cloud/ 目录下。public/spire.cloud/web/editors/spireapi/SpireCloudEditor.js
⚠️ 注意:此路径需与后续配置的 office-js.ts 中路径完全一致,否则编辑器无法加载
用于同步文件上传数据(文件对象 + 二进制数据),确保编辑器组件可访问。
(1)安装依赖
使用 NgRx Signals 管理文件数据,在VS Code 终端执行以下安装命令:
npm install @ngrx/effects @ngrx/signals @ngrx/store
(2)创建状态管理 Store
import { signalStore, withState, withMethods, patchState } from '@ngrx/signals';
// 定义文件状态接口
interface FileState {
file: File | null; // 上传的文件对象
fileUint8Data: Uint8Array | null; // 文件二进制数据
}
// 初始状态
const initialState: FileState = {
file: null,
fileUint8Data: null,
};
// 创建全局 Store
export const fileStore = signalStore(
{ providedIn: 'root' },
withState(initialState),
withMethods((store) => ({
// 更新文件对象
setFileData(data: File | null): void {
patchState(store, { file: data });
},
// 更新文件二进制数据
setFileUint8Data(uint8Data: Uint8Array | null): void {
patchState(store, { fileUint8Data: uint8Data });
}
}))
);
(1)创建组件
在终端执行以下命令,创建文件上传组件和编辑器集成组件:
ng g c spire/uploadFile
ng g c spire/officeJS
执行完成后,src/app/spire/ 目录下会生成 upload-file/ 和 office-js/ 两个组件文件夹。

(2)文件上传组件(upload-file)配置
上传组件负责接收用户拖放或选择的文件,转换为二进制格式后存储到全局状态,再跳转至编辑器页面。
upload-file.css)::host {
display: block;
min-height: 100vh;
}
.upload-main {
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
background-color: #f5f5f5;
}
.upload-container {
width: 80%;
max-width: 600px;
padding: 40px;
background-color: white;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
text-align: center;
}
.drop-area {
border: 2px dashed #ccc;
border-radius: 6px;
padding: 40px;
margin-bottom: 20px;
transition: all 0.3s;
}
.drop-area.highlight {
border-color: #4CAF50;
background-color: #f0fff0;
}
button {
background-color: #4CAF50;
color: white;
border: none;
padding: 10px 20px;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
margin-top: 10px;
}
button:hover {
background-color: #45a049;
}
#fileInput {
display: none; /* 隐藏原生文件选择框 */
}
upload-file.html),支持拖放上传和点击选择文件两种方式:<main class="upload-main">
<div class="upload-container">
<h2>拖放文件上传</h2>
<div class="drop-area" id="dropArea">
<p>拖放文件到浏览器</p>
<p>或</p>
<button id="browseBtn" #browseBtn (click)="handleButtonClick($event)">选择文件</button>
<input type="file" id="fileInput" #fileInput (change)="handleDrop($event)">
</div>
</div>
</main>
upload-file.ts),处理文件拖放、选择、二进制转换与状态储存:import { ViewChild, ElementRef, Component, AfterViewInit, inject } from '@angular/core';
import { Router } from '@angular/router';
import { fileStore } from '../../store/index';
@Component({
selector: 'app-upload-file',
imports: [],
templateUrl: './upload-file.html',
styleUrl: './upload-file.css',
})
export class UploadFile implements AfterViewInit {
constructor(private router: Router) { }
// 注入状态管理 Store
store = inject(fileStore);
// 绑定 HTML 元素
@ViewChild('browseBtn') browseBtn!: ElementRef<HTMLButtonElement>;
@ViewChild('fileInput') fileInput!: ElementRef<HTMLInputElement>;
file: File | null = null; // 上传的文件对象
fileUint8Data: Uint8Array | null = null; // 文件二进制数据
// 组件视图初始化完成后执行
ngAfterViewInit() {
this.init();
}
// 初始化拖放事件监听
init() {
// 阻止浏览器默认拖放行为
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
document.addEventListener(eventName, this.preventDefaults, false);
});
// 监听文件拖放事件
document.addEventListener('drop', (e) => {
this.handleDrop.call(this, e);
}, false);
}
// 阻止默认事件
preventDefaults(e: Event) {
e.preventDefault();
e.stopPropagation();
}
// 点击“选择文件”按钮触发原生文件选择框
handleButtonClick(e: Event) {
e.preventDefault();
this.fileInput.nativeElement.click();
}
// 处理文件拖放/选择事件
async handleDrop(e: any) {
// 获取文件对象(拖放或点击选择)
if (e.target && e.target.files) {
this.file = e.target.files[0];
} else if (e.dataTransfer && e.dataTransfer.files) {
this.file = e.dataTransfer.files[0];
}
// 转换文件为 Uint8Array 二进制格式
this.fileUint8Data = await this.handleFile(this.file) as Uint8Array;
// 更新状态到 Store(供编辑器组件使用)
this.store.setFileData(this.file);
this.store.setFileUint8Data(this.fileUint8Data);
// 跳转到编辑器页面
this.openDocument();
}
// 将文件转换为 Uint8Array 二进制数据
handleFile(file: any) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
const arrayBuffer = reader.result as ArrayBuffer;
const uint8Array = new Uint8Array(arrayBuffer);
resolve(uint8Array);
};
reader.onerror = (error) => reject(error);
reader.readAsArrayBuffer(file); // 以 ArrayBuffer 格式读取文件
});
}
// 跳转到编辑器页面(路由导航)
openDocument() {
this.router.navigate(['spire']);
}
}
(3) 编辑器组件(office-js)配置
编辑器组件负责加载 Spire.OfficeJS 脚本、初始化编辑器实例、配置编辑权限与功能,是核心交互模块。
office-js.html):<div class="form">
<div id="iframeEditor">
</div>
</div>
office-js.ts):import { Component, AfterViewInit, inject } from '@angular/core';
import { Router } from '@angular/router';
import { fileStore } from '../../store/index';
// 声明 SpireCloudEditor 全局变量(来自产品脚本)
declare const SpireCloudEditor: any;
@Component({
selector: 'app-office-js',
imports: [],
templateUrl: './office-js.html',
styleUrl: './office-js.css',
})
export class OfficeJS implements AfterViewInit {
constructor(private router: Router) { };
// 注入状态管理 Store
store = inject(fileStore);
// 从 Store 获取文件数据
file = this.store.file() as File;
fileUint8Data = this.store.fileUint8Data() as Uint8Array;
originUrl = window.location.origin; // 当前项目域名
Editor: any; // 编辑器实例
config: any; // 编辑器配置
Api: any; // 编辑器 API
// 组件视图初始化完成后执行
ngAfterViewInit() {
this.init();
}
// 初始化校验(无文件则跳回上传页)
init() {
if (!this.file) {
this.router.navigate(['']); // 跳转到上传页面
return;
}
this.loadSrcipt(); // 加载编辑器脚本
}
// 动态加载 SpireCloudEditor.js 脚本
loadSrcipt() {
const script = document.createElement('script');
// 脚本路径需与静态资源部署路径一致
script.setAttribute('src', '/spire.cloud/web/editors/spireapi/SpireCloudEditor.js');
script.onload = () => this.initEditor(); // 脚本加载完成后初始化编辑器
document.head.appendChild(script);
}
// 初始化编辑器配置与实例
initEditor() {
const iframeId = 'iframeEditor'; // 与模板文件中容器 ID 一致
this.initConfig(); // 配置编辑器参数
// 创建编辑器实例
this.Editor = new SpireCloudEditor.OpenApi(iframeId, this.config);
this.Api = this.Editor.GetOpenApi(); // 获取编辑器 API (用于扩展功能)
this.OnWindowReSize(); // 适配窗口大小
}
// 编辑器核心配置(文件信息 + 用户权限 + 编辑器行为)
initConfig() {
this.config = {
"fileAttrs": {
"fileInfo": {
"name": this.file.name, // 文件名
"ext": this.getFileExtension(), // 文件后缀
"primary": String(new Date().getTime()), // 唯一标识(时间戳)
"creator": "",
"createTime": ""
},
"sourceUrl": `${this.originUrl}/files/__ffff_192.168.3.121/${this.file.name}`,
"createUrl": `${this.originUrl}/open`,
"mergeFolderUrl": "",
"fileChoiceUrl": "",
"templates": {}
},
"user": {
"id": "uid-1",
"name": "Jonn",
"canSave": true, // 允许保存文件
},
"editorAttrs": {
"editorMode": this.file.name.endsWith('.pdf') ? 'view' : "edit", // PDF 默认为预览模式
"editorWidth": "100%", // 编辑器宽度
"editorHeight": "100%", // 编辑器高度
"editorType": "document", // 编辑器类型(文档)
"platform": "desktop", // 平台类型(桌面端)
"viewLanguage": "zh", // 界面语言(中文)
"isReadOnly": false, // 非只读模式
"canChat": true, // 启用聊天功能
"canComment": true, // 启用批注功能
"canReview": true, // 启用审阅功能
"canDownload": true, // 允许下载文件
"canEdit": this.file.name.endsWith('.pdf') ? false : true, // PDF 禁止编辑
"canForcesave": true, // 允许强制保存
"embedded": {
"saveUrl": "",
"embedUrl": "",
"shareUrl": "",
"toolbarDocked": "top" // 工具栏置顶
},
// 启用 WebAssembly 加速(提升编辑和转换性能)
"useWebAssemblyDoc": true,
"useWebAssemblyExcel": true,
"useWebAssemblyPpt": true,
"useWebAssemblyPdf": true,
// 许可证配置(若有许可证,填写对应密钥)
"spireDocJsLicense": "",
"spireXlsJsLicense": "",
"spirePresentationJsLicense": "",
"spirePdfJsLicense": "",
"serverless": {
"useServerless": true,
"baseUrl": this.originUrl,
"fileData": this.fileUint8Data, // 核心:传入文件二进制数据
},
"events": {
"onSave": this.onFileSave // 保存回调事件
},
"plugins": {
"pluginsData": []
}
}
};
}
// 窗口大小适配
OnWindowReSize() {
const wrapEl = document.getElementsByClassName("form") as any;
if (wrapEl.length) {
wrapEl[0].style.height = window.innerHeight + "px";
window.scrollTo(0, -1);
}
}
// 获取文件后缀名
getFileExtension() {
const filename = this.file.name.split(/[\\/]/).pop() as String;
return filename.substring(filename.lastIndexOf('.') + 1).toLowerCase() || '';
}
// 自定义保存逻辑(可根据需求扩展)
onFileSave(data: any) {
console.log('保存的数据:', data);
}
}
为上传页面和编辑器页面配置路由,实现页面跳转。
app.routes.ts),配置上传页和编辑器页的路由:import { Routes } from '@angular/router';
import { UploadFile } from './spire/upload-file/upload-file';
import { OfficeJS } from './spire/office-js/office-js';
export const routes: Routes = [
{ path: '', component: UploadFile }, // 默认路由:文件上传页
{ path: 'spire', component: OfficeJS } // 编辑器路由:/spire
];
app.config.ts),确保路由配置生效:import { ApplicationConfig, provideBrowserGlobalErrorListeners, provideZoneChangeDetection } from '@angular/core';
import { provideRouter } from '@angular/router';
import { routes } from './app.routes';
export const appConfig: ApplicationConfig = {
providers: [
provideBrowserGlobalErrorListeners(),
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(routes), // 注入路由
]
};
app.html),确保页面正确渲染:<main class="app-main">
<router-outlet /> <!-- 路由出口:渲染当前路由对应的组件 -->
</main>
保存所有修改后,在项目根目录(F:\angular\spireOfficeJS)执行以下命令,重启开发服务器:
npm run start



Q1:编辑器加载失败,页面空白?
SpireCloudEditor.js 路径与 office-js.ts 中 script.src 一致Q2:安装 NgRx 依赖时提示 peer dependency 冲突?
npm install @ngrx/signals @ngrx/store --legacy-peer-deps
Q3:启动项目时,浏览器控制台报错,提示找不到 zone.js 模块?
zone.js 依赖),但 Spire.OfficeJS 依赖 zone.js 处理异步事件。npm install zone.js --savesrc/main.ts,在文件顶部添加导入:import 'zone.js';下载 Angular 集成 Spire.OfficeJS 的完整示例项目,包含所有配置文件与代码,直接运行即可体验功能。
如需去除水印或解锁全部功能,可联系我们获取 30 天临时许可证。
Spire.Doc 13.12.2 现已正式发布。本版本支持双行合一功能,强化了 Word 转 PDF 的效果。同时,支持设置段落文本的“Horizontal in Vertical”属性、Markdown 转 Docx 时从模板文档复制样式,以及获取样式更改修订。更多详情如下。
新功能:
Document doc = new Document();
Section section = doc.AddSection();
Spire.Doc.Documents.Paragraph paragraph = section.AddParagraph();
Spire.Doc.Fields.TextRange farEastLayout = paragraph.AppendText("test");
FarEastLayout style = new FarEastLayout();
style.Vertical = true;
farEastLayout.CharacterFormat.FarEastLayout = style;
doc.SaveToFile(outputFile, FileFormat.Docx);
doc.Close();
//Load template documents with existing styles
Document temple = new Document();
temple.LoadFromFile("http://cdn.e-iceblue.cn/temple.docx");
//Load markdown file
Document doc = new Document();
doc = new Document(@"Doc.md");
//Copy styles from template documents
doc.CopyStylesFromTemplate(temple);
//Save
doc.SaveToFile(@"Doc.docx", Spire.Doc.FileFormat.Docx2016);
Document doc = new Document();
doc.LoadFromFile(inputFile);
RevisionInfoCollection revisionInfoCollection = doc.GetRevisionInfos();
StringBuilder sb = new StringBuilder();
foreach (RevisionInfo revisionInfo in revisionInfoCollection)
{
if (revisionInfo.RevisionType == RevisionType.FormatChange)
{
if (revisionInfo.OwnerObject is Spire.Doc.Fields.TextRange)
{
TextRange range = (TextRange)revisionInfo.OwnerObject;
sb.AppendLine("TextRange:" + range.Text + "\r\n");
doc.RevisionsView = RevisionsView.Original;
sb.AppendLine("Original style:" + "isBold:" + range.CharacterFormat.Bold + ";" + "TextColor:" + range.CharacterFormat.TextColor + ";HighlightColor:" + range.CharacterFormat.HighlightColor + ";FontName:" + range.CharacterFormat.FontName + ";UnderlineStyle:" + range.CharacterFormat.UnderlineStyle + "\r\n");
doc.RevisionsView = RevisionsView.Final;
sb.AppendLine("Final style:" + "isBold:" + range.CharacterFormat.Bold + ";" + "TextColor:" + range.CharacterFormat.TextColor + ";HighlightColor:" + range.CharacterFormat.HighlightColor + ";FontName:" + range.CharacterFormat.FontName + ";UnderlineStyle:" + range.CharacterFormat.UnderlineStyle + "\r\n");
}
}
}
File.WriteAllText(outputFile, sb.ToString());
doc.Close();
问题修复:
Spire.Doc for Java 13.12.2 现已正式发布。该版本支持检测写保护密码是否正确,此外还修复了一些在转换 Word 到 PDF、HTML 到 Word,和替换书签时出现的问题。详情如下。
新功能:
Boolean protectionPassword = document.checkWriteProtectionPassword("password");
问题修复:
Spire.Doc for Python 13.12.0 已正式发布,该版本带来了多项重要的 API 增强功能,包括针对文档元素的精细化格式控制、图表配置选项的强化以及文档比较功能的升级。此外,还对辅助功能进行了增强,重构了列表系统,并对整体 API 进行了全面优化,从而显著提升了用户体验。具体更新内容如下。
调整:
| 类名 | 新功能 | 功能说明 |
|---|---|---|
| Paragraph | GetText | 获取段落的文本内容 |
| Table | SetBorders, ClearBorders |
设置表格边框样式;清除表格的所有边框格式 |
| CellFormat | ClearFormatting | 清除单元格的所有格式 |
| Borders | ClearFormatting, IsShadow |
清除边框格式设置;控制边框是否显示阴影效果 |
| RowFormat | ClearBackground, Height | 清除行背景色;设置行高 |
| StyleCollection | Add (重载) | 增加用于创建样式的重载方法 |
| PreferredWidth | FromPercent, FromPoints |
支持使用百分比或磅值定义宽度 |
| CharacterFormat | LocaleIdBi | 支持双向文本的区域设置 |
| Frame | IsFrame | 判断对象是否为 Frame |
| OfficeMath | ToLaTexMathCode, FromOMMLCode |
将公式对象转换为 LaTeX 数学代码;从 OMML 字符串创建公式对象 |
| 类名 | 新功能 | 功能说明 |
|---|---|---|
| Chart 及其子对象(包括 ChartAxis、ChartSeries、ChartDataLabelCollection、ChartLegend、ChartTitle 等) | 多个属性和方法 | 支持坐标轴配置、数据标签管理、图例/标题格式设置等 |
| 类名 | 新功能 | 功能说明 |
|---|---|---|
| CompareOptions | IgnoreTable, IgnoreHeadersAndFooters | 文档比较时忽略表格内容以及页眉/页脚 |
| DifferRevisions | MoveToRevisions, MoveFromRevisions | 获取“移入”和“移出”类型的修订内容 |
| StructureDocumentTag*(包括 Cell / Inline / Row) | RemoveSelfOnly | 仅删除内容控件本身,保留内部内容 |
| 类名 | 新功能 | 功能说明 |
|---|---|---|
| ToPdfParameterList | PdfImageCompression、DigitalSignatureInfo | 保存到PDF时配置图像压缩以及数字签名信息 |
| MarkdownExportOptions、ListReferences | MoveToRevisions, MoveFromRevisions | 支持 Markdown 导出选项及列表引用 |
| 类名 | 新功能 | 功能说明 |
|---|---|---|
| ListFormat | ApplyStyle, ApplyListRef | 支持直接应用列表引用及快速样式 |
| ListLevel | Equals, CreatePictureBullet, DeletePictureBullet, PictureBullet | 支持图片项目符号管理及列表级别比较 |
| ListStyle | ListRef, BaseStyle | 支持列表引用及基础样式配置 |
| Document | ListReferences | 获取文档中的列表引用集合 |
Spire.PDF 11.12.0 现已正式发布。该版本新增 PdfFreeTextAnnotation.StringFormat 属性,用于设置文本的水平与垂直对齐方式,并对 WPF 平台下的 PDF 转图片渲染逻辑进行了优化。同时,还修复了多项与 PDF 转换、多语言文本渲染以及注释处理相关的问题。更多详情如下。
调整:
新功能:
PdfDocument newPdf = new PdfDocument();
PdfPageBase page = newPdf.Pages.Add();
Spire.Pdf.Graphics.PdfFont font = new PdfFont(PdfFontFamily.Helvetica, 10f);
SizeF textSize = font.MeasureString("sample");
RectangleF rect = new RectangleF(50,50, textSize.Width, textSize.Height);
Spire.Pdf.Annotations.PdfFreeTextAnnotation textAnnotation = new Spire.Pdf.Annotations.PdfFreeTextAnnotation(rect);
textAnnotation.Text = "sample";
textAnnotation.Subject = "subject";
textAnnotation.Font = font;
Spire.Pdf.Annotations.PdfAnnotationBorder border = new Spire.Pdf.Annotations.PdfAnnotationBorder(8);
textAnnotation.Border = border;
textAnnotation.LineEndingStyle = Spire.Pdf.Annotations.PdfLineEndingStyle.None;
textAnnotation.Size = new SizeF(textSize.Width * 1.5F, textSize.Height * 1.5F);
textAnnotation.StringFormat.Alignment = PdfTextAlignment.Center;
textAnnotation.StringFormat.LineAlignment = PdfVerticalAlignment.Middle;
page.Annotations.Add(textAnnotation);
newPdf.SaveToFile(result);
问题修复:
HTML(超文本标记语言)是一种用于创建网页的标记语言,能让开发者构建内容丰富、视觉吸引力强的页面布局。然而,HTML 文件通常包含大量标签,若仅需获取主要内容,这些标签会使其难以阅读。通过 Python 将 HTML 转换为文本,可轻松解决这一问题。与原始 HTML 不同,转换后的文本文件会剥离所有不必要的标记,仅保留干净易读的内容,更便于存储、分析或进一步处理。
主要内容:
推荐使用 Spire.Doc for Python 实现转换。该 Python Word 库不仅是轻量高效的 HTML 转文本工具,还支持几乎所有 Word 操作(如创建、内容编辑等),兼容性强、上手简单。
通过 pip 命令安装(推荐)
打开终端 / 命令行,执行以下命令即可自动完成安装:
pip install spire.doc
借助 Spire.Doc for Python,仅需 3 个简单步骤即可将本地 HTML 文件转换为纯文本:创建 Document 对象 → 加载 HTML 文件 → 保存为 TXT 格式。整个过程简洁高效,新手也能轻松上手!下面详细介绍代码实现:
代码示例 — HTML 文件转 TXT 文本文件
from spire.doc import *
from spire.doc.common import *
# 打开 HTML 文件
document = Document()
document.LoadFromFile("sample.html.html", FileFormat.Html, XHTMLValidationType.none)
# 保存为文本文件
document.SaveToFile("HTML转文本.txt", FileFormat.Txt)
document.Close()
转换效果预览(源文件 vs 输出文件):

请注意:若 HTML 文件包含表格,输出的文本文件将仅保留表格中的数据,无法保留原始表格格式。若希望移除标记的同时保留特定样式,建议先将 HTML 转换为 Word 文档,这样可保留标题、表格等格式,让内容更易于编辑和使用。
如果仅需提取网页部分内容(如爬取的 HTML 片段),可直接将 HTML 字符串转换为文本,无需加载完整 HTML 文件,更灵活高效。
Spire.Doc 转换 HTML 字符串到文本文件的实现步骤:
Document 对象并添加节(Section)和段落(Paragraph);Paragraph.AppendHTML() 方法将 HTML 字符串插入段落;Document.SaveToFile() 方法将文档保存为 .txt 文件。代码示例 — HTML 字符串转 TXT 文本文件
from spire.doc import *
from spire.doc.common import *
# 获取 HTML 字符串(可注释下方代码,取消注释读取本地文件的逻辑)
# with open(inputFile) as fp:
# html = fp.read()
# 定义 HTML 字符串
html = """
<html>
<body>
<h1>示例HTML内容</h1>
<p>这是一个包含<strong>粗体</strong>和<em>斜体</em>文本的段落。</p>
<p>另一行带有<a href='https://example.com'>链接</a>。</p>
<ul>
<li>列表项1</li>
<li>列表项2(带有<em>斜体</em>文本)</li>
</ul>
<p>特殊字符:© & ®</p>
</body>
</html>
"""
# 创建新文档
document = Document()
section = document.AddSection()
# 将 HTML 字符串插入段落
section.AddParagraph().AppendHTML(html)
# 保存为 TXT 文件
document.SaveToFile("HTML字符串转文本.txt", FileFormat.Txt)
document.Close()
转换后的 TXT 文件预览:

借助 Spire.Doc for Python,仅需几行代码即可完成 HTML 文件 / 字符串到纯文本的转换,操作简单、效率高,且能适配大多数实际场景。若需探索更多功能,可申请 30 天免费试用许可,解锁全部功能。
无论是数据分析、内容提取还是文档处理,HTML 转文本都是高频需求 — 掌握这一技巧,能大幅提升 Python 数据处理的灵活性和效率!
答:可通过遍历文件夹内的 HTML 文件,循环调用 Spire.Doc 的转换逻辑实现批量处理,核心是结合os模块遍历文件,示例代码如下:
from spire.doc import *
from spire.doc.common import *
import os
# 定义输入/输出文件夹路径
input_folder = "./html_files"
output_folder = "./txt_files"
# 确保输出文件夹存在
if not os.path.exists(output_folder):
os.makedirs(output_folder)
# 遍历输入文件夹下所有HTML文件
for filename in os.listdir(input_folder):
if filename.endswith(".html") or filename.endswith(".htm"):
# 拼接文件路径
input_path = os.path.join(input_folder, filename)
# 生成输出文件名(替换后缀为txt)
output_filename = os.path.splitext(filename)[0] + ".txt"
output_path = os.path.join(output_folder, output_filename)
# 执行转换逻辑
document = Document()
document.LoadFromFile(http://cdn.e-iceblue.cn/input_path, FileFormat.Html, XHTMLValidationType.none)
document.SaveToFile(output_path, FileFormat.Txt)
print(f"成功转换:{filename} → {output_filename}")
document.Close()
答:可以。Spire.Doc 内置了对格式错误 HTML 的容错能力,但需要禁用严格验证以确保正确解析,即加载 HTML 文件时,使用 XHTMLValidationType.none 跳过严格的 XHTML 检查:
答:Spire.Doc for Python 仅处理 HTML 中的文本内容,图片(<img>)、视频(<video>)、音频(<audio>)等媒体标签会被直接剥离,不会在文本中保留标签本身或媒体文件相关信息。
Spire.Presentation for Python 10.12.0 现已正式发布。本次更新针对 PPTX 转 PDF 的准确性和稳定性进行了增强,修复了文本丢失、内容重叠以及表格布局不正确等问题。详情如下。
问题修复:
https://www.e-iceblue.cn/Downloads/Spire-Presentation-Python.html
随着 Web 前端技术的发展,越来越多的企业与开发者开始将文档处理和文件管理等能力迁移至纯前端体系中,以提升应用的响应速度、安全性和易用性。
作为当前流行的前端框架之一,React 以其声明式、组件化的优势,广泛应用于构建从后台管理系统、在线文档平台到教育管理系统等各类复杂Web应用。 本文将指导你便捷地在 React 项目中集成使用Spire.OfficeJS,让复杂的文档处理变得简单、可控。
目录:
Spire.OfficeJS 是 E-ICEBLUE 推出的纯前端文档编辑组件套件,包含 Word、Excel、PowerPoint、PDF 等多种格式的查看、编辑、创建与转换能力。开发者无需安装 Microsoft Office,也无需依赖后端服务,即可在浏览器中完成从打开到编辑、再到导出的完整文档处理流程。其轻量、高效、易集成的特性,使其非常适合用于构建在线文档平台、知识库、教育平台及各类管理后台。
套件包括以下四款前端编辑器组件:
核心功能:
下载安装 Node.JS (Node.JS 官方网站:https://nodejs.org/en/download/),安装后可通过打开 cmd 后输入下述命令验证版本:
node -v
npm -v

指定项目文件夹位置,cmd 进入执行命令:npm create vite@latest my-officejs-app -- --template react 创建初始化 React 项目。

Vscode 打开 my-officejs-app 项目,命令执行:npm install react-router-dom 安装路由管理库,以便页面之间更好地切换显示。
下载 Spire.OfficeJS 产品包, 在 React 项目 public 文件下新建 spire.cloud 文件夹,并将产品解压包中的web文件夹复制到 spire.cloud 下,路径配置跟 Editor.jsx 中路径保持一致

import { createContext, useContext, useState } from 'react';
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import Home from './Home';
import Editor from './Editor';
// 文件状态 Context
const FileContext = createContext();
export const useFileStore = () => useContext(FileContext);
function FileProvider({ children }) {
const [file, setFile] = useState(null);
const [fileUint8Data, setFileUint8Data] = useState(null);
return (
<FileContext.Provider value={{
file,
fileUint8Data,
setFileData: setFile,
setFileUint8Data
}}>
{children}
</FileContext.Provider>
);
}
const router = createBrowserRouter([
{ path: '/', element: <Home /> },
{ path: '/editor', element: <Editor /> },
]);
function App() {
return (
<FileProvider>
<RouterProvider router={router} />
</FileProvider>
);
}
export default App;
说明:
import { useRef, useEffect } from "react"
import { useFileStore } from './App';
import { useNavigate } from 'react-router-dom';
function Home() {
const { setFileData, setFileUint8Data } = useFileStore();
const navigate = useNavigate();
let dropArea = null;
let fileInput = useRef();
let file = null;
let fileUint8Data = null;
useEffect(() => {
dropArea = document;
// 阻止默认拖放行为
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
dropArea.addEventListener(eventName, preventDefaults, false);
});
// 处理文件拖放
dropArea.addEventListener('drop', handleDrop, false);
}, [])
const preventDefaults = (e) => {
e.preventDefault();
e.stopPropagation();
}
const handleBtnClick = (e) => {
e.preventDefault();
fileInput.current.click();
}
const handleDrop = async (e) => {
if (e.target && e.target.files)
file = e.target.files[0]
else if (e.dataTransfer && e.dataTransfer.files)
file = e.dataTransfer.files[0];
if (!file) return;
fileUint8Data = await handleFile(file);
setFileData(file);
setFileUint8Data(fileUint8Data);
openDocument();
}
const handleFile = (file) => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
const arrayBuffer = reader.result;
const uint8Array = new Uint8Array(arrayBuffer);
resolve(uint8Array);
};
reader.onerror = (error) => reject(error);
reader.readAsArrayBuffer(file);
});
}
const openDocument = () => {
navigate('/editor')
}
return (
<div>
<div>
<h2>文件上传</h2>
<div>
<p>拖放文件到浏览器</p>
<p style={{ marginLeft: '20px' }}>或</p>
<button ref={fileInput} onClick={handleBtnClick}>选择文件</button>
<input
type="file"
id="fileInput"
ref={fileInput}
onChange={handleDrop}
style={{ display: 'none' }}
/>
</div>
</div>
</div>
)
}
export default Home
import { useRef, useEffect } from "react"
import { useFileStore } from './App';
import { useNavigate } from 'react-router-dom';
function Editor() {
const { file, fileUint8Data } = useFileStore();
const navigate = useNavigate();
const config = useRef({});
let Editor = useRef(null);
let Api = useRef(null);
let originUrl = window.location.origin;
useEffect(() => {
if (!file) {
navigate('/')
return;
}
// 动态加载SpireCloudEditor.js
loadScript();
}, [])
const loadScript = () => {
var script = document.createElement('script');
// 根据你的产品包路径调整
script.setAttribute('src', '/spire.cloud/web/editors/spireapi/SpireCloudEditor.js');
script.onload = () => initEditor()
document.head.appendChild(script);
}
const initEditor = () => {
let iframeId = 'iframeEditor';
initConfig();
Editor = new SpireCloudEditor.OpenApi(iframeId, config.value);
window.Api = Api = Editor.GetOpenApi();
OnWindowReSize();
}
const initConfig = () => {
config.value = {
"fileAttrs": {
"fileInfo": {
"name": file.name,
"ext": getFileExtension(),
"primary": String(new Date().getTime()),
"creator": "User",
"createTime": new Date().toLocaleString()
},
"sourceUrl": originUrl + "/files/" + file.name,
"createUrl": originUrl + "/open",
"mergeFolderUrl": "",
"fileChoiceUrl": "",
"templates": {}
},
"user": {
"id": "uid-1",
"name": "User",
"canSave": true,
},
"editorAttrs": {
"editorMode": "edit",
"editorWidth": "100%",
"editorHeight": "100%",
"editorType": "document", // document/spreadsheet/presentation
"platform": "desktop",
"viewLanguage": "zh", // en/zh
"isReadOnly": false,
"canChat": true,
"canComment": true,
"canReview": true,
"canDownload": true,
"canEdit": true,
"canForcesave": true,
"embedded": {
"saveUrl": "",
"embedUrl": "",
"shareUrl": "",
"toolbarDocked": "top"
},
// 启用WebAssembly以提升性能
"useWebAssemblyDoc": true,
"useWebAssemblyExcel": true,
"useWebAssemblyPpt": true,
"spireDocJsLicense": "",
"spireXlsJsLicense": "",
"spirePresentationJsLicense": "",
"spirePdfJsLicense": "",
// Serverless模式:直接使用文件数据,无需服务器
"serverless": {
"useServerless": true,
"baseUrl": originUrl,
"fileData": fileUint8Data, // 文件的Uint8Array数据
},
"events": {
"onSave": onFileSave
},
"plugins": {
"pluginsData": []
}
}
};
}
const OnWindowReSize = () => {
let wrapEl = document.getElementById("editor-container");
if (wrapEl) {
wrapEl.style.height = screen.availHeight + "px";
window.scrollTo(0, -1);
wrapEl.style.height = window.innerHeight + "px";
}
}
const getFileExtension = () => {
const filename = file.name.split(/[\\/]/).pop();
return filename.substring(filename.lastIndexOf('.') + 1).toLowerCase() || '';
}
const onFileSave = (data) => {
console.log('保存数据:', data)
// 在这里实现你的保存逻辑
// 例如:发送到服务器、下载文件等
}
return (
<div id="editor-container">
<div id="iframeEditor"></div>
</div>
)
}
export default Editor
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import App from './App.jsx'
createRoot(document.getElementById('root')).render(
// 注意:StrictMode会导致Spire编辑器渲染两次,建议禁用
<App />
)
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
server: {
host: '0.0.0.0',
port: 8050
},
plugins: [react()],
})
命令执行:npm run dev 运行配置后的项目,浏览器访问http://localhost:8050/ 。

通过”选择文件“上传本地文档打开编辑。

通过“文件“—>”下载为” 可以下载或转换为其它格式。

1. 创建项目时报错 npm或 node 版本不兼容:
可能是 Node.js 版本 问题,建议安装较新版本。
2. 依赖安装失败:
3. Spire.OfficeJS 文件无法加载:
如果您需要去除生成文档中的评估提示或解除功能限制,请联系我们获取有效期 30 天的临时许可证。