elapouya / python-docx-template

Use a docx as a jinja2 template
GNU Lesser General Public License v2.1
1.91k stars 378 forks source link

inline image support for bytes from file or network #468

Open liudonghua123 opened 1 year ago

liudonghua123 commented 1 year ago

Is your feature request related to a problem? Please describe.

I want to render a docx which contains some images from network, I find it is difficult to do it.

I have to download and save as a temp file, use the current InlineImage and then I have to remember to remove it.

Why not directly support image contents as input?

Describe the solution you'd like

Support the image contents when using InlineImage.

Describe alternatives you've considered

n.a.

Additional context

n.a.

liudonghua123 commented 1 year ago

I noticed that python-docx is used, and it seems it only support file path instead of file contents.

https://github.com/elapouya/python-docx-template/blob/5d9bba118ac3c0c0f3af3242884945e526a5d4c9/docxtpl/inline_image.py#L23-L30

https://github.com/python-openxml/python-docx/blob/36cac78de080d412e9e50d56c2784e33655cad59/docx/parts/story.py#L50

Maybe we could save the image contents in a file, generate docx and do some cleanup internal.

liudonghua123 commented 1 year ago

I found the upstream project python-docx support stream of image.

See:

cw0516 commented 1 year ago

I hope this feature to be added too👏

radove commented 4 months ago

Hello, Its possible to pass an io.BytesIO(bytes) to the InlineImage() function. Perhaps there needs to be a new example leveraging this method: @cw0516 @liudonghua123

fig = px.scatter(x=[0, 1, 2, 3, 4], y=[0, 1, 4, 9, 16]) #plotly Image example
img_bytes = fig.to_image(format="png",width=600, height=350) # plotly image generate bytes
main_visualization = io.BytesIO(img_bytes) # convert bytes to File obj
context = { 'main_visualization': docxtpl.InlineImage(doc, main_visualization) } # python-docx-template will believe this is a File and run seek on it.
liudonghua123 commented 4 months ago

Hello, Its possible to pass an io.BytesIO(bytes) to the InlineImage() function. Perhaps there needs to be a new example leveraging this method: @cw0516 @liudonghua123

fig = px.scatter(x=[0, 1, 2, 3, 4], y=[0, 1, 4, 9, 16]) #plotly Image example
img_bytes = fig.to_image(format="png",width=600, height=350) # plotly image generate bytes
main_visualization = io.BytesIO(img_bytes) # convert bytes to File obj
context = { 'main_visualization': docxtpl.InlineImage(doc, main_visualization) } # python-docx-template will believe this is a File and run seek on it.

Yeah, I found new_pic_inline in upstream project docx support IO[bytes], see https://github.com/python-openxml/python-docx/blob/57d3b9ee9cb778e76258653c6dc464d5598ca80b/src/docx/parts/story.py#L60-L65.

liudonghua123 commented 4 months ago

And I tested, it worked as expected!

import io
from docxtpl import DocxTemplate, InlineImage
from docx.shared import Mm

doc = DocxTemplate("test.docx")
context = { 'company_name' : "World company", 'inline_image': InlineImage(doc, io.BytesIO(open('test.png', 'rb').read()), width=Mm(10), height=Mm(10))}
doc.render(context)
doc.save("generated_doc.docx")

image

liudonghua123 commented 4 months ago

I can also use file like object as the second argument of InlineImage.

image