Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Image.frombytes throws on empty image #7492

Closed
dragazo opened this issue Oct 24, 2023 · 10 comments · Fixed by #7493
Closed

Image.frombytes throws on empty image #7492

dragazo opened this issue Oct 24, 2023 · 10 comments · Fixed by #7493

Comments

@dragazo
Copy link

dragazo commented Oct 24, 2023

What did you do?

I have some code I use to render text as images. When trying to render an empty string, it fails on Image.frombytes

What did you expect to happen?

I expected Image.frombytes with size (0, 0) and bytes [] (as a numpy array of uint8) to successfully yield an empty image.

What actually happened?

An exception is thrown:

Traceback (most recent call last):
  File "/home/devin/Desktop/pyblox/netsblox/turtle.py", line 89, in wrapped
    fn(*args, **kwargs)
  File "/home/devin/Desktop/pyblox/netsblox/turtle.py", line 119, in wrapped
    return f(*args, **kwargs)
  File "<stdin>", line 34, in my_onstart
  File "/home/devin/Desktop/pyblox/netsblox/turtle.py", line 1250, in say
    imgs = [_render_text(x, size = 8, color = (0, 0, 0)) for x in lines]
  File "/home/devin/Desktop/pyblox/netsblox/turtle.py", line 1250, in <listcomp>
    imgs = [_render_text(x, size = 8, color = (0, 0, 0)) for x in lines]
  File "/home/devin/Desktop/pyblox/netsblox/turtle.py", line 80, in _render_text
    text_mask = Image.frombytes('L', text_mask.size, _np.array(text_mask).astype(_np.uint8)) # convert ImagingCore to Image
  File "/home/devin/.local/lib/python3.8/site-packages/PIL/Image.py", line 2976, in frombytes
    im.frombytes(data, decoder_name, args)
  File "/home/devin/.local/lib/python3.8/site-packages/PIL/Image.py", line 804, in frombytes
    d.setimage(self.im)
ValueError: tile cannot extend outside image

What are your OS, Python and Pillow versions?

  • OS: Linux Mint 20
  • Python: 3.8.10
  • Pillow: 10.1.0
font = ImageFont.truetype('example-font.otf')
text = ''
color = (0, 0, 0)

text_mask = font.getmask(text, mode = 'L')
text_mask = Image.frombytes('L', text_mask.size, _np.array(text_mask).astype(_np.uint8)) # fails here with exception
@dragazo
Copy link
Author

dragazo commented Oct 24, 2023

Looks like I constructed a minimal example incorrectly. The issue was actually from trying to render empty string, due to a pagination step in between getting the text and rendering it. In that case, the issue would actually be that Image.frombytes throws an error when initializing it with size (0, 0) and bytes []. It seems like this should just successfully return an empty image.

updated the issue report

@dragazo dragazo changed the title ImageFont.getmask and ImageFont.getbbox fail for long strings ImageFont.frombytes throws on empty image Oct 24, 2023
@dragazo dragazo changed the title ImageFont.frombytes throws on empty image Image.frombytes throws on empty image Oct 24, 2023
@radarhere
Copy link
Member

Ok, I've created PR #7493 to resolve this. See what you think.

@hugovk
Copy link
Member

hugovk commented Oct 25, 2023

It seems like this should just successfully return an empty image.

Why should Image.frombytes("L", (0, 0), b"") return an empty image instead of an error?

What use is an empty image?

Would it be better to raise because we're passing in "bad" input?

Should the user be checking they're no passing in bad input?

@radarhere
Copy link
Member

Image.new('L', (0, 0)) was restored in #2262, and Image.fromarray(numpy.empty((0, 0), dtype=numpy.uint8)) was added to tests in #2419

@dragazo
Copy link
Author

dragazo commented Oct 25, 2023

I haven't tried the new version personally, but if that test case passes, that should solve my issue.

@radarhere
Copy link
Member

Using one of our test fonts, yes, it passes.

Could you help address a concern that has been raised? Why is it useful to you to have an empty image?

@dragazo
Copy link
Author

dragazo commented Oct 26, 2023

Well like the example code showed, I'm not explicitly trying to produce an empty image, that's just what ends up happening during an intermediate step. Basically my code can sometimes try to render text consisting of only whitespace, for which font.getmask returns an empty image core. It's the conversion of the (empty) image core object into an (empty) image that was failing.

I suppose I could add pre-checks to ensure that doesn't happen, but from a type theory perspective does that mean I just have to always produce str -> Optional[Image] instead of the desired str -> Image? The only way to avoid that would be replacing the None fail case with a valid but empty(ish) image like a 1x1 transparent image. But that sounds less nice than just allowing empty images, esp. since font.getmask already happily produces them.

@hugovk
Copy link
Member

hugovk commented Oct 26, 2023

Thanks. Let's say str -> Image succeeds. You now have an empty image, what do you do with it?

The thing I'm trying to balance is: do we give the user an exception they need to handle, or a (useless?) empty image they need to handle in another place/way.

@dragazo
Copy link
Author

dragazo commented Oct 26, 2023

Among other things, I can paste it onto another image. For my use case this is part of a renderer - I have a string and a location, I render the text and paste it onto the base image (with some decoration steps in between). Empty image allows me to not need to check exceptions just for rendering text from a font object.

As for the useless part, I think an empty image is no less useless than an empty string.

If you want to do the exception route, you should make it so that font.getmask fails on empty or whitespace strings (currently returns an empty image core object), rather than the conversion to the Image wrapper type. That's what's confusing, I think.

@hugovk
Copy link
Member

hugovk commented Oct 26, 2023

Thanks, that sounds reasonable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants