在 Word 文档中处理形状是日常办公开发中最常见的需求之一,无论是为合同模板加盖一个圆角矩形的签章框、用流程图形状组标注审批步骤,还是清理文档中残留的装饰形状、把旧模板里形状的配色统一成新的品牌色,形状的动态操作都能让文档内容随业务数据同步更新。Spire.Doc for JavaScript 基于 WebAssembly 在浏览器端直接完成 Word 文档的形状处理,通过虚拟文件系统(VFS)管理字体、文档与图片资源,无需后端服务支持。
本文介绍三个核心功能点:
有关安装和项目配置,请参考 React 项目中集成 Spire.Doc for JavaScript。以下示例默认已安装 Spire.Doc 并完成 WebAssembly 模块初始化。
在Word文档中添加形状
添加形状的核心流程分为三个阶段:首先通过 FetchFileToVFS 将字体文件载入 WASM 虚拟文件系统;然后实例化 Document 并依次添加节与段落,调用 Paragraph.AppendShape 在段落中插入指定类型、指定尺寸的形状,再通过 HorizontalOrigin/VerticalOrigin 把形状锚定到页面,配合 HorizontalPosition/VerticalPosition 设置其绝对坐标;最后将文档保存并从 VFS 读取生成的文件,封装为 Blob 后触发浏览器下载。
function App() {
const appendShape = async () => {
const docModule = window.wasmModule?.spiredoc;
if (!docModule) {
alert('Spire.Doc is not ready yet');
return;
}
// 新建文档
const doc = new docModule.Document();
// 添加一节
let sec = doc.AddSection();
// 添加一个用于承载形状的段落
let paragraph = sec.AddParagraph();
let x = 60, y = 40, lineCount = 0;
for (let i = 1; i < 20; i++) {
if (lineCount > 0 && lineCount % 8 == 0) {
// 每页排满 8 行后分页,并重置起始坐标
paragraph.AppendBreak(docModule.BreakType.PageBreak);
x = 60;
y = 40;
lineCount = 0;
}
// 添加形状,并设置其尺寸
let shape = paragraph.AppendShape(50, 50, docModule.ShapeType.fromValue(i));
// 以页面为参照系设置形状的绝对位置
shape.HorizontalOrigin = docModule.HorizontalOrigin.Page;
shape.HorizontalPosition = x;
shape.VerticalOrigin = docModule.VerticalOrigin.Page;
shape.VerticalPosition = y + 50;
// 计算下一个形状的坐标
x = x + shape.Width + 50;
if (i > 0 && i % 5 == 0) {
y = y + shape.Height + 120;
lineCount++;
x = 60;
}
}
// 保存文档
const outputFileName = "添加形状.docx";
doc.SaveToFile({ fileName: outputFileName, fileFormat: docModule.FileFormat.Docx });
// 释放资源
doc.Dispose();
const modifiedFileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([modifiedFileArray], { type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' });
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>添加形状到Word文档</h1>
<button onClick={appendShape}>
开始
</button>
</div>
);
}
export default App;
上述代码循环插入 19 种不同的预设形状,每个形状尺寸为 50×50,通过 ShapeType.fromValue(i) 逐一取出形状类型,并以页面为坐标原点按行列排布,每行 5 个,排满 8 行后通过 AppendBreak 自动分页,生成的 Word 文档效果如下:

当需要一次插入多个相互关联的形状时(例如用矩形、平行四边形与箭头拼出一张流程图),可以改用 Paragraph.AppendShapeGroup 先创建一个形状组,再通过 ChildObjects.Add 把文本框、箭头等子形状加入组内统一排布:形状组内的坐标以组自身为参照,因此需要用 Width / 1000.0 与 Height / 1000.0 算出缩放比例,再把子形状的目标坐标除以该比例赋值给 HorizontalPosition/VerticalPosition;文本框通过 new wasmModule.TextBox(doc) 创建后用 SetShapeType 指定外形,其 Format.LineColor 与普通形状的 StrokeColor 一样可用于设置描边色。
删除Word文档中的形状
删除形状的核心流程分为三个阶段:首先通过 FetchFileToVFS 将字体文件和待处理的 Word 文档载入 WASM 虚拟文件系统;然后实例化 Document 加载文件,逐层遍历文档中的节与段落,通过 DocumentObjectType 判断段落内的子对象是否为形状——文档中的普通形状与文本框统一以 Shape 类型存在,形状组为 ShapeGroup,而在内存中通过 new wasmModule.TextBox(doc) 新建、尚未保存的文本框单独为 TextBox,三种类型都需要一并判断;确认后将对象收集起来,调用 ChildObjects.Remove 逐个移出段落;最后保存文档并从 VFS 读取生成的文件,封装为 Blob 后触发浏览器下载。
function App() {
const removeShape = async () => {
const docModule = window.wasmModule?.spiredoc;
if (!docModule) {
alert('Spire.Doc is not ready yet');
return;
}
const inputFileName = 'ShapeTemplate.docx';
await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}static/data/`);
// 加载文档
let doc = new docModule.Document();
doc.LoadFromFile(inputFileName);
let removedCount = 0;
// 遍历所有节与段落,删除段落中的形状
for (let i = 0; i < doc.Sections.Count; i++) {
let sec = doc.Sections.get_Item(i);
for (let j = 0; j < sec.Paragraphs.Count; j++) {
let para = sec.Paragraphs.get_Item(j);
// 一次性收集段落中的形状:普通形状与文本框为 Shape,形状组为 ShapeGroup
let shapes = [];
for (let k = 0; k < para.ChildObjects.Count; k++) {
let docObj = para.ChildObjects.get_Item(k);
let objType = docObj.DocumentObjectType;
if (objType == docModule.DocumentObjectType.Shape
|| objType == docModule.DocumentObjectType.ShapeGroup
|| objType == docModule.DocumentObjectType.TextBox) {
shapes.push(docObj);
}
}
// 逐个移除段落中的形状
for (let m = 0; m < shapes.length; m++) {
para.ChildObjects.Remove(shapes[m]);
removedCount++;
}
}
}
// 保存文档
const outputFileName = "删除形状.docx";
doc.SaveToFile({ fileName: outputFileName, fileFormat: docModule.FileFormat.Docx });
// 释放资源
doc.Dispose();
const modifiedFileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([modifiedFileArray], { type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' });
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>从Word文档中删除形状</h1>
<button onClick={removeShape}>
开始
</button>
</div>
);
}
export default App;
上述代码遍历文档结构并移除页面上的所有形状,段落中的文字内容与格式保持不变,生成的 Word 文档效果如下:

修改Word文档中的形状
修改形状的核心流程分为三个阶段:首先通过 FetchFileToVFS 将字体文件和待处理的 Word 文档载入 WASM 虚拟文件系统;然后实例化 Document 加载文件,遍历节与段落定位其中的形状对象,对普通形状与文本框直接设置 FillColor、StrokeColor 更换配色,设置 Rotation、Width、Height 调整旋转角度与尺寸,对形状组则继续下钻到其 ChildObjects 逐一修改组内子形状;最后保存文档并从 VFS 读取生成的文件,封装为 Blob 后触发浏览器下载。
function App() {
const modifyShape = async () => {
const docModule = window.wasmModule?.spiredoc;
if (!docModule) {
alert('Spire.Doc is not ready yet');
return;
}
const inputFileName = 'ShapeTemplate.docx';
await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}static/data/`);
// 加载文档
let doc = new docModule.Document();
doc.LoadFromFile(inputFileName);
// 遍历所有节与段落,修改段落中的形状
for (let i = 0; i < doc.Sections.Count; i++) {
let sec = doc.Sections.get_Item(i);
for (let j = 0; j < sec.Paragraphs.Count; j++) {
let para = sec.Paragraphs.get_Item(j);
for (let k = 0; k < para.ChildObjects.Count; k++) {
let docObj = para.ChildObjects.get_Item(k);
let objType = docObj.DocumentObjectType;
// 修改普通形状与文本框的填充色、轮廓色、旋转角度与尺寸
if (objType == docModule.DocumentObjectType.Shape) {
docObj.FillColor = docModule.Color.get_Orange();
docObj.StrokeColor = docModule.Color.get_Red();
docObj.Rotation = 15;
docObj.Width = docObj.Width * 1.2;
docObj.Height = docObj.Height * 1.2;
}
// 修改形状组:下钻到组内,逐一修改子形状的轮廓色
if (objType == docModule.DocumentObjectType.ShapeGroup) {
for (let n = 0; n < docObj.ChildObjects.Count; n++) {
let child = docObj.ChildObjects.get_Item(n);
child.StrokeColor = docModule.Color.get_Purple();
}
}
}
}
}
// 保存文档
const outputFileName = "修改形状.docx";
doc.SaveToFile({ fileName: outputFileName, fileFormat: docModule.FileFormat.Docx });
// 释放资源
doc.Dispose();
const modifiedFileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([modifiedFileArray], { type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' });
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>修改Word文档中已有的形状</h1>
<button onClick={modifyShape}>
开始
</button>
</div>
);
}
export default App;
上述代码在保留形状原有位置与文字的前提下,把文档中的形状统一改为橙色填充、红色轮廓,并放大 120%、旋转 15 度,形状组内的子形状也一并换成了新的轮廓色,生成的 Word 文档效果如下:

常见问题
形状添加后位置错乱,或跑到页面之外
原因:HorizontalPosition/VerticalPosition 的坐标含义取决于参照系(Origin)。若未显式设置 HorizontalOrigin/VerticalOrigin,坐标默认以段落或栏为参照,会随段落缩进、页面边距的变化而偏移,导致形状偏离预期位置。
解决:先用 HorizontalOrigin/VerticalOrigin 指定参照系,再设置具体的坐标值:
// 以页面为参照系定位形状
shape.HorizontalOrigin = wasmModule.HorizontalOrigin.Page;
shape.HorizontalPosition = x;
shape.VerticalOrigin = wasmModule.VerticalOrigin.Page;
shape.VerticalPosition = y + 50;
删除形状时漏掉了部分对象
原因:形状在 Spire.Doc 中并非只有一种 DocumentObjectType:普通形状与文本框统一为 Shape,形状组为 ShapeGroup,而在内存中新建、尚未保存的文本框为 TextBox。只判断 Shape 会漏掉形状组与尚未保存的文本框;此外 ChildObjects.Remove 会使后续子对象的索引整体前移,若在遍历过程中一边读取 ChildObjects.Count 一边移除,还会跳过部分对象。
解决:先把三种类型一次性收集到数组中,再对数组逐个处理;修改形状组内的子形状时,需下钻到其 ChildObjects 单独遍历:
// 先收集三种类型的形状对象
if (objType == wasmModule.DocumentObjectType.Shape
|| objType == wasmModule.DocumentObjectType.ShapeGroup
|| objType == wasmModule.DocumentObjectType.TextBox) {
shapes.push(docObj);
}
// 再统一移除
for (let m = 0; m < shapes.length; m++) {
para.ChildObjects.Remove(shapes[m]);
}
获取免费许可证
如果您希望删除结果文档中的评估消息,或者摆脱功能限制,请该Email地址已收到反垃圾邮件插件保护。要显示它您需要在浏览器中启用JavaScript。获取有效期 30 天的临时许可证。







