python-openxml / python-docx

Create and modify Word documents with Python
MIT License
4.66k stars 1.14k forks source link

document.add_picture ZeroDivisionError: division by zero #515

Open luowqn opened 6 years ago

luowqn commented 6 years ago

Hello,I have a question,i want to add a picture to doc Mycode:

from docx import Document
from docx.shared import Inches

document = Document()
document.add_picture('./test.jpg',width=Inches(1.25))
document.save('demo.docx')
Error:
`C:\Users\luowq\AppData\Local\Programs\Python\Python36\python3.exe F:/学习代码/study/python/imageCompareSvr/main.py
Traceback (most recent call last):
  File "F:/学习代码/study/python/imageCompareSvr/main.py", line 28, in <module>
    document.add_picture('./test.jpg',width=Inches(1.25))
  File "C:\Users\luowq\AppData\Local\Programs\Python\Python36\lib\site-packages\docx\document.py", line 79, in add_picture
    return run.add_picture(image_path_or_stream, width, height)
  File "C:\Users\luowq\AppData\Local\Programs\Python\Python36\lib\site-packages\docx\text\run.py", line 62, in add_picture
    inline = self.part.new_pic_inline(image_path_or_stream, width, height)
  File "C:\Users\luowq\AppData\Local\Programs\Python\Python36\lib\site-packages\docx\parts\document.py", line 93, in new_pic_inline
    cx, cy = image.scaled_dimensions(width, height)
  File "C:\Users\luowq\AppData\Local\Programs\Python\Python36\lib\site-packages\docx\image\image.py", line 159, in scaled_dimensions
    scaling_factor = float(width) / float(self.width)
  File "C:\Users\luowq\AppData\Local\Programs\Python\Python36\lib\site-packages\docx\image\image.py", line 127, in width
    return Inches(self.px_width / self.horz_dpi)
ZeroDivisionError: division by zero
scanny commented 6 years ago

I've shortened your code to get rid of lines that I don't believe are related. Please ensure the error still occurs with this minimal code to reproduce.

I believe you're going to find that your JPEG file is missing some important information, in particular, the horizontal DPI setting. Try using another JPEG file and seeing if the error reoccurs.

floriaanpost commented 4 years ago

In case anyone gets this error... It is "solved" by monkey patching the height and width of the Image class:

from docx.image.image import Image
from docx.shared import Inches

@property
def image_width(self):
    if (self.horz_dpi == 0):
        return Inches(self.px_width / 72)
    return Inches(self.px_width / self.horz_dpi)

@property
def image_height(self):
    if (self.vert_dpi == 0):
        return Inches(self.px_height / 72)
    return Inches(self.px_height / self.vert_dpi)

Image.width = image_width
Image.height = image_height

If don't have a clue to why it is zero in the first place, as the images (in my case) do not appear to have an header, so it should default to 72 dpi.

Swannbm commented 4 years ago

I think it's a True issue because in readthedoc it says that it should default to 72 DPI.

taiebnoe commented 3 years ago

Thanks! I had the same issue. Jpegs with bad header. Hope this gets fixed in the main branch.

GISallover commented 3 years ago

So I couldn't get this monkey patch to work, so I set out to try and fix the actual bug. A bit beyond my pay grade, but I did track it down to this function in tiff.py:

` def _dpi(self, resolution_tag): """ Return the dpi value calculated for resolution_tag, which can be either TIFF_TAG.X_RESOLUTION or TIFF_TAG.Y_RESOLUTION. The calculation is based on the values of both that tag and the TIFF_TAG.RESOLUTION_UNIT tag in this parser's |_IfdEntries| instance. """

    ifd_entries = self._ifd_entries
    logging.debug(resolution_tag)
    if resolution_tag not in ifd_entries:
        logging.debug("no tag")
        return 72`

That error trap simply isn't going off.. because the photos do have resolution tags. There's something else that's preventing these values from getting passed correctly, but that's why it's not defaulting.

Anyways, I did manage to get it to work simply put putting line 343 of tiff.py in a try/except block and setting a default value of 72 there. I don't even know if this is referring to resolution... but I figured it had something to do with sizing so I started there, and it worked.

Hope that gives a starting point to anyone

In case anyone gets this error... It is "solved" by monkey patching the height and width of the Image class:

from docx.image.image import Image
from docx.shared import Inches

@property
def image_width(self):
    if (self.horz_dpi == 0):
        return Inches(self.px_width / 72)
    return Inches(self.px_width / self.horz_dpi)

@property
def image_height(self):
    if (self.vert_dpi == 0):
        return Inches(self.px_height / 72)
    return Inches(self.px_height / self.vert_dpi)

Image.width = image_width
Image.height = image_height

If don't have a clue to why it is zero in the first place, as the images (in my case) do not appear to have an header, so it should default to 72 dpi.

netaddict commented 2 months ago

Had the same issue in 2024, the bug still exists.

DayDotMe commented 2 days ago

Just in case, I solved this using Pillow. It's probably a bit overkill but hey, it works.

# before:

picture_stream = BytesIO(picture_content) 
run = paragraph.add_run()
run.add_picture(picture_stream) # raises the ZeroDivisionError

# after:

picture_stream = BytesIO(picture_content)
image = Image.open(picture_stream)
clean_stream = BytesIO()
image.save(clean_stream, format=image.format)
clean_stream.seek(0)
run = paragraph.add_run()
run.add_picture(clean_stream)