工作表調整大小的可行解決方案

背景

在文章管理 OLE中,我們說明了如何使用 Aspose.Slides for Python via .NET 為 PowerPoint 簡報加入 OLE 框。為了解決物件預覽問題,我們將選取工作表區域的影像指派給 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.set_ole_size定義顯示範圍。預覽以 96 DPI 進行渲染,並使用每英寸 72 點將像素尺寸轉換為點數。ShapeCollection.add_ole_object_frame將已修改的活頁簿以這些尺寸嵌入框中。預覽透過OleObjectFrame.substitute_picture_format指派,以取代已儲存簡報中預設的「EMBEDDED OLE OBJECT」佔位文字。

預覽協助程式將PageSetup.print_area設為選取範圍,並移除邊界、頁首與頁尾。啟用ImageOrPrintOptions.only_area後,SheetRender.to_image將該範圍渲染為 PNG 影像。協助程式在返回給簡報使用前,會先將影像串流倒回開頭。

import io
import aspose.cells as cells

def create_ole_image(cell_range, image_resolution):
    page_setup = cell_range.worksheet.page_setup
    page_setup.print_area = cell_range.address
    page_setup.left_margin = 0.0
    page_setup.right_margin = 0.0
    page_setup.top_margin = 0.0
    page_setup.bottom_margin = 0.0
    page_setup.clear_header_footer()

    image_options = cells.rendering.ImageOrPrintOptions()
    image_options.image_type = cells.drawing.ImageType.PNG
    image_options.vertical_resolution = image_resolution
    image_options.horizontal_resolution = image_resolution
    image_options.one_page_per_sheet = True
    image_options.only_area = True

    sheet_render = cells.rendering.SheetRender(cell_range.worksheet, image_options)
    image_data = io.BytesIO()

    sheet_render.to_image(0, image_data)
    image_data.seek(0)

    return image_data

以下完整範例結合協助程式與活頁簿嵌入,並將簡報儲存為 output.pptx。

import io
import aspose.slides as slides
import aspose.cells as cells

def create_ole_image(cell_range, image_resolution):
    page_setup = cell_range.worksheet.page_setup
    page_setup.print_area = cell_range.address
    page_setup.left_margin = 0.0
    page_setup.right_margin = 0.0
    page_setup.top_margin = 0.0
    page_setup.bottom_margin = 0.0
    page_setup.clear_header_footer()

    image_options = cells.rendering.ImageOrPrintOptions()
    image_options.image_type = cells.drawing.ImageType.PNG
    image_options.vertical_resolution = image_resolution
    image_options.horizontal_resolution = image_resolution
    image_options.one_page_per_sheet = True
    image_options.only_area = True

    sheet_render = cells.rendering.SheetRender(cell_range.worksheet, image_options)
    image_data = io.BytesIO()

    sheet_render.to_image(0, image_data)
    image_data.seek(0)

    return image_data

start_row, row_count = 0, 10
start_column, column_count = 0, 13
worksheet_index = 0

image_resolution = 96

with cells.Workbook("sample.xlsx") as workbook:
    worksheet = workbook.worksheets[worksheet_index]

    # 設定工作簿檔案作為 PowerPoint 中 OLE 物件時的顯示尺寸。
    last_row = start_row + row_count - 1
    last_column = start_column + column_count - 1
    workbook.worksheets.set_ole_size(start_row, last_row, start_column, last_column)

    cell_range = worksheet.cells.create_range(start_row, start_column, row_count, column_count)
    image_stream = create_ole_image(cell_range, image_resolution)

    # 取得 OLE 影像的寬度與高度(單位為點)。
    with slides.Images.from_stream(image_stream) as image:
        image_width = image.width * 72 / image_resolution
        image_height = image.height * 72 / image_resolution

    # 我們需要使用已修改的工作簿。
    with io.BytesIO() as ole_stream:
        workbook.save(ole_stream, cells.SaveFormat.XLSX)

        with slides.Presentation() as presentation:
            slide = presentation.slides[0]

            # 將 OLE 影像加入簡報資源。
            image_stream.seek(0)
            ole_image = presentation.images.add_image(image_stream)

            # 建立 OLE 物件框。
            data_info = slides.dom.ole.OleEmbeddedDataInfo(ole_stream.getvalue(), "xlsx")
            ole_frame = slide.shapes.add_ole_object_frame(10, 10, image_width, image_height, data_info)
            ole_frame.substitute_picture_format.picture.image = ole_image
            ole_frame.is_object_icon = False

            presentation.save("output.pptx", slides.export.SaveFormat.PPTX)

縮放儲存格範圍尺寸

此方法將選取列的高度與選取欄的寬度縮放,以符合自訂的 OLE 框尺寸。

範例載入現有的 Excel 活頁簿,並使用零基索引選取其第一個工作表的前 10 列與 13 欄。WorksheetCollection.set_ole_size定義顯示範圍,以下協助程式將該範圍縮放至寬 400 點、高 100 點的框內。將修改後的活頁簿儲存至串流後,ShapeCollection.add_ole_object_frame會依指定尺寸將其嵌入。以 96 DPI 渲染的預覽透過OleObjectFrame.substitute_picture_format指派,因此已儲存的簡報會顯示選取的儲存格,而非預設佔位文字。

縮放協助程式會為選取範圍計算寬度與高度比例,保留其欄的相對寬度與列的相對高度。它會先將計算出的尺寸從點數轉換為英吋,然後使用Cells.set_column_width_inch與Cells.set_row_height_inch套用。選取的範圍必須具備非零的寬度與高度。

import aspose.cells as cells

# <param name="width">預期的儲存格範圍寬度(單位為點)。</param>
# <param name="height">預期的儲存格範圍高度(單位為點)。</param>
def scale_cell_range(cell_range, width, height):
    range_width = cell_range.width
    range_height = cell_range.height

    for i in range(cell_range.column_count):
        column_index = cell_range.first_column + i
        column_width = cell_range.worksheet.cells.get_column_width(column_index, False, cells.CellsUnitType.POINT)

        new_column_width = column_width * width / range_width
        width_in_inches = new_column_width / 72
        cell_range.worksheet.cells.set_column_width_inch(column_index, width_in_inches)

    for i in range(cell_range.row_count):
        row_index = cell_range.first_row + i
        row_height = cell_range.worksheet.cells.get_row_height(row_index, False, cells.CellsUnitType.POINT)

        new_row_height = row_height * height / range_height
        height_in_inches = new_row_height / 72
        cell_range.worksheet.cells.set_row_height_inch(row_index, height_in_inches)

此預覽協助程式使用SheetRender.to_image渲染調整大小的範圍。它將PageSetup.print_area限制於該範圍,移除邊界、頁首與頁尾,並返回定位於開頭的 PNG 串流。

import io
import aspose.cells as cells

def create_ole_image(cell_range, image_resolution):
    page_setup = cell_range.worksheet.page_setup
    page_setup.print_area = cell_range.address
    page_setup.left_margin = 0.0
    page_setup.right_margin = 0.0
    page_setup.top_margin = 0.0
    page_setup.bottom_margin = 0.0
    page_setup.clear_header_footer()

    image_options = cells.rendering.ImageOrPrintOptions()
    image_options.image_type = cells.drawing.ImageType.PNG
    image_options.vertical_resolution = image_resolution
    image_options.horizontal_resolution = image_resolution
    image_options.one_page_per_sheet = True
    image_options.only_area = True

    sheet_render = cells.rendering.SheetRender(cell_range.worksheet, image_options)
    image_data = io.BytesIO()

    sheet_render.to_image(0, image_data)
    image_data.seek(0)

    return image_data

以下完整範例結合協助程式與活頁簿嵌入,並將簡報儲存為 output.pptx。

import io
import aspose.slides as slides
import aspose.cells as cells

# <param name="width">儲存格範圍的預期寬度(單位為點)。</param>
# <param name="height">儲存格範圍的預期高度(單位為點)。</param>
def scale_cell_range(cell_range, width, height):
    range_width = cell_range.width
    range_height = cell_range.height

    for i in range(cell_range.column_count):
        column_index = cell_range.first_column + i
        column_width = cell_range.worksheet.cells.get_column_width(column_index, False, cells.CellsUnitType.POINT)

        new_column_width = column_width * width / range_width
        width_in_inches = new_column_width / 72
        cell_range.worksheet.cells.set_column_width_inch(column_index, width_in_inches)

    for i in range(cell_range.row_count):
        row_index = cell_range.first_row + i
        row_height = cell_range.worksheet.cells.get_row_height(row_index, False, cells.CellsUnitType.POINT)

        new_row_height = row_height * height / range_height
        height_in_inches = new_row_height / 72
        cell_range.worksheet.cells.set_row_height_inch(row_index, height_in_inches)

def create_ole_image(cell_range, image_resolution):
    page_setup = cell_range.worksheet.page_setup
    page_setup.print_area = cell_range.address
    page_setup.left_margin = 0.0
    page_setup.right_margin = 0.0
    page_setup.top_margin = 0.0
    page_setup.bottom_margin = 0.0
    page_setup.clear_header_footer()

    image_options = cells.rendering.ImageOrPrintOptions()
    image_options.image_type = cells.drawing.ImageType.PNG
    image_options.vertical_resolution = image_resolution
    image_options.horizontal_resolution = image_resolution
    image_options.one_page_per_sheet = True
    image_options.only_area = True

    sheet_render = cells.rendering.SheetRender(cell_range.worksheet, image_options)
    image_data = io.BytesIO()

    sheet_render.to_image(0, image_data)
    image_data.seek(0)

    return image_data

start_row, row_count = 0, 10
start_column, column_count = 0, 13
worksheet_index = 0

image_resolution = 96
frame_width, frame_height = 400.0, 100.0

with cells.Workbook("sample.xlsx") as workbook:
    worksheet = workbook.worksheets[worksheet_index]

    # 設定工作簿檔案作為 PowerPoint 中 OLE 物件時的顯示尺寸。
    last_row = start_row + row_count - 1
    last_column = start_column + column_count - 1
    workbook.worksheets.set_ole_size(start_row, last_row, start_column, last_column)

    # 將儲存格範圍縮放以符合框的尺寸。
    cell_range = worksheet.cells.create_range(start_row, start_column, row_count, column_count)
    scale_cell_range(cell_range, frame_width, frame_height)

    image_stream = create_ole_image(cell_range, image_resolution)

    # 我們需要使用已修改的工作簿。
    with io.BytesIO() as ole_stream:
        workbook.save(ole_stream, cells.SaveFormat.XLSX)

        with slides.Presentation() as presentation:
            slide = presentation.slides[0]

            # 將 OLE 影像加入簡報資源。
            ole_image = presentation.images.add_image(image_stream)

            # 建立 OLE 物件框。
            data_info = slides.dom.ole.OleEmbeddedDataInfo(ole_stream.getvalue(), "xlsx")
            ole_frame = slide.shapes.add_ole_object_frame(10, 10, frame_width, frame_height, data_info)
            ole_frame.substitute_picture_format.picture.image = ole_image
            ole_frame.is_object_icon = False

            presentation.save("output.pptx", slides.export.SaveFormat.PPTX)

結論

常見問題

為何嵌入的 Excel 工作表在 PowerPoint 中首次啟動時會改變大小?

因為 Excel 嘗試在啟動時維持原始視窗大小,而 PowerPoint 中的 OLE 物件框則有自己的尺寸。PowerPoint 與 Excel 會協商尺寸以維持長寬比,導致調整大小的情況發生。

能否完全防止此調整大小問題?

可以。透過將 OLE 框縮放至符合 Excel 儲存格範圍尺寸,或將儲存格範圍縮放至符合目標 OLE 框尺寸,即可避免不必要的調整大小。

應該使用哪種縮放方式,OLE 框縮放還是儲存格範圍縮放?

如果希望保留 Excel 列與欄的原始尺寸,請選擇OLE 框縮放。如果希望在簡報中固定 OLE 框的尺寸,請選擇儲存格範圍縮放。

這些解決方案在使用範本建立的簡報中也有效嗎?

有效。兩種解決方案皆適用於基於範本或從頭建立的簡報。

使用這些方法時,OLE 框的尺寸是否有限制?

沒有。只要適當設定縮放比例,OLE 物件框可以設定為任意大小。

有沒有辦法避免 PowerPoint 中顯示「EMBEDDED OLE OBJECT」佔位文字?

可以。透過擷取目標 Excel 儲存格範圍的快照,並將其設為 OLE 框的佔位圖片,即可在簡報中顯示自訂的預覽影像,取代預設的佔位文字。