2

I'm trying to store an image as text, so that I can do something like this example of a transparent icon for a Tk gui:

import tempfile

# byte literal code for a transparent icon, I think
ICON = (b'\x00\x00\x01\x00\x01\x00\x10\x10\x00\x00\x01\x00\x08\x00h\x05\x00\x00'
        b'\x16\x00\x00\x00(\x00\x00\x00\x10\x00\x00\x00 \x00\x00\x00\x01\x00'
        b'\x08\x00\x00\x00\x00\x00@\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
        b'\x00\x01\x00\x00\x00\x01') + b'\x00'*1282 + b'\xff'*64

# makes a temp file for the transparent icon and saves it
_, ICON_PATH = tempfile.mkstemp()
with open(ICON_PATH, 'wb') as icon_file:
    icon_file.write(ICON)

I've tried base 64 encoding, decoding with utf8, converting to bytes and bytearray, and some answers from another post: (Python Script to convert Image into Byte array)

import tempfile, base64, io

# byte literal code for a transparent icon, I think
ICON = (b'\x00\x00\x01\x00\x01\x00\x10\x10\x00\x00\x01\x00\x08\x00h\x05\x00\x00'
        b'\x16\x00\x00\x00(\x00\x00\x00\x10\x00\x00\x00 \x00\x00\x00\x01\x00'
        b'\x08\x00\x00\x00\x00\x00@\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
        b'\x00\x01\x00\x00\x00\x01') + b'\x00'*1282 + b'\xff'*64

# makes a temp file for the transparent icon and saves it
_, ICON_PATH = tempfile.mkstemp()
with open(ICON_PATH, 'wb') as icon_file:
    icon_file.write(ICON)

a = open(ICON_PATH, 'rb').read()

b = base64.b64encode(a)

print b # output does not match what ICON equals above

# doesn't work; gives error
# UnicodeDecodeError: 'utf8' codec can't decode byte 0xff in position 1342: invalid start byte
# b = bytes(a).decode('utf-8') 

c = bytearray(a)

print c # prints garbled junk


# gives error AttributeError: __exit__ on Image.open(ICON_PATH) line
with io.BytesIO() as output:
    from PIL import Image
    with Image.open(ICON_PATH) as img:
        img.convert('RGB').save(output, 'BMP')                
    data = output.getvalue()[14:]

print data

It also doesn't work for b.decode('utf-8') or b.encode('utf-8')

4
  • stackoverflow.com/questions/22351254/… are solutions mentioned here not working? Commented Apr 8, 2015 at 6:59
  • No, they weren't working. Updated question to reflect it Commented Apr 8, 2015 at 7:10
  • I think it may be possible with something from struct docs.python.org/2/library/struct.html Commented Apr 8, 2015 at 7:21
  • I see that you are using the print statement. This would mean that you are using Python 2. Python 3 is better in many regards. You might want to upgrade. Commented Apr 8, 2015 at 9:30

3 Answers 3

5

I think you're looking for

print(repr(a))

For a as defined in your code, this will print b'\x00\x00\x01\x00\x01\x00\x10\x10\x00\x00\x01\x00\x08\x00h\x05\x00\x00\x16\x00\x00\x00(\x00\x00\x00\x10\x00\x00\x00 \x00\x00\x00\x01\x00\x08 and so on, similar to your original ICON definition, but quite large because all the \x00s and \xffs at the end are written out.


In your code, you have included some ad hoc compression (namely + b'\x00'*1282 + b'\xff'*64). To get compression automatically, so the ICON definition in your source file doesn't have to be so large, leverage an existing compression library, like zlib:

import zlib
print(repr(zlib.compress(a)))

On my machine, this prints 'x\x9cc``\x04B\x01\x01\x06 \xc9\xc1\x90\xc1\xca\xc0 \xc6\xc0\xc0\xa0\x01\xc4@!\x06\x05\x06\x888\x088\xb02 \x00#\x14\x8f\x82Q0\nF\xc1\x08\x05\xff)\x04\x00U\xf1A\x17', which is quite small. To decompress, use zlib.decompress:

import zlib

ICON = zlib.decompress(b'x\x9cc``\x04B\x01\x01\x06 \xc9\xc1\x90\xc1\xca\xc0 '
    b'\xc6\xc0\xc0\xa0\x01\xc4@!\x06\x05\x06\x888\x088\xb02 \x00#\x14\x8f\x82'
    b'Q0\nF\xc1\x08\x05\xff)\x04\x00U\xf1A\x17')

ICON now has the same value as in your original example.


If you now want a representation that's still more compact in your source file, it is time to apply base 64 encoding, which gets rid of the verbose binary encoding in python (the \x..-format).

To encode:

import base64, zlib

print(repr(base64.b64encode(zlib.compress(a))))

This gives me 'eJxjYGAEQgEBBiDJwZDBysAgxsDAoAHEQCEGBQaIOAg4sDIgACMUj4JRMApGwQgF/ykEAFXxQRc='

To decode:

import base64, zlib

ICON = zlib.decompress(base64.b64decode('eJxjYGAEQgEBBiDJwZDBy'
    'sAgxsDAoAHEQCEGBQaIOAg4sDIgACMUj4JRMApGwQgF/ykEAFXxQRc='))

And again, ICON has the same value as originally specified.


The final strategy as presented is good for ico files. I see that you also mention png files. These already have compression applied, so you should probably prefer to use only base 64 encoding:

import base64

print(base64.b64encode(png_icon))

and

PNG_ICON = base64.b64decode( ** insert literal here ** )

As it turns out, these encodings are also available through the str.encode and str.decode APIs. This lets you off without writing the imports. For completeness, here they are:

Encoding:

print(repr(a.encode('zlib').encode('base64')))

Decoding:

ICON = ('eJxjYGAEQgEBBiDJwZDBysAgxsDAoAHEQCEGBQaIOAg4sDIgACMUj4J'
    'RMApGwQgF/ykEAFXxQRc=').decode('base64').decode('zlib')
Sign up to request clarification or add additional context in comments.

Comments

2

I think you're just not printing out the data properly — there doesn't seem to be any need to mess around with base64 doing this.

Here's proof:

from itertools import izip
import tempfile

# byte literal code for a transparent icon, I think
ICON = (b'\x00\x00\x01\x00\x01\x00\x10\x10\x00\x00\x01\x00\x08\x00h\x05\x00\x00'
        b'\x16\x00\x00\x00(\x00\x00\x00\x10\x00\x00\x00 \x00\x00\x00\x01\x00'
        b'\x08\x00\x00\x00\x00\x00@\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
        b'\x00\x01\x00\x00\x00\x01') + b'\x00'*1282 + b'\xff'*64

# make a temp file from ICON data for testing
_, ICON_PATH = tempfile.mkstemp()
with open(ICON_PATH, 'wb') as icon_file:
    icon_file.write(ICON)

# Convert raw data in the file into a valid Python string literal.

# helper function
def grouper(n, seq):
    "s -> (s0,s1,...sn-1), (sn,sn+1,...s2n-1), (s2n,s2n+1,...s3n-1), ..."
    for i in xrange(0, len(seq), n):
        yield seq[i:i+n]

# read data file in binary mode
a = open(ICON_PATH, 'rb').read()

# create Python code to define string literal
code = '\n'.join(['ICON2 = ('] +
                 ['    '+repr(group) for group in grouper(16, a)] +
                 [')'])

print 'len(ICON): {}'.format(len(ICON))
print 'len(a): {}'.format(len(a))
print code
exec(code)
print
print 'len(ICON2): {}'.format(len(ICON2))
print 'ICON2 == ICON: {}'.format(ICON2 == ICON)

Output:

len(ICON): 1406
len(a): 1406
ICON2 = (
    '\x00\x00\x01\x00\x01\x00\x10\x10\x00\x00\x01\x00\x08\x00h\x05'
    '\x00\x00\x16\x00\x00\x00(\x00\x00\x00\x10\x00\x00\x00 \x00'
    '\x00\x00\x01\x00\x08\x00\x00\x00\x00\x00@\x05\x00\x00\x00\x00'
    '\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00'
    '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
       ...
    '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
    '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
    '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff'
    '\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'
    '\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'
    '\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'
    '\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'
)

len(ICON2): 1406
ICON2 == ICON: True

Comments

0

Well I did figure out you can do it this way:

import tempfile, base64, io

# byte literal code for a transparent icon, I think
ICON = (b'\x00\x00\x01\x00\x01\x00\x10\x10\x00\x00\x01\x00\x08\x00h\x05\x00\x00'
        b'\x16\x00\x00\x00(\x00\x00\x00\x10\x00\x00\x00 \x00\x00\x00\x01\x00'
        b'\x08\x00\x00\x00\x00\x00@\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
        b'\x00\x01\x00\x00\x00\x01') + b'\x00'*1282 + b'\xff'*64

# makes a temp file for the transparent icon and saves it
_, ICON_PATH = tempfile.mkstemp()
with open(ICON_PATH, 'wb') as icon_file:
    icon_file.write(ICON)

with open(ICON_PATH, 'rb') as imgFile:
    a = imgFile.read()

b = base64.b64encode(a)

print b # output does not match what ICON equals above

with open('test.png','wb') as writeFile:
    writeFile.write(b.decode('base64'))

but I still want to know how to get the same format as 'ICON = (...' at the top

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.