python-docx 可以添加作为外部图像文件链接的图片吗?

问题描述 投票:0回答:1

我正在使用 python-docx 库(https://python-docx.readthedocs.io/)来编写 Word 文档。

嵌入图片

我可以使用 python-docx 库的 API 添加嵌入文档中的图片。例如:

from docx import Document

doc = Document()
par = doc.add_paragraph( 'Embed picture:' )
run = par.add_run()
run.add_picture( 'puppy.png' )
doc.save( 'test.docx' )

图片成为存储在文档内的 ImagePart:

word/
  media/
    image1.png

指向该图像部分的关系是:

<Relationships ...>
  ...
  <Relationship Id="rId4" Type=".../image" Target="media/image1.png"/>

链接图片

我更愿意添加链接到外部图像文件的图片。您可以在 Microsoft Word 中执行此操作。 python-docx 库没有为此提供方便的 API。然而,一些底层代码似乎支持链接到外部文件

因此,我开始了自己的实用功能,将图片链接到外部文件。例如:

from docx import Document

doc = Document()
par = doc.add_paragraph( 'Link picture:' )
run = par.add_run()
Util.Run_add_picture( run, 'puppy.png', is_external = True )
doc.save( 'test.docx' )

这会导致图片的关系如下所示:

<Relationships ...>
  ...
  <Relationship Id="rId4" Type=".../image" Target="file:///C:\work\puppy.png" TargetMode="External"/>

令我感到鼓舞的是,一些底层代码似乎支持链接到外部文件。例如,

docx.opc.part.Part.relate_to()
有一个
is_external
参数,默认为 false。为
Run.add_picture()
发明我自己的实用程序包装器以及调用的函数似乎是合理的,这样我就可以传递值为 true 的
is_external
参数。

再例如,

docx.oxml.shape.CT_Blip
具有
embed
link
属性,如果将关系 ID 分配给后者而不是前者,那么它应该渗透到其他位置并在文档中生成所需的标记。为
docx.oxml.shape.CT_Picture.new()
发明一个实用程序包装器以及调用它的函数似乎是合理的,这样我就可以传递值为 true 的
is_external
参数,然后执行如下操作:

@staticmethod
def CT_Picture_new( pic_id, filename, relationship_id, cx, cy, is_external ):
  pic = parse_xml( CT_Picture._pic_xml() )
  pic.nvPicPr.cNvPr.id = pic_id
  pic.nvPicPr.cNvPr.name = filename

  # The important change that pays attention to given is_external parameter.
  if is_external:
    pic.blipFill.blip.link = relationship_id
  else:
    pic.blipFill.blip.embed = relationship_id

  pic.spPr.cx = cx
  pic.spPr.cy = cy
  return pic

到目前为止,我有实用函数来代替,并提供了一种指定

is_external
参数的方法,用于:

  • docx.text.run.Run.add_picture()
  • docx.parts.story.BaseStoryPart.new_pic_inline()
  • docx.parts.story.BaseStoryPart.get_or_add_image()
  • docx.oxml.shape.CT_Inline.new_pic_inline()
  • docx.oxml.shape.CT_Picture.new()

兔子洞太多了

唉,我保存文档时还是出现异常。

类型错误:参数必须是字节或 unicode,得到“ImagePart”

我认为我必须改变的下一件事是如何填充

Relationship
对象;
target_ref
属性只需分配一个外部图像文件的路径,而不是
ImagePart
对象。

我必须暂时放弃。是否有计划支持将图片链接到外部图像文件?或者,更简单的解决方法?

python image ms-word docx python-docx
1个回答
0
投票

我也有同样的问题。这就是我用里面的评论解决它的方法


from docx.opc.constants import RELATIONSHIP_TYPE
from docx.oxml import CT_Inline, parse_xml, CT_Picture
from docx.shape import InlineShape
from docx.shared import Length
from docx.text.run import Run


# noinspection PyProtectedMember
def add_linked_pic(r: Run, image_path: str, width: Length, height: Length) -> InlineShape:
    """
    Image will be inserted as character without embedding, just as link.
    :param r: run
    :param image_path:
        Seems like it has to be absolute path.as_uri() like "file:///full/path/file.jpg".
        It seems that it also works with relative path like "./folder/image.jpg"
    :param width: size of image in document
    :param height: size of image in document
    :return:
    """

    # create RELATION.
    relations = r.part.rels
    rel_id = relations._next_rId
    relations.add_relationship(reltype=RELATIONSHIP_TYPE.IMAGE, target=image_path, rId=rel_id, is_external=True)

    # Comment about pic_id from python-docx creators:
    # -- Word doesn't seem to use this, but does not omit it
    pic_id = 0

    # Next code taken from this method:
    # def new(cls, pic_id, filename, rId, cx, cy):
    # Just one line changed in order to replace `r:embed` with `r:link`.

    # The following fout lines created to make variable names same as in python-docx method.
    filename = image_path  # Filename - something useless. will make it equal to image_path
    cx = width
    cy = height

    # Expand that code as CT_Picture.new(pic_id, filename, rId, cx, cy):
    pic = parse_xml(CT_Picture._pic_xml())
    pic.nvPicPr.cNvPr.id = pic_id
    pic.nvPicPr.cNvPr.name = filename

    # pic.blipFill.blip.embed = rId  # This line is replaced with next one
    pic.blipFill.blip.link = rel_id

    pic.spPr.cx = cx
    pic.spPr.cy = cy

    shape_id = r.part.next_id

    # Now from here: inline = cls.new(cx, cy, shape_id, pic)
    inline = CT_Inline.new(cx, cy, shape_id, pic)
    inline = r._r.add_drawing(inline)

    return InlineShape(inline)

© www.soinside.com 2019 - 2024. All rights reserved.