python-openxml / python-docx

Create and modify Word documents with Python
MIT License
4.47k stars 1.1k forks source link

feature: floating image #159

Open MCopperhead opened 9 years ago

MCopperhead commented 9 years ago

Hi. Is there any possibility to wrap text around image? Like this: 111

scanny commented 9 years ago

The short answer is, not presently.

The best short name for this feature might be "floating image", where the picture is placed in an "anchored" graphical frame. It's related in some ways to the existing add_picture() capability, except that the container has these floating properties rather than being inline, where the picture acts in most respects as a (large-ish) character glyph.

I'll leave this issue open as a feature request for this. It's likely to be some time in coming as there are several large features ahead of it in the backlog. Let me know if you'd like to give it a crack yourself.

neube3 commented 8 years ago

It would be great if floating images' support could be added.

I'm trying to programmatically write text over image (a pretty standard form, doesn't have to be pretty) and your lib is as close as I got. For now I have to resort to generating two halves of a document and printing them on the same, aligned sheet of paper to achieve the two layers' illusion.

Daniel-Quistan commented 7 years ago

Hi!

I manage to do this inserting the picture on a cell docx table and writing the text on adjacent cells.

e.g.

Create table

table = doc.add_table(rows=1, cols=2)

Style from a docx template document, a table with no borders and no liens

table.style = "branco"

insert picture on the first cell

pic_cells = table.rows[0].cells paragraph = pic_cells[0].paragraphs[0] run = paragraph.add_run() run.add_picture('C:\Users\daniel.oliveira\Desktop\Documentos Daniel\money1.jpg', width=Mm(45), height=Mm(25))

inset text on the second cell

tx_cells = table.rows[0].cells tx_cells[0].width = -1 tx_cells[1].text = "Test" doc.save('demo.docx')

you can replicate the image above creating an three columns table and following the same logic

sabya14 commented 5 years ago

any updates?

pkor commented 4 years ago

Yeah, any updates?

miksica commented 4 years ago

++ Need this feature. I've read somwhere that some1 acctualy did make the code of it likein 2015 or 2016, but that it wasnt implemented.

Solving this with with tables isn't exactly the same aws in when you have more text than the height of the picture it doesnt wraparond it.

Also, you cant add picture this way into header and write on top of it.. something along the lines of "picture.position = behind_text"

sachinrevvsales commented 4 years ago

Need this feature on an urgent basis! If anybody has a PR for this Please share the link.

dothinking commented 3 years ago

An implementation of floating picture (wrapping style = behind text) here https://github.com/dothinking/pdf2docx/issues/54#issuecomment-715925252, should work for "square" mode with a bit of modification.

fzyzcjy commented 3 years ago

Any updates? This is very useful! One reason I need to use python-docx is because I do not want to change the image type one by one by hand!

ECCKaka commented 3 years ago

An implementation of floating picture (wrapping style = behind text) here dothinking/pdf2docx#54 (comment), should work for "square" mode with a bit of modification.

changing to will do the work for square text wrap. the original comment: here dothinking/pdf2docx#54 (comment) image

EFH52 commented 1 year ago

I've much interest in this feature being implemented. I wish to create a series of documents based off data table containing 10s or items where the picture to be displayed on a particular page would be governed the the data as it is read into the program. I need to have the image float right. There are 6 images to select from. Some of the images I'd like to be derived from an SVG such that I can modify the contents of the image based on the table before it is published in the .docx.

GaidamakUA commented 1 year ago

This feature would be much appreciated.

Kill0geR commented 6 months ago
import os
from docx.oxml import parse_xml, register_element_cls
from docx.oxml.ns import nsdecls
from docx.oxml.shape import CT_Picture
from docx.oxml.xmlchemy import BaseOxmlElement, OneAndOnlyOne
from docx import Document
from docx.shared import Inches, Pt

class CT_Anchor(BaseOxmlElement):
    extent = OneAndOnlyOne('wp:extent')
    docPr = OneAndOnlyOne('wp:docPr')
    graphic = OneAndOnlyOne('a:graphic')

    @classmethod
    def new(cls, cx, cy, shape_id, pic, pos_x, pos_y):
        anchor = parse_xml(cls._anchor_xml(pos_x, pos_y))
        anchor.extent.cx = cx
        anchor.extent.cy = cy
        anchor.docPr.id = shape_id
        anchor.docPr.name = 'Picture %d' % shape_id
        anchor.graphic.graphicData.uri = (
            'http://schemas.openxmlformats.org/drawingml/2006/picture'
        )
        anchor.graphic.graphicData._insert_pic(pic)
        return anchor

    @classmethod
    def new_pic_anchor(cls, shape_id, rId, filename, cx, cy, pos_x, pos_y):
        pic_id = 0
        pic = CT_Picture.new(pic_id, filename, rId, cx, cy)
        anchor = cls.new(cx, cy, shape_id, pic, pos_x, pos_y)
        anchor.graphic.graphicData._insert_pic(pic)
        return anchor

    @classmethod
    def _anchor_xml(cls, pos_x, pos_y):
        return (
            '<wp:anchor distT="0" distB="0" distL="0" distR="0" simplePos="0" relativeHeight="0" \n'
            '           behindDoc="1" locked="0" layoutInCell="1" allowOverlap="1" \n'
            '           %s>\n'
            '  <wp:simplePos x="0" y="0"/>\n'
            '  <wp:positionH relativeFrom="page">\n'
            '    <wp:posOffset>%d</wp:posOffset>\n'
            '  </wp:positionH>\n'
            '  <wp:positionV relativeFrom="page">\n'
            '    <wp:posOffset>%d</wp:posOffset>\n'
            '  </wp:positionV>\n'                    
            '  <wp:extent cx="914400" cy="914400"/>\n'
            '  <wp:wrapNone/>\n'
            '  <wp:docPr id="666" name="unnamed"/>\n'
            '  <wp:cNvGraphicFramePr>\n'
            '    <a:graphicFrameLocks noChangeAspect="1"/>\n'
            '  </wp:cNvGraphicFramePr>\n'
            '  <a:graphic>\n'
            '    <a:graphicData uri="URI not set"/>\n'
            '  </a:graphic>\n'
            '</wp:anchor>' % ( nsdecls('wp', 'a', 'pic', 'r'), int(pos_x), int(pos_y) )
        )

def new_pic_anchor(part, image_descriptor, width, height, pos_x, pos_y):
    rId, image = part.get_or_add_image(image_descriptor)
    cx, cy = image.scaled_dimensions(width, height)
    shape_id, filename = part.next_id, image.filename
    return CT_Anchor.new_pic_anchor(shape_id, rId, filename, cx, cy, pos_x, pos_y)

# refer to docx.text.run.add_picture
def add_float_picture(p, image_path_or_stream, width=None, height=None, pos_x=0, pos_y=0):
    """Add float picture at fixed position `pos_x` and `pos_y` to the top-left point of page.
    """
    run = p.add_run()
    anchor = new_pic_anchor(run.part, image_path_or_stream, width, height, pos_x, pos_y)
    run._r.add_drawing(anchor)

if __name__ == '__main__':
    register_element_cls('wp:anchor', CT_Anchor)
    document = Document("document.docx")

    p = document.add_paragraph()
    add_float_picture(p, 'image_test.png', width=Inches(2.25), pos_x=Pt(400), pos_y=Pt(30))

    p.add_run('Hello World'*50)

    document.save('document.docx')
    os.system("start document.docx")

This worked for me

cirrus3d commented 1 month ago

@Kill0geR this worked perfectly in my case. I only had to change: <wp:wrapNone/> to <wp:wrapSquare wrapText = "bothSides"/> in order to set text wrapping to square.

I hope that I will be made easier to do this in the future.