在图片中添加自己的文本信息(PNG及JPEG格式)

背景:
PNG格式和JPEG格式的图片都有一个表示文本信息的段,这些信息可以是任意的字符串,或者版权信息,或者工作室信息,或者其它的。本文的程序是在原始图片中添加这个段的信息。由于比较简单,不多说。

对比图片格式,小弟学习过一段时间,由于工作原因,对JPEG了解深一些,但只限于格式,像JPEG的压缩算法还没学习到。
下面给出关键代码,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
/**************************************************************************

PNG:
段组成:
名称    占用字节      内容
长度       4        数据长度
段名       4       如:'IHDR'
数据       N       数据内容
CRC        4  CRC32值(段名及数据的CRC)

添加文本信息:
长度: strlen(text)
段名:'tEXt'
数据: text(内容自定)
CRC: crc32('tEXt'+text)

放置位置:位于IHDR后面
PNG头8字节,IHDR段25字节(内容13字节,其它12字节),共33字节,
故放置33偏移量处后面。

JPEG:
组成:
名称   占用字节                  内容
段名      2       如:FF D8表示JPEG图片,FF FE表示注释段
长度      2          数据段长度+2(即这里的2字节)
数据      N                  数据内容

添加文件信息:
段名: FF FE
长度: strlen(text_len) + 2
数据: info(text)

放置位置:位于FF E0或FF E1段后面
读0x04、0x05处长度(即FFE0或FFE1的长度),须转换成小端模式

PNG、JPEG图片为大端模式,以字节保存没问题,但数据长度必须转换成大端模式

写新文件方法:
先保存原来的开始部分,再插入文本信息,再保存剩下部分数据
**************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "crc.h"

const char *text = ">AUTHOR: Late Lee from http://www.latelee.org.\n";

#define little2big32(x) \
        ( (x&0xff000000) >> 24 | (x&0xff0000) >> 8 | (x&0xff00) << 8 | (x&0xff) << 24 )

#define little2big16(x) \
        ( (x&0xff00) >> 8 | (x&0xff) << 8 )

#define HEAD_SIZE 33
int add_text_png(const char *file, const char *new_file)
{
        FILE *in;
        FILE *out;
        int len = 0;
        size_t ret = 0;
        char *buffer;
        char *text_buf;
        size_t text_len = 0;
        size_t text_len_1 = 0;
        char text_type[4] = {'t', 'E', 'X', 't'};
        int text_crc = 0;

        in = fopen(file, "rb");
        if (in == NULL)
        {
                printf("Can not open file %s\n", file);
                return -1;
        }

        fseek(in, 0, SEEK_END);
        len = ftell(in);
        fseek(in, 0, SEEK_SET);

        printf("file size: %d\n", len);

        buffer = (char *)malloc(sizeof(char) * len);
        if (buffer == NULL)
        {
                return -1;
        }

        // read all to buffer
        ret = fread(buffer, 1, len, in);
        if (ret != len)
        {
                printf("read file failed.\n");
                return -1;
        }

        out = fopen(new_file, "wb");
        if (out == NULL)
        {
                printf("open new file failed.\n");
                return -1;
        }

        // write 'head'
        ret = fwrite(buffer, 1, HEAD_SIZE, out);
        if (ret != HEAD_SIZE)
        {
                printf("write head failed.\n");
                return -1;
        }

        /////////////////////////
        text_len = strlen(text);
        text_buf = (char *)malloc(sizeof(char) * (text_len + 12));
        text_len_1 = little2big32(text_len);
        memcpy(text_buf, &text_len_1, 4);        // big endian
        memcpy(text_buf + 4, text_type, 4);
        memcpy(text_buf + 4 + 4, text, text_len);
        text_crc = crc32((unsigned char *)(text_buf + 4), (u32)text_len + 4);
        memcpy(text_buf + 4 + 4 + text_len, &text_crc, 4);

        printf("text len: %d\n", text_len);

        // write information
        ret = fwrite(text_buf, 1, text_len + 12, out);
        if (ret != text_len + 12)
        {
                printf("write head failed.\n");
                return -1;
        }

        // write 'left'
        ret = fwrite(buffer + HEAD_SIZE, 1, len - HEAD_SIZE, out);
        if (ret != len - HEAD_SIZE)
        {
                printf("write head failed.\n");
                return -1;
        }

        fclose(in);
        fclose(out);

        free(buffer);
        free(text_buf);

        printf("good job.\n");
        
        return 0;
}

int add_text_jpeg(const char *file, const char *new_file)
{
        FILE *in;
        FILE *out;
        int len = 0;
        size_t ret = 0;
        unsigned char *buffer;        // must be unsigned char
        char *text_buf;
        size_t text_len = 0;
        size_t text_len_1 = 0;
        size_t tmp_len = 0;
        unsigned char text_type[2] = {0xFF, 0xFE};

        in = fopen(file, "rb");
        if (in == NULL)
        {
                printf("Can not open file %s\n", file);
                return -1;
        }

        fseek(in, 0, SEEK_END);
        len = ftell(in);
        fseek(in, 0, SEEK_SET);

        printf("file size: %d\n", len);

        buffer = (unsigned char *)malloc(sizeof(char) * len);
        if (buffer == NULL)
        {
                return -1;
        }

        // read all to buffer
        ret = fread(buffer, 1, len, in);
        if (ret != len)
        {
                printf("read file failed.\n");
                return -1;
        }

        // read tmp_len
        tmp_len = *(buffer + 4) << 8 | *(buffer + 5);
        printf("tmp len: %x\n", tmp_len);

        out = fopen(new_file, "wb");
        if (out == NULL)
        {
                printf("open new file failed.\n");
                return -1;
        }

        // write 'head'
        ret = fwrite(buffer, 1, tmp_len + 4, out);
        if (ret != tmp_len + 4)
        {
                printf("write head failed.\n");
                return -1;
        }

        /////////////////////////
        text_len = strlen(text);
        text_buf = (char *)malloc(sizeof(char) * (text_len + 4));
        text_len_1 = little2big16(text_len + 2);
        memcpy(text_buf, text_type, 2);        // FF FE
        memcpy(text_buf + 2, &text_len_1, 2);        // len
        memcpy(text_buf + 2 + 2, text, text_len);        // text

        printf("text len: %d\n", text_len);

        // write information
        ret = fwrite(text_buf, 1, text_len + 4, out);
        if (ret != text_len + 4)
        {
                printf("write text failed.\n");
                return -1;
        }

        // write 'left'
        ret = fwrite(buffer + tmp_len + 4, 1, len - (tmp_len + 4), out);
        if (ret != len - (tmp_len + 4))
        {
                printf("write file failed.\n");
                return -1;
        }

        fclose(in);
        fclose(out);

        free(buffer);
        free(text_buf);

        printf("good job.\n");
        
        return 0;
}

int my_strnicmp(const char *s1, const char *s2, size_t len)
{
        unsigned char c1, c2;

        const char *tmp1 = s1 + strlen(s1) - 1;
        const char *tmp2 = s2 + strlen(s2) - 1;
        c1 = 0;        c2 = 0;
        if (len) {
                do {
                        c1 = *tmp1; c2 = *tmp2;
                        tmp1--; tmp2--;
                        if (!c1)
                                break;
                        if (!c2)
                                break;
                        if (c1 == c2)
                                continue;
                        c1 = tolower(c1);
                        c2 = tolower(c2);
                        if (c1 != c2)
                                break;
                } while (--len);
        }
        return (int)c1 - (int)c2;
}

// a.exe foo.jpg bar.jpg
// 其中的图片格式需要一致,由用户控制
int main(int argc, char *argv[])
{
        int ret = 0;

        if (argc != 3)
        {
                printf("usage: %s old.jpg|png new.jpg|png.\n", argv[0]);
                return -1;
        }

        if (!my_strnicmp(argv[1], "jpg", 3))
                add_text_jpeg(argv[1], argv[2]);

        else if (!my_strnicmp(argv[1], "png", 3))
                add_text_png(argv[1], argv[2]);
        else
        {
                printf("not support.\n");
                return -1;
        }
        return 0;
}

代码放到github上了,地址:https://github.com/latelee/picture_info