PNG にテキストを埋め込む

# 環境

- Ubuntu 22.04 LTS

# 埋め込む

draw.io を使うと、png に埋め込みができ、それを再編集できます。どうやっているのかな?

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/831d30b3-0ac5-b459-5f32-199473d0b06e.png)
エクスポート画面

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/73db5a20-6547-9dbb-530f-b297f2a4d79f.png)

エクスポートした png を qiita に貼って

![pngテスト.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/9098101b-0390-8c5e-bc0a-f669bf10dd50.png)

上の png をダウンロード、drawio で開く

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/8053c5a4-e5b1-4b52-3e45-cd1a98590bbc.png)

数式は、再編集可能です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/91a28dd5-8afc-0081-ce93-d39031e812ac.png)


## chunk 

「Canvas から生成した PNG 画像に独自の情報を埋め込む | GREE Engineering」
https://labs.gree.jp/blog/2013/12/8594/

を読むと、PNG はチャンクとしてデータを管理していて、独自チャンクなどとして自由に情報が格納できることがわかります。

「PNG: Chunk by Chunk &mdash; PyPNG 0.0.20 documentation」
https://pypng.readthedocs.io/en/latest/chunk.html
では、定義された18種類のチャンクがリストされています。

先の drawio で作成した png ファイルについて、
「PNG file chunk inspector」
https://www.nayuki.io/page/png-file-chunk-inspector
これでチェックすると、以下のように表示されました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/219c530f-6832-627c-cdd3-ad7e507235dc.png)

オレオレチャンクタイプではなく、tExt チャンクを使っているようです。

「PNG Specification: Chunk Specifications」
https://www.w3.org/TR/PNG-Chunks.html
では、tExt チャンクについて以下のように説明されています。


> Textual information that the encoder wishes to record with the image can be stored in tEXt chunks. Each tEXt chunk contains a keyword and a text string, in the format:
>    Keyword:        1-79 bytes (character string)
>    Null separator: 1 byte
>    Text:           n bytes (character string)
> The keyword and text string are separated by a zero byte (null character). Neither the keyword nor the text string can contain a null character. Note that the text string is not null-terminated (the length of the chunk is sufficient information to locate the ending). The keyword must be at least one character and less than 80 characters long. The text string can be of any length from zero bytes up to the maximum permissible chunk size less the length of the keyword and separator.
Any number of tEXt chunks can appear, and more than one with the same keyword is permissible.
> 
> The keyword indicates the type of information represented by the text string. The following keywords are predefined and should be used where appropriate:



PNG Specification: Chunk Specifications
# python プログラム でテキストを埋め込む

## 元にしたもの
「linux - Insert a text chunk into a png image - Stack Overflow」
https://stackoverflow.com/questions/9036152/insert-a-text-chunk-into-a-png-image
こちらに投稿されているサンプルコード
```
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!')
```

## テスト

上記に以下を足します。

```
src = r'icon.png'
insert_text_chunk(src, 'just for test!')

```

pypng モジュールをインストールします。

```
$ sudo pip install pypng
```

カレントディレクトリに icon.png を置いて、実行します。

```
$ python chunkpng.py
```



## チェック

「PNG file chunk inspector」
https://www.nayuki.io/page/png-file-chunk-inspector
こちらでうまくいったかどうか確認してみます。

処理前
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/01992d81-f790-a251-967f-988e2274e89a.png)
処理後
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/bd738e21-b992-552c-3945-7490707d8222.png)


埋め込まれているみたいですね!
「Missing null separator」は、キーワードとテキストが分離されていないためのエラーです。


# python プログラムでテキストを取り出す

先のプログラムは2番めのチャンクに埋め込んでいました。ここでは固定した場所からテキストを取り出してみます。

```
import png
def decode_text_chunk(target, 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[1])

src = r'icon.png'
decode_text_chunk(src)
```

コレを実行すると、以下のように取り出せました。

```
(b'tEXt', b'just for test!')
```