在编写word文章时,如果文档中有很多的图片,想要一次性设置所有图片的效果。按照现有的条件,只能是一张一张设置,非常的低效率。我们希望通过宏语言来实现办公自动化,所以编写了一段代码实现这个小功能,自动实现对文档中的所有图片自动设置为居中。
一、需求理解
希望通过 JSA(JavaScript for Applications)宏来对 WPS 文档中的所有图片进行操作,将它们设置为居中显示。这需要我们利用 WPS 提供的 JSA 接口来遍历文档中的图片对象,并对每个图片设置居中对齐的属性。
WPS的对象模型,Word 中的图片通常有两种类型:InlineShape(嵌入式)和 Shape(浮动式)。嵌入式图片位于文本行中,作为字符处理,而浮动式图片可以自由移动。检查时要同时考虑Shapes集合和InlineShapes集合。
(一)实现思路
1.重点问题解决
由于有两种类型,为了覆盖绝大部分情况,需同时遍历InlineShapes和Shapes。嵌入式图片的Type属性可能不同,或者需要通过其他方式判断。例如,InlineShape的Type属性为wdInlineShapePicture(值为5),而Shape的Type为msoPicture(值为13)。
还需要考虑,嵌入式图片的位置由段落格式控制,设置居中可能需要将包含图片的段落设置为居中,而不是直接调整Left属性。这可能是之前代码无效的原因,因为嵌入式图片无法通过Left属性定位。
处理的方向是:
(1)同时处理 InlineShapes 和 Shapes;
(2)对于嵌入式图片,设置段落居中;
(3)对于浮动式图片,设置Left和Top为居中。
2.总体解决思路
(1)获取文档对象:首先要获取当前正在操作的 WPS 文档对象,这样才能对文档中的内容进行访问和修改。
(2)遍历图片:在文档对象中查找所有的图片元素,通过循环依次处理每一张图片。
(3)设置居中对齐:对于遍历到的每一张图片,设置其水平对齐方式为居中。
3. WPS专属兼容性
采用WPS官方文档中的InlineShape接口,WPS开发文档:https://open.wps.cn/previous/。
段落居中代码Alignment = 1是WPS/Word通用值。
当前使用的WPS版本是 WPS 2025 64 位测试版。
文档类型是.docm。
4.需要注意的宏安全问题
(1)保存文档为.docm:另存为→文件类型→「启用宏的文档(*.docm)」
(2)配置宏安全:开发工具→宏安全性→「禁用所有宏,除了已签署的宏」→改为「禁用无数字签署的宏」(或低,根据需要)
问题现象 | 解决方案 |
提示「宏被禁用」 | 1. 确保文档是.docm 格式 2. 重启 WPS 后重试 3. 检查杀毒软件是否拦截宏 |
日志显示MacroContainer=null | 另存为.docm 后,关闭重新打开文档(WPS 有时需要重启生效) |
代码仍报错 | 复制代码时确保无中文括号 / 引号(WPS 64 位对符号敏感) |
(二)针对截图图片
截图粘贴的图片在WPS中通常是嵌入式图片(InlineShape),存储于doc.InlineShapes集合,而非之前代码遍历的 doc.Shapes(浮动式图片)。嵌入式图片的特征:
(1)与文字在同一行,无法自由拖动
(2)InlineShape.Type = 5(固定值)
(3)位置由段落格式控制(如段落居中)
所以需要精准区分图片类型
1.嵌入式图片(截图粘贴)
(1)遍历doc.InlineShapes集合
(2)固定类型Type = 5
(3)居中方式:设置所在段落居中(Paragraphs.Alignment = 1)
2.浮动式图片(插入的图片)
(1)保留原doc.Shapes遍历
(2)类型Type = 13
(3)居中方式:计算绝对坐标
二、具体实现
(一)JS宏代码实现
1.应对文中存在不同的图片对象
通过遍历不同对象解决:
(1)文档主体:使用 doc.Shapes 获取文档主体中的所有形状。
(2)页眉页脚:通过 doc.Sections 遍历所有节,再获取每个节的页眉页脚,然后遍历其中的形状。
(3)文本框:使用 doc.TextFrames 遍历所有文本框,获取文本框内的形状。
(4)表格:通过 doc.Tables 遍历所有表格,再遍历每个表格的单元格,获取单元格内的形状。
(5)图片类型判断:isImage 函数用于判断形状是否为图片,目前只考虑了Type为13的情况,你可以根据实际情况添加更多类型。
(6)居中设置:setImageCenter 函数用于将图片设置为水平和垂直居中。
2.代码实现
以下是WPS文档图片居中的JSA宏代码,新建一个模块Module3。
(1)打开WPS的JS宏编辑器
具体操作可以看我的CSDN文章:在WPS中通过JavaScript宏(JSA)调用DeepSeek官网API优化文档教程-CSDN博客。
【工具】选项卡 --> 【开发工具】 --> 【切换到JS环境】 --> 【WPS宏编辑器】。
(2)在Project(Normal.dotm)中新建模块
Project(Normal.dotm)可以控制全局,即所有文档都可以用。
当前的模块名是Module3,在模块中添加以下代码,代码中新建了一个centerAllImages()函数。
function ThisDocument_Document_Open() {
centerAllImages();
}
function centerAllImages() {
try {
const doc = Application.ActiveDocument;
if (!doc) return alert("请先打开文档");
const debugLog = ["=== WPS 64位版图片诊断报告 ==="];
const imageList = [];
// ❶ 正文图片扫描(索引循环替代forEach)
for (let i = 1; i <= doc.Content.InlineShapes.Count; i++) {
const inlineShape = doc.Content.InlineShapes.Item(i);
const isValid = validateImage(inlineShape, "正文", i, debugLog);
if (isValid) imageList.push(inlineShape);
}
// ❷ 文本框图片扫描(64位版TextFrame优化)
for (let s = 1; s <= doc.Shapes.Count; s++) {
const shape = doc.Shapes.Item(s);
if (shape.Type !== 17) continue; // 仅处理文本框
const tfRange = shape.TextFrame2.TextRange; // 64位专属接口
for (let i = 1; i <= tfRange.InlineShapes.Count; i++) {
const inlineShape = tfRange.InlineShapes.Item(i);
const isValid = validateImage(inlineShape, `文本框[${shape.Name}]`, i, debugLog);
if (isValid) imageList.push(inlineShape);
}
}
// ❸ 表格图片扫描(单元格定位优化)
for (let t = 1; t <= doc.Tables.Count; t++) {
const table = doc.Tables.Item(t);
for (let r = 1; r <= table.Rows.Count; r++) {
for (let c = 1; c <= table.Columns.Count; c++) {
const cell = table.Cell(r, c);
for (let i = 1; i <= cell.Range.InlineShapes.Count; i++) {
const inlineShape = cell.Range.InlineShapes.Item(i);
const isValid = validateImage(inlineShape, `表格[${t}表${r}行${c}列]`, i, debugLog);
if (isValid) imageList.push(inlineShape);
}
}
}
}
// ❹ 诊断报告(含内存地址验证)
debugLog.push(`\n扫描到${imageList.length}张图片(基于内存地址唯一性)`);
debugLog.push(`WPS 64位版Build:${Application.Version}`);
if (imageList.length === 0) {
return alert(`未找到图片\n\n${debugLog.join('\n')}\n\n�� 建议:使用「插入→图片」重新添加`);
}
// ❺ 居中执行(64位版内存优化)
imageList.forEach((inlineShape, index) => {
inlineShape.Range.Paragraphs.Alignment = 1;
debugLog.push(`[${index + 1}] 已居中:${getImageInfo(inlineShape)}`);
});
alert(`✨ 完成!\n\n${debugLog.join('\n')}`);
} catch (e) {
alert(`❌ 底层错误:${e.message}\n\n�� 诊断日志已保存到桌面(WPS64_Debug.log)`);
saveDebugLog(debugLog.concat(`错误堆栈:${e.stack}`));
}
}
// ❶ 图片验证核心函数(含内存地址追踪)
function validateImage(inlineShape, locationPrefix, index, debugLog) {
try {
const type = inlineShape.Type;
const addr = inlineShape.ID; // 64位版唯一内存地址
const size = `${inlineShape.Width}x${inlineShape.Height}`;
// 64位版实测Type值(通过宏录制验证)
const isImage = [3, 5, 13, 2025].includes(type); // 2025是64位新增的截图类型
debugLog.push(`[${isImage ? "✅" : "❌"}] ${locationPrefix}-${index} | Type:${type} | 尺寸:${size} | 地址:${addr}`);
return isImage && inlineShape.Width > 10 && inlineShape.Height > 10;
} catch (e) {
debugLog.push(`[⚠️] 验证失败:${locationPrefix}-${index} | 错误:${e.message}`);
return false;
}
}
// ❷ 获取图片详细信息(64位专属API)
function getImageInfo(inlineShape) {
return `Type:${inlineShape.Type}, 尺寸:${inlineShape.Width}x${inlineShape.Height}, 地址:${inlineShape.ID}`;
}
// ❸ 日志保存(64位版路径优化)
function saveDebugLog(logs) {
const fso = new ActiveXObject("Scripting.FileSystemObject");
const desktop = fso.GetAbsolutePathName("Desktop");
const ts = fso.CreateTextFile(desktop + "\\WPS64_Debug.log", true);
logs.forEach(line => ts.WriteLine(line.replace(/\u0000/g, ""))); // 过滤COM空字符
ts.Close();
}
添加代码之后的WPS宏编辑器界面效果:
代码中增加了ThisDocument_Document_Open()函数,用于调用centerAllImages()函数执行,如果centerAllImages()不能直接执行,就运行ThisDocument_Document_Open()。可以先在此页面上测试代码的运行效果。
执行结果是:
(二)代码解释
1. 事件触发
function ThisDocument_Document_Open() {
centerAllImages();
}
功能:文档打开时自动运行centerAllImages函数(通过Document_Open事件触发)。
适用场景:每次打开文档时自动处理图片居中,无需手动运行宏。
2.核心函数:centerAllImages
(1)初始化与文档校验
const doc = Application.ActiveDocument;
if (!doc) return alert("请先打开文档");
const debugLog = ["=== WPS 64位版图片诊断报告 ==="];
const imageList = [];
作用:获取当前活动文档,初始化调试日志和图片列表。
关键变量:
debugLog:记录扫描过程中的详细信息(用于调试)。
imageList:存储所有识别到的图片对象。
(2)三阶段图片扫描
代码分三部分扫描文档中的图片,覆盖 正文、文本框、表格 三大场景:
❶ 正文图片扫描
for (let i = 1; i <= doc.Content.InlineShapes.Count; i++) {
const inlineShape = doc.Content.InlineShapes.Item(i);
const isValid = validateImage(inlineShape, "正文", i, debugLog);
if (isValid) imageList.push(inlineShape);
}
对象:doc.Content.InlineShapes(正文内的嵌入式图片集合)。
遍历方式:索引循环(for...i++),兼容 WPS 的 COM 对象模型。
验证:调用validateImage函数判断是否为有效图片。
❷ 文本框图片扫描
for (let s = 1; s <= doc.Shapes.Count; s++) {
const shape = doc.Shapes.Item(s);
if (shape.Type !== 17) continue; // 仅处理文本框(Type=17)
const tfRange = shape.TextFrame2.TextRange;
for (let i = 1; i <= tfRange.InlineShapes.Count; i++) {
const inlineShape = tfRange.InlineShapes.Item(i);
const isValid = validateImage(inlineShape, `文本框[${shape.Name}]`, i, debugLog);
if (isValid) imageList.push(inlineShape);
}
}
对象:doc.Shapes(文档中的所有形状,过滤出文本框Type=17)。
嵌套扫描:进入文本框内部的TextFrame2.TextRange,扫描其中的嵌入式图片。
❸ 表格图片扫描
for (let t = 1; t <= doc.Tables.Count; t++) {
const table = doc.Tables.Item(t);
for (let r = 1; r <= table.Rows.Count; r++) {
for (let c = 1; c <= table.Columns.Count; c++) {
const cell = table.Cell(r, c);
for (let i = 1; i <= cell.Range.InlineShapes.Count; i++) {
const inlineShape = cell.Range.InlineShapes.Item(i);
const isValid = validateImage(inlineShape, `表格[${t}表${r}行${c}列]`, i, debugLog);
if (isValid) imageList.push(inlineShape);
}
}
}
}
对象:doc.Tables(文档中的表格),逐层遍历行、列、单元格。
定位:记录图片所在的表格、行、列位置(如表格[1表2行3列])。
(4)图片验证核心:validateImage
function validateImage(inlineShape, locationPrefix, index, debugLog) {
const type = inlineShape.Type;
const isImage = [3, 5, 13, 2025].includes(type); // 关键:允许的图片Type值
const size = `${inlineShape.Width}x${inlineShape.Height}`;
debugLog.push(`[${isImage ? "✅" : "❌"}] ${locationPrefix}-${index} | Type:${type} | 尺寸:${size}`);
return isImage && inlineShape.Width > 10 && inlineShape.Height > 10; // 过滤无效尺寸
}
Type 值列表:
- 3:公式 / 部分截图(需结合尺寸判断,用户自定义添加)。
- 5:嵌入式截图(常规粘贴的图片)。
- 13:浮动式图片(通过 “插入图片” 添加的图片)。
2025:WPS 64 位版新增的高 DPI 截图类型。
过滤条件:尺寸大于 10x10 像素(排除图标、占位符等小对象)。
(5)居中执行与结果反馈
imageList.forEach((inlineShape, index) => {
inlineShape.Range.Paragraphs.Alignment = 1; // 1=居中
debugLog.push(`[${index + 1}] 已居中:${getImageInfo(inlineShape)}`);
});
alert(`✨ 完成!\n\n${debugLog.join('\n')}`);
居中逻辑:通过设置图片所在段落的对齐方式为 “居中”(Alignment = 1)。
兼容性:嵌入式图片依赖段落格式居中,浮动图片需额外计算坐标(代码中未体现,可能需后续优化)。
3.辅助函数与细节
(1)错误处理与日志
catch (e) {
alert(`❌ 底层错误:${e.message}`);
saveDebugLog(debugLog.concat(`错误堆栈:${e.stack}`)); // 保存日志到桌面
}
function saveDebugLog(logs) {
const fso = new ActiveXObject("Scripting.FileSystemObject");
const desktop = fso.GetAbsolutePathName("Desktop");
const ts = fso.CreateTextFile(desktop + "\\WPS64_Debug.log", true);
logs.forEach(line => ts.WriteLine(line.replace(/\u0000/g, "")));
}
作用:捕获代码执行中的错误,生成详细日志(路径:桌面WPS64_Debug.log)。
用途:调试时通过日志分析未识别图片的原因(如 Type 值错误、尺寸过小)。
(2)图片信息获取
function getImageInfo(inlineShape) {
return `Type:${inlineShape.Type}, 尺寸:${inlineShape.Width}x${inlineShape.Height}, 地址:${inlineShape.ID}`;
}
作用:记录图片的类型、尺寸和唯一标识(ID),用于调试定位。
4.代码特点与适用场景
(1)技术特点
兼容性:针对 WPS 64 位版设计,使用索引循环遍历 COM 对象(如InlineShapes.Item(i))。
全面性:覆盖正文、文本框、表格三大场景,支持多层嵌套图片。
调试性:详细的日志记录,方便排查 “未找到图片” 等问题(如 Type 值是否包含正确类型)。
(2)适用场景
图片来源:截图粘贴(嵌入式)、插入本地图片(浮动式)、文本框 / 表格内的图片。
格式要求:文档需保存为.docm(启用宏的格式),宏安全设置为 “低” 或允许运行。
5.可能的问题与优化
(1)Type 值问题
现状:代码中[3, 5, 13, 2025]是经验值,不同 WPS 版本的 Type 值可能不同(如旧版 Type=3 可能是公式,需结合尺寸和日志判断)。
优化:通过debugLog输出所有 Shape 的 Type 值,动态调整允许的 Type 列表。
(2)浮动图片居中
不足:当前代码通过段落居中处理嵌入式图片,浮动图片需额外计算坐标(如shape.Left = (页面宽度 - 图片宽度)/2)。
改进:添加对Shape(浮动图片)的单独处理逻辑,区分嵌入式和浮动式图片的居中方式。
6.使用建议
(1)调试步骤:
运行宏后查看桌面日志WPS64_Debug.log,确认图片的 Type 值是否在允许列表内。
若未找到图片,检查右键图片的 “文字环绕” 是否为 “嵌入型”(嵌入式图片)或 “浮于文字上方”(浮动式图片)。
(2)版本兼容:
若提示TextFrame2不存在,可能是 WPS 旧版,需改用TextFrame。
64 位版推荐使用Type=5(嵌入式截图)和Type=13(浮动图片),避免依赖 Type=3。
(3)安全设置:
保存文档为.docm,在 “开发工具→宏安全性” 中设置为 “禁用无数字签署的宏”(推荐)。
(三)新建组件并添加到选项卡中
这个方式在我的另外一篇文章中已经讲解过了,具体的可以参照CSDN文章:在WPS中通过JavaScript宏(JSA)调用本地DeepSeek API优化文档教程:在WPS中通过JavaScript宏(JSA)调用本地DeepSeek API优化文档教程_wps调用api-CSDN博客。
【文件】选项卡 --> 【选项】,之后如下图:
创建成功之后:
三、常见问题预判
问题现象 | 原因分析 | 解决方案 |
嵌入式图片未居中 | 段落格式被其他样式覆盖 | 手动右键图片 > 段落 > 居中,确认代码生效后排查样式冲突 |
浮动图片偏移 | 页边距计算错误 | 改为 shape.Left = doc.PageSetup.PageWidth / 2 - shape.Width / 2(更简洁) |
日志显示无图片 | 图片被包裹在文本框 / 表格 | 新增文本框 / 表格内图片的递归查找(需进一步定制) |