工作表大小调整的可行方案
注意
使用 Aspose 组件将 Excel 工作表作为 OLE 对象嵌入 PowerPoint 演示文稿后,首次激活时可能会出现意外的大小调整。此行为会导致 OLE 对象在激活前后的视觉差异明显。本文章解释了此问题并描述了两种解决方案。背景
在文章Manage OLE中,我们阐述了如何使用 Aspose.Slides for Node.js via Java 向 PowerPoint 演示文稿添加 OLE 框。为了解决object preview issue,我们为 OLE 对象框分配了选定工作表区域的图像。在输出的演示文稿中,双击显示工作表图像的 OLE 对象框会激活 Excel 工作簿。最终用户可以对实际的 Excel 工作簿进行任意更改,然后点击激活的 Excel 工作簿外部返回幻灯片。返回幻灯片时,OLE 对象框的大小会发生变化。调整因子取决于 OLE 对象框和嵌入的 Excel 工作簿的尺寸。
调整大小的原因
由于 Excel 工作簿有自己的窗口大小,它在首次激活时会尝试保留原始尺寸。另一方面,OLE 对象框有自己的尺寸。根据 Microsoft 的说法,当 Excel 工作簿被激活时,Excel 与 PowerPoint 会协商尺寸,以确保在嵌入过程中保持正确的比例。大小调整基于 Excel 窗口尺寸与 OLE 对象框尺寸和位置之间的差异发生。
可行方案
有两种可能的方案可以避免大小调整效果。
- 将 PowerPoint 演示文稿中的 OLE 框缩放至与选定行列的总高度和宽度匹配。
- 保持 OLE 框尺寸不变,缩放选定的行列以适应其内部。
缩放 OLE 框大小
此方法将 OLE 框的大小设置为与嵌入的 Excel 工作表中选定行列的总高度和宽度匹配。
示例加载现有 Excel 工作簿,并使用零基索引选取其第一个工作表的前 10 行和前 13 列。WorksheetCollection.setOleSize定义显示范围。预览以 96 DPI 渲染,其像素尺寸使用每英寸 72 点转换为点数。ShapeCollection.addOleObjectFrame使用该尺寸将修改后的工作簿嵌入框中。预览通过OleObjectFrame.getSubstitutePictureFormat分配,以替换已保存演示文稿中的“EMBEDDED OLE OBJECT”占位符。
安装 aspose.cells、aspose.slides.via.java 和 java 包,并将 sample.xlsx 放置在工作目录中。对每种方法,首先运行准备脚本,然后在同一目录的单独 Node.js 进程中运行嵌入脚本。两个 Aspose 包分别初始化 Java 桥接。
将此准备脚本保存为 prepare-frame-scaled.js 并使用 node prepare-frame-scaled.js 运行。它会生成 frame-scaled.xlsx 和 frame-scaled.png。
const cells = require("aspose.cells");
function createOleImage(cellRange, imageResolution, imagePath) {
const worksheet = cellRange.getWorksheet();
const pageSetup = worksheet.getPageSetup();
pageSetup.setPrintArea(cellRange.getAddress());
pageSetup.setLeftMargin(0);
pageSetup.setRightMargin(0);
pageSetup.setTopMargin(0);
pageSetup.setBottomMargin(0);
pageSetup.clearHeaderFooter();
const imageOptions = new cells.ImageOrPrintOptions();
imageOptions.setImageType(cells.ImageType.PNG);
imageOptions.setVerticalResolution(imageResolution);
imageOptions.setHorizontalResolution(imageResolution);
imageOptions.setOnePagePerSheet(true);
imageOptions.setOnlyArea(true);
const sheetRender = new cells.SheetRender(worksheet, imageOptions);
sheetRender.toImage(0, imagePath);
}
function prepareWorkbook(outputName) {
const workbook = new cells.Workbook("sample.xlsx");
try {
const startRow = 0;
const rowCount = 10;
const startColumn = 0;
const columnCount = 13;
const worksheet = workbook.getWorksheets().get(0);
const lastRow = startRow + rowCount - 1;
const lastColumn = startColumn + columnCount - 1;
workbook.getWorksheets().setOleSize(startRow, lastRow, startColumn, lastColumn);
const cellRange = worksheet.getCells().createRange(startRow, startColumn, rowCount, columnCount);
createOleImage(cellRange, 96, outputName + ".png");
workbook.save(outputName + ".xlsx", cells.SaveFormat.XLSX);
return true;
} finally {
workbook.dispose();
}
}
prepareWorkbook("frame-scaled");
// 所有同步工作已完成;停止 Java 桥接进程。
process.exit(0);
将此嵌入脚本保存为 embed-frame-scaled.js 并使用 node embed-frame-scaled.js 运行。它会读取这些文件并写入 frame-scaled.pptx。
const fs = require("fs");
const java = require("java");
const slides = require("aspose.slides.via.java");
function embedWorkbook(inputName, useFixedFrame) {
const imageBuffer = fs.readFileSync(inputName + ".png");
const imageBytes = java.newArray("byte", Array.from(imageBuffer));
const workbookBuffer = fs.readFileSync(inputName + ".xlsx");
const workbookBytes = java.newArray("byte", Array.from(workbookBuffer));
const image = slides.Images.fromData(imageBytes);
try {
const imageResolution = 96;
const frameWidth = useFixedFrame ? 400 : image.getWidth() * 72 / imageResolution;
const frameHeight = useFixedFrame ? 100 : image.getHeight() * 72 / imageResolution;
const presentation = new slides.Presentation();
try {
const slide = presentation.getSlides().get_Item(0);
const dataInfo = new slides.OleEmbeddedDataInfo(workbookBytes, "xlsx");
const oleFrame = slide.getShapes().addOleObjectFrame(java.newFloat(10), java.newFloat(10), java.newFloat(frameWidth), java.newFloat(frameHeight), dataInfo);
const previewImage = presentation.getImages().addImage(image);
oleFrame.getSubstitutePictureFormat().getPicture().setImage(previewImage);
oleFrame.setObjectIcon(false);
presentation.save(inputName + ".pptx", slides.SaveFormat.Pptx);
} finally {
presentation.dispose();
}
} finally {
image.dispose();
}
}
embedWorkbook("frame-scaled", false);
// 所有同步工作已完成;停止 Java 桥接进程。
process.exit(0);
预览助手将PageSetup.setPrintArea设置为选定范围,并移除页边距、页眉和页脚。启用ImageOrPrintOptions.setOnlyArea后,SheetRender.toImage将范围渲染为 PNG 图像。辅助程序将 PNG 图像保存到文件以供演示文稿使用。
缩放单元格范围大小
此方法将选定行的高度和选定列的宽度缩放至匹配自定义 OLE 框大小。
示例加载现有 Excel 工作簿,并使用零基索引选取其第一个工作表的前 10 行和前 13 列。WorksheetCollection.setOleSize定义显示范围,助手将该范围缩放至宽 400 点、高 100 点的框中。将修改后的工作簿保存为文件后,ShapeCollection.addOleObjectFrame按指定大小嵌入。以 96 DPI 渲染的预览通过OleObjectFrame.getSubstitutePictureFormat分配,因此已保存的演示文稿显示选定的单元格而非默认占位符。
将此准备脚本保存为 prepare-cells-scaled.js 并使用 node prepare-cells-scaled.js 运行。它会生成 cells-scaled.xlsx 和 cells-scaled.png。
const cells = require("aspose.cells");
function scaleCellRange(cellRange, width, height) {
const rangeWidth = cellRange.getWidth();
const rangeHeight = cellRange.getHeight();
if (rangeWidth <= 0 || rangeHeight <= 0) {
console.error("The selected range must have nonzero width and height.");
return false;
}
const worksheetCells = cellRange.getWorksheet().getCells();
for (let i = 0; i < cellRange.getColumnCount(); i++) {
const columnIndex = cellRange.getFirstColumn() + i;
const columnWidth = worksheetCells.getColumnWidth(columnIndex, false, cells.CellsUnitType.POINT);
const widthInInches = columnWidth * width / rangeWidth / 72;
worksheetCells.setColumnWidthInch(columnIndex, widthInInches);
}
for (let i = 0; i < cellRange.getRowCount(); i++) {
const rowIndex = cellRange.getFirstRow() + i;
const rowHeight = worksheetCells.getRowHeight(rowIndex, false, cells.CellsUnitType.POINT);
const heightInInches = rowHeight * height / rangeHeight / 72;
worksheetCells.setRowHeightInch(rowIndex, heightInInches);
}
return true;
}
function createOleImage(cellRange, imageResolution, imagePath) {
const worksheet = cellRange.getWorksheet();
const pageSetup = worksheet.getPageSetup();
pageSetup.setPrintArea(cellRange.getAddress());
pageSetup.setLeftMargin(0);
pageSetup.setRightMargin(0);
pageSetup.setTopMargin(0);
pageSetup.setBottomMargin(0);
pageSetup.clearHeaderFooter();
const imageOptions = new cells.ImageOrPrintOptions();
imageOptions.setImageType(cells.ImageType.PNG);
imageOptions.setVerticalResolution(imageResolution);
imageOptions.setHorizontalResolution(imageResolution);
imageOptions.setOnePagePerSheet(true);
imageOptions.setOnlyArea(true);
const sheetRender = new cells.SheetRender(worksheet, imageOptions);
sheetRender.toImage(0, imagePath);
}
function prepareWorkbook(outputName, scaleCells) {
const workbook = new cells.Workbook("sample.xlsx");
try {
const startRow = 0;
const rowCount = 10;
const startColumn = 0;
const columnCount = 13;
const worksheet = workbook.getWorksheets().get(0);
const lastRow = startRow + rowCount - 1;
const lastColumn = startColumn + columnCount - 1;
workbook.getWorksheets().setOleSize(startRow, lastRow, startColumn, lastColumn);
const cellRange = worksheet.getCells().createRange(startRow, startColumn, rowCount, columnCount);
if (scaleCells && !scaleCellRange(cellRange, 400, 100)) {
return false;
}
createOleImage(cellRange, 96, outputName + ".png");
workbook.save(outputName + ".xlsx", cells.SaveFormat.XLSX);
return true;
} finally {
workbook.dispose();
}
}
const cellsPrepared = prepareWorkbook("cells-scaled", true);
// 所有同步工作已完成;停止 Java 桥接进程。
process.exit(cellsPrepared ? 0 : 1);
将此嵌入脚本保存为 embed-cells-scaled.js 并使用 node embed-cells-scaled.js 运行。它会读取这些文件并写入 cells-scaled.pptx。
const fs = require("fs");
const java = require("java");
const slides = require("aspose.slides.via.java");
function embedWorkbook(inputName, useFixedFrame) {
const imageBuffer = fs.readFileSync(inputName + ".png");
const imageBytes = java.newArray("byte", Array.from(imageBuffer));
const workbookBuffer = fs.readFileSync(inputName + ".xlsx");
const workbookBytes = java.newArray("byte", Array.from(workbookBuffer));
const image = slides.Images.fromData(imageBytes);
try {
const imageResolution = 96;
const frameWidth = useFixedFrame ? 400 : image.getWidth() * 72 / imageResolution;
const frameHeight = useFixedFrame ? 100 : image.getHeight() * 72 / imageResolution;
const presentation = new slides.Presentation();
try {
const slide = presentation.getSlides().get_Item(0);
const dataInfo = new slides.OleEmbeddedDataInfo(workbookBytes, "xlsx");
const oleFrame = slide.getShapes().addOleObjectFrame(java.newFloat(10), java.newFloat(10), java.newFloat(frameWidth), java.newFloat(frameHeight), dataInfo);
const previewImage = presentation.getImages().addImage(image);
oleFrame.getSubstitutePictureFormat().getPicture().setImage(previewImage);
oleFrame.setObjectIcon(false);
presentation.save(inputName + ".pptx", slides.SaveFormat.Pptx);
} finally {
presentation.dispose();
}
} finally {
image.dispose();
}
}
embedWorkbook("cells-scaled", true);
// 所有同步工作已完成;停止 Java 桥接进程。
process.exit(0);
缩放助手为选定范围计算宽高比例,分别保持列的相对宽度和行的相对高度。它在使用Cells.setColumnWidthInch和Cells.setRowHeightInch设置之前,将计算得到的尺寸从点转换为英寸。选定范围的宽度和高度必须非零。
此预览助手使用SheetRender.toImage渲染已调整大小的范围。它将PageSetup.setPrintArea限制在该范围内,移除页边距、页眉和页脚,并将 PNG 图像保存到文件。
结论
注意
有两种方法可以解决工作表大小调整问题。请选择最符合您需求和使用场景的方法。两种方法在从模板创建或从头创建演示文稿时都以同样方式工作。此外,此解决方案对 OLE 对象框的大小没有限制。常见问题
为什么嵌入的 Excel 工作表在 PowerPoint 中首次激活时会改变大小?
因为 Excel 在激活时尝试保持原始窗口大小,而 PowerPoint 中的 OLE 对象框具有自己的尺寸。PowerPoint 与 Excel 会协商尺寸以保持纵横比,这会导致大小调整。
是否可以完全防止此大小调整问题?
可以。通过将 OLE 框缩放至适合 Excel 单元格范围的大小,或将单元格范围缩放至适合所需的 OLE 框大小,均可防止不希望的大小调整。
应该使用哪种缩放方法,OLE 框缩放还是单元格范围缩放?
如果希望保持原始 Excel 行列的尺寸,请选择 OLE 框缩放。如果希望在演示文稿中拥有固定的 OLE 框尺寸,请选择 单元格范围缩放。
这些解决方案在基于模板的演示文稿中是否同样适用?
是的。两种方案都适用于从模板创建的演示文稿以及从头创建的演示文稿。
使用这些方法时 OLE 框的尺寸是否有限制?
没有限制。只要适当设置比例,OLE 对象框可以任意大小。
是否有办法避免 PowerPoint 中的 “EMBEDDED OLE OBJECT” 占位符文本?
有。通过对目标 Excel 单元格范围进行快照并将其设置为 OLE 框的占位图像,可在默认占位符位置显示自定义预览图像。
相关文档
Creating an Excel Chart and Embedding It in a Presentation as an OLE Object
Updating OLE Objects Automatically Using an MS PowerPoint Add-In