Reputation: 11963
I'm looking for a simple command-line tool (on Linux) to insert a text chunk (e.g. copyright) into a png file, resulting in a new png file:
> png-insert-text-chunk "here's my text chunk" < in.png > out.png
Note: by "insert a text chunk", I do not mean "draw some text on the image". I mean: insert the text into the png file as a chunk, in the technical sense. This can be used, for example, to insert a copyright message that isn't displayed on the actual image.
Upvotes: 26
Views: 19121
Reputation: 396
gioele already showed how to do it with ImageMagick, but I had more specific requirements that were not fulfilled by that answer. Here is what I did to definitely get an uncompressed tEXt
chunk with the keyword of my choice instead of a compressed zTXt
chunk:
convert input.png -set 'my_chosen_keyword' "$(cat content.txt)" -compress none output.png
Using "$(cat content.txt)"
makes it easier to get text with line breaks and other ugly stuff into the file.
NB: Despite the -compress none
, the PNG image data will still be as compressed as usual.
Upvotes: 1
Reputation: 558
I extracted the core algorithm of reading and writing png-text-chunk from pypng. This is the simplest example. And it's 10x faster than original pypng implementation
import struct
import zlib
from typing import Union
def write_text_chunk(img_bytes: bytes, data: Union[str, bytes]) -> bytes:
assert img_bytes[:8] == b'\x89PNG\r\n\x1a\n'
if isinstance(data, str):
data = data.encode()
start_idx = end_idx = img_bytes.find(b'tEXt') - 4
if b'tEXt' in img_bytes:
existed_data_len = struct.unpack("!I", img_bytes[start_idx: start_idx + 4])[0]
end_idx += 12 + existed_data_len
out_arr = img_bytes[:start_idx] + \
struct.pack("!I", len(data)) + \
b'tEXt' + data + \
struct.pack("!I", zlib.crc32(data, zlib.crc32(b'tEXt')) & 2 ** 32 - 1) + \
img_bytes[end_idx:]
return out_arr
def read_text_chunk(img_bytes: bytes) -> bytes:
assert img_bytes[:8] == b'\x89PNG\r\n\x1a\n'
idx = img_bytes.find(b'tEXt')
data_len = struct.unpack("!I", img_bytes[idx - 4: idx])[0]
return img_bytes[idx + 4: idx + 4 + data_len]
if __name__ == '__main__':
import time
src = '打倒帝国主义.png'
img_bytes = open(src, 'rb').read()
t1 = time.time()
embed_data = '中华人民共和国万岁!世界人民大团结万岁!'.encode() * 1
out = write_text_chunk(img_bytes, embed_data)
t2 = time.time()
open('test.png', 'wb').write(out)
t3 = time.time()
read_embed_data = read_text_chunk(out)
t4 = time.time()
print(embed_data == read_embed_data, read_embed_data.decode())
print('WRITE', t2 - t1)
print('READ', t4 - t3)
Upvotes: 0
Reputation: 54
You can use the pypng library in Python:
#!/bin/python3
import png
reader = png.Reader(filename='input.png')
chunks = list(reader.chunks())
chunk_field = (b"tEXt", b"Profile\x00Profile Content Here")
chunks.insert(1, chunk_field)
file = open('output.png', 'wb')
png.write_chunks(file, chunks)
Upvotes: 0
Reputation: 456
This can be implemented with python pypng module. The python3 example code is below:
import png
TEXT_CHUNK_FLAG = b'tEXt'
def generate_chunk_tuple(type_flag, content):
return tuple([type_flag, content])
def generate_text_chunk_tuple(str_info):
type_flag = TEXT_CHUNK_FLAG
return generate_chunk_tuple(type_flag, bytes(str_info, 'utf-8'))
def insert_text_chunk(target, text, index=1):
if index < 0:
raise Exception('The index value {} less than 0!'.format(index))
reader = png.Reader(filename=target)
chunks = reader.chunks()
chunk_list = list(chunks)
print(chunk_list[0])
print(chunk_list[1])
print(chunk_list[2])
chunk_item = generate_text_chunk_tuple(text)
chunk_list.insert(index, chunk_item)
with open(target, 'wb') as dst_file:
png.write_chunks(dst_file, chunk_list)
def _insert_text_chunk_to_png_test():
src = r'E:\temp\png\register_05.png'
insert_text_chunk(src, 'just for test!')
if __name__ == '__main__':
_insert_text_chunk_to_png_test()
Upvotes: 8
Reputation: 34294
Note: This answer was a response to the original revision of the question where it was not clear if the text had to be written on the image or if the text had to be embedded within the image binary file as metadata. This answer assumed the former. However the question was edited to clarify that it meant the latter. This answer is left intact, in case someone is looking for a solution to the former.
convert -draw "text 20,20 'hello, world'" input.png output.png
The 20,20
in the above example is the co-ordinate where I want to place the text.
You need to use the imagemagick package to get this command.
On Ubuntu or Debian, it can be installed with the command: apt-get install imagemagick
.
Here is a slightly more elaborate usage of the command:
convert -font Helvetica -pointsize 20 -draw "text 20,20 'hello, world'" input.png output.png
Upvotes: 5
Reputation: 10215
Use ImageMagick's convert
and the -set
option:
convert IN.png \
-set 'Copyright' 'CC-BY-SA 4.0' \
-set 'Title' 'A wonderful day' \
-set comment 'Photo taken while running' \
OUT.png
The -set
option is used to set metadata elements. In the case of PNG these often go into tEXt
chunks.
Upvotes: 20
Reputation: 1181
Adding to the comment made by @susam-pal, to change color use the option
-fill color
This option accepts a color name, a hex color, or a numerical RGB, RGBA, HSL, HSLA, CMYK, or CMYKA specification. See Color Names for a description of how to properly specify the color argument.
Enclose the color specification in quotation marks to prevent the "#" or the parentheses from being interpreted by your shell. For example,
-fill blue
-fill "#ddddff"
-fill "rgb(255,255,255)"
Obtained from link
Upvotes: 0
Reputation: 9
I believe that pngcrush has this ability: http://pwet.fr/man/linux/commandes/pngcrush
Upvotes: 0
Reputation: 11963
I have searched around for utilities to do this, and not yet found anything that really matches what I want to do. So I decided to build my own, which it turns out is not too hard. The utility png-text-dump
displays all text chunks in a PNG image. It depends only on libpng. The utility png-text-append
inserts text chunks into a PNG image. It depends only on the standard C library - I had initially tried to implement this using libpng, but actually found it easier to work from scratch using only the PNG specification.
Upvotes: 15