简体   繁体   English

如何从powerpoint(python-pptx)访问图像

[英]How can I access images from powerpoint (python-pptx)

I'm having a hard time trying to access/save images using the python-pptx library.我很难尝试使用 python-pptx 库访问/保存图像。 So, if the image is of shape type PICTURE (that's shape.shape_type == MSO_SHAPE_TYPE.PICTURE ) I can access/save the image easily using the 'blob' attribute.因此,如果图像的形状类型为PICTURE (即shape.shape_type == MSO_SHAPE_TYPE.PICTURE ),我可以使用“blob”属性轻松访问/保存图像。 Here is the code:这是代码:

import argparse
import os
from PIL import Image
import pptx
from pptx.enum.shapes import MSO_SHAPE_TYPE
from pptx import Presentation
from mdutils.mdutils import MdUtils
from mdutils import Html

def main():

    parser = argparse.ArgumentParser()
    parser.add_argument('ppt_name', type=str, help='add the name of the PowerPoint file(NOTE: the folder must be in the same directory as the prorgram file')
    args = parser.parse_args()
    
    pptx_name = args.ppt_name
    pptx_name_formatted = pptx_name.split('.')[0]

    prs = Presentation(pptx_name)

    path = '{}_converted'.format(pptx_name_formatted)
    if not os.path.exists(path):
        os.mkdir(path)
    images_folder = '{}_images'.format(pptx_name_formatted)
    images_path = os.path.join(path, images_folder)
    if not os.path.exists(images_path):
        os.mkdir(images_path)

    ppt_dict = {} #Keys: slide numbers, values: slide content
    texts = []
    slide_count = 0
    picture_count = 0
    for slide in prs.slides:
        texts = []
        slide_count += 1
        
        for shape in slide.shapes:
            if shape.has_text_frame:
                if '\n' in shape.text:
                    splitted = shape.text.split('\n')
                    for word in splitted:
                        if word != '':
                            texts.append(word)
                elif shape.text == '':
                    continue
                else:
                    texts.append(shape.text)
            elif shape.shape_type == MSO_SHAPE_TYPE.PICTURE:
                with open('{}/image{}_slide{}.png'.format(images_path, picture_count, slide_count), 'wb') as f:
                    f.write(shape.image.blob)
                    picture_count += 1
            ppt_dict[slide_count] = texts

    ppt_content = ''
    for k,v in ppt_dict.items():
        ppt_content = ppt_content + ' - Slide number {}\n'.format(k)
        for a in v:
            ppt_content = ppt_content + '\t - {}\n'.format(a)

    mdFile = MdUtils(file_name='{}/{}'.format(path,path)) #second argument isn't path, it just shares the path name.
    mdFile.write(ppt_content)
    mdFile.create_md_file()


if __name__ == "__main__":
    main()

The problem is when the picture is of shape type 'auto shape' , I tried a lot of approaches but to no avail.问题是当图片的形状类型为 'auto shape' 时,我尝试了很多方法但无济于事。 When I do run the following code for a shape that I know is a picture:当我为我知道是图片的形状运行以下代码时:

         if shape.shape_type == MSO_SHAPE_TYPE.AUTO_SHAPE:
                print(shape.auto_shape_type)
                print(shape.fill.type)

#indented because it's in a for loop

It outputs RECTANGLE for shape.auto_shape_type它为shape.auto_shape_type输出RECTANGLE

and PICTURE for shape.fill.typeshape.fill.type PICTURE

But what I want now is to save the picture (maybe by writing the the binary image bytestream of the image).但我现在想要的是保存图片(可能通过写入图像的二进制图像字节流)。 Can someone help?有人可以帮忙吗?

The "link" to the image (part, having the blob) is in the fill definition.到图像(部分,有 blob)的“链接”在填充定义中。 Using that you can get to the image.使用它你可以得到图像。

Print out the XML for the surroundings of the fill definition with shape.fill._xPr.xml .使用shape.fill._xPr.xml打印填充定义周围的 XML。 That will give you a look at what you need to navigate to.这将使您了解需要导航到的内容。 Good chance it will look something like "rId9" with some particular other number where the "9" placeholder is in that example.很有可能它看起来像"rId9"带有一些特定的其他数字,其中“9”占位符在该示例中。 Probably in the neighborhood of something like "blipfill" .可能在诸如"blipfill"类的东西附近。 The image is used as the "fill" of the shape, so that's what's going on here.图像被用作形状的“填充”,所以这就是这里发生的事情。

Then get the slide part with something like slide._part and use its .related_parts "dict" to look up the image "fill" part using the relationship-id (the string like "rId9").然后使用类似slide._part获取幻灯片部分,并使用其.related_parts "dict" 使用relation-id (类似“rId9”的字符串)查找图像“填充”部分。

image_part = slide._part.related_parts["rId9"]

The ImagePart implementation is here: ImagePart实现在这里:
https://github.com/scanny/python-pptx/blob/master/pptx/parts/image.py#L21 https://github.com/scanny/python-pptx/blob/master/pptx/parts/image.py#L21
and it gives access to the image and a lot of details about it as well.它可以访问图像以及有关它的许多详细信息。

You'll have to retrieve the "rId9"-like string using lxml calls, something roughly like:您必须使用lxml调用检索类似“rId9”的字符串,大致类似于:

rIds = shape.fill._xPr.xpath(".//@embed")
rId = rIds[0]

You'll need to do a little research on XPath to work out the right expression, based on the XML you print out in the earlier step.您需要对 XPath 进行一些研究,以根据您在前面步骤中打印出的 XML 计算出正确的表达式。 There's a lot out there on XPath, including here on SO, this is one resource to get started: http://www.rpbourret.com/xml/XPathIn5.htm XPath 上有很多内容,包括这里的 SO,这是一个入门资源: http : //www.rpbourret.com/xml/XPathIn5.htm

If you can't work it out, post the XML you printed out and we can get you to the next step.如果您无法解决,请发布您打印的 XML,我们可以让您进入下一步。

Here is my approach, thanks to scanny .这是我的方法,感谢scanny

    for slide in prs.slides:
        slide_count += 1

        slide_parts = list(slide._part.related_parts.keys())
        for part in slide_parts:
            image_part = slide._part.related_parts[part]
            if type(image_part) == pptx.parts.image.ImagePart or pptx.opc.package.Part:
                file_startswith = image_part.blob[0:1]
                if file_startswith == b'\x89' or file_startswith == b'\xff' or file_startswith == b'\x47':
                    with open('{}/image{}_slide{}.png'.format(images_path, picture_count, slide_count), 'wb') as f:
                        f.write(image_part.blob)
                        picture_count += 1

the if condition to check for PNG, JPEG or GIF is there because pptx.opc.package.Part isn't always an image.检查 PNG、JPEG 或 GIF 的 if 条件存在,因为pptx.opc.package.Part并不总是图像。

Actually, I think since I'm checking for the beginning of image_part.blob , I don't think I need to include say if type(image_part) == pptx.parts.image.ImagePart or pptx.opc.package.Part:实际上,我认为因为我正在检查image_part.blob的开头, image_part.blob我认为我不需要包括if type(image_part) == pptx.parts.image.ImagePart or pptx.opc.package.Part:

But as long as it's working...但只要它工作...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM