pythonpngmetadataexif

How do I save custom information to a PNG Image file in Python?


I would like to open a PNG image in Python, add some custom data to it, save the image file and at a later time open it again to retrieve the data. I am working in Python 3.7. I would prefer to use the PNG image format but could move to another format if there was little other option.

I have read lots of outdated answers and articles from before the PNG standard was updated to allow custom data to be stored in it. I would like an answer for what is available today (October 2019) or would, of course, accept updated answers in the future if something helpful is enabled. It really is hard to search for this sort of specific issue as "save", "png", "python", "info" are all quite generic terms.


I can retrieve some data using Pillow (6.2.0 at present). What I cannot work out is how to store more than the exif data to a png.

from PIL import image
targetImage = Image.open("pathToImage.png")
targetImage.info["MyNewString"] = "A string"
targetImage.info["MyNewInt"] = 1234
targetImage.save("NewPath.png")

The above loses the information when I save. I have seen some documentation for using targetImage.save("NewPath.png", exif=exif_bytes) but that only works with exif formatted data. I looked at the piexif and piexif2 packages but they either only support JPEG or will not allow custom data.

I do not mind if the information is stored as iTXt, tEXt or zTXt chunks in the PNG. See https://dev.exiv2.org/projects/exiv2/wiki/The_Metadata_in_PNG_files for an explanation of the formats.


I am quite new to Python so I apologise if I have missed something obvious in the documentation that a more practised coder would recognise. I really would like to not re-invent the wheel if a solution already exists.


Solution

  • You can store metadata in Pillow using PngImagePlugin.PngInfo like this:

    from PIL import Image
    from PIL.PngImagePlugin import PngInfo
    
    targetImage = Image.open("pathToImage.png")
    
    metadata = PngInfo()
    metadata.add_text("MyNewString", "A string")
    metadata.add_text("MyNewInt", str(1234))
    
    targetImage.save("NewPath.png", pnginfo=metadata)
    targetImage = Image.open("NewPath.png")
    
    print(targetImage.text)
    
    >>> {'MyNewString': 'A string', 'MyNewInt': '1234'}
    

    Here, targetImage is a PngImageFile, that's where the text property is defined. The Image base class also has the info attribute which seems to contain the same data (thanks to @Stormblessed for pointing this out).

    In this example I use tEXt, but you can also save it as iTXt using add_itxt.

    Additional information added by Mark Setchell below.

    The additional information can be seen with pngcheck like this:

    pngcheck -t result.png
    
    File: result.png (563 bytes)
    MyNewString:
    A string
    MyNewInt:
    1234
    OK: result.png (640x480, 1-bit grayscale, non-interlaced, 98.5%).
    

    And with ImageMagick like this:

    identify -verbose result.png
    
    ...
    ...
    Properties:
    date:create: 2025-11-04T21:49:25+00:00
    date:modify: 2025-11-04T21:49:25+00:00
    date:timestamp: 2025-11-04T21:51:05+00:00
    MyNewInt: 1234
    MyNewString: A string
    png:IHDR.bit-depth-orig: 1
    png:IHDR.bit_depth: 1
    png:IHDR.color-type-orig: 0
    png:IHDR.color_type: 0 (Grayscale)
    png:IHDR.interlace_method: 0 (Not interlaced)
    png:IHDR.width,height: 640, 480
    png:text: 2 tEXt/zTXt/iTXt chunks were found
    ...
    ...
    

    And with exiftool like this:

    exiftool result.png
    
    ExifTool Version Number         : 13.30
    File Name                       : result.png
    Directory                       : .
    File Size                       : 563 bytes
    ...
    ...
    Interlace                       : Noninterlaced
    My New String                   : A string
    My New Int                      : 1234
    Image Size                      : 640x480
    Megapixels                      : 0.307
    

    And more concisely:

    exiftool -MyNewString result.png
    My New String                   : A string
    
    exiftool -MyNewInt result.png
    My New Int                      : 1234