Skip to content

Commit 264d24b

Browse files
committed
feat(3-some-png-images-that-are-not-generated-by-comfyui-seem-not-to-work): add exif section for keyword
add exif section for keyword
1 parent 69ddf30 commit 264d24b

File tree

5 files changed

+61
-7
lines changed

5 files changed

+61
-7
lines changed

app/utils/exif.ts

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ export function getPngMetadata(buffer: ArrayBuffer): Record<string, string> {
3636
offset + 8 + length
3737
);
3838
const contentJson = new TextDecoder("utf-8").decode(contentArraySegment);
39+
40+
if (txt_chunks[keyword])
41+
console.warn(`Duplicated keyword ${keyword} has been overwritten`);
3942
txt_chunks[keyword] = contentJson;
4043
}
4144

@@ -71,7 +74,7 @@ ref: png chunk struct:
7174
export function setPngMetadata(
7275
buffer: ArrayBuffer,
7376
new_txt_chunks: Record<string, string>
74-
): Uint8Array<ArrayBufferLike> {
77+
): Uint8Array {
7578
// Get the PNG data as a Uint8Array
7679
const pngData = new Uint8Array(buffer);
7780
const newPngChunks: Uint8Array[] = [];
@@ -96,9 +99,7 @@ export function setPngMetadata(
9699
if (type === "tEXt" || type == "comf" || type === "iTXt") {
97100
// Get the keyword
98101
let keyword_end = offset + 8;
99-
while (pngData[keyword_end] !== 0) {
100-
keyword_end++;
101-
}
102+
while (pngData[keyword_end] !== 0) keyword_end++;
102103
const keyword = String.fromCharCode(
103104
...pngData.slice(offset + 8, keyword_end)
104105
);
@@ -163,6 +164,7 @@ export function setPngMetadata(
163164
newPngChunk.set(encoded, 8);
164165
dataView.setUint32(8 + chunkLength, chunkCRC32);
165166
newPngChunks.push(newPngChunk);
167+
delete new_txt_chunks[keyword]; //mark used
166168
}
167169
} else {
168170
// if this keyword is not in new_txt_chunks,
@@ -177,7 +179,28 @@ export function setPngMetadata(
177179
offset += 12 + length;
178180
}
179181

180-
// Concatenate the new PNG chunks
182+
// If no EXIF section was found, add new metadata chunks
183+
Object.entries(new_txt_chunks).map(([keyword, content]) => {
184+
// console.log(`Adding exif section for ${keyword}`);
185+
const encoded = new TextEncoder().encode(keyword + "\x00" + content);
186+
const chunkLength = encoded.length;
187+
const chunkType = new TextEncoder().encode("tEXt");
188+
189+
// Calculate crc32
190+
const crcTarget = new Uint8Array(chunkType.length + encoded.length);
191+
crcTarget.set(chunkType, 0);
192+
crcTarget.set(encoded, chunkType.length);
193+
const chunkCRC32 = crc32FromArrayBuffer(crcTarget);
194+
195+
const newPngChunk = new Uint8Array(8 + chunkLength + 4);
196+
const dataView = new DataView(newPngChunk.buffer);
197+
dataView.setUint32(0, chunkLength);
198+
newPngChunk.set(chunkType, 4);
199+
newPngChunk.set(encoded, 8);
200+
dataView.setUint32(8 + chunkLength, chunkCRC32);
201+
newPngChunks.push(newPngChunk);
202+
});
203+
181204
const newPngData = concatUint8Arrays(newPngChunks);
182205
return newPngData;
183206
}

tests/Blank_2025-03-06-input.png

684 KB
Loading
File renamed without changes.

tests/exif-png.test.ts

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { getPngMetadata, setPngMetadata } from "@/app/utils/exif";
22
import { glob } from "glob";
33

44
it("extract png workflow", async () => {
5-
const pngs = await glob("./tests/*.png");
5+
const pngs = await glob("./tests/ComfyUI_*.png");
66
expect(pngs.length).toBeGreaterThanOrEqual(1);
77

88
for await (const filename of pngs) {
@@ -18,7 +18,7 @@ it("extract png workflow", async () => {
1818
});
1919

2020
it("set png workflow", async () => {
21-
const pngs = await glob("./tests/*.png");
21+
const pngs = await glob("./tests/ComfyUI_*.png");
2222
expect(pngs.length).toBeGreaterThanOrEqual(1);
2323

2424
for await (const filename of pngs) {
@@ -37,3 +37,34 @@ it("set png workflow", async () => {
3737
expect(workflow_expect).toEqual(workflow_actual);
3838
}
3939
});
40+
41+
it("extract blank png workflow", async () => {
42+
const pngs = await glob("./tests/Blank_*.png");
43+
expect(pngs.length).toBeGreaterThanOrEqual(1);
44+
45+
for await (const filename of pngs) {
46+
const png = Bun.file(filename);
47+
const exif = getPngMetadata(await png.arrayBuffer());
48+
expect(exif.workflow).toBe(undefined);
49+
}
50+
});
51+
52+
it("set blank png workflow", async () => {
53+
const pngs = await glob("./tests/Blank_*.png");
54+
expect(pngs.length).toBeGreaterThanOrEqual(1);
55+
56+
for await (const filename of pngs) {
57+
const png = Bun.file(filename);
58+
59+
const newWorkflow = '{"test":"hello, snomiao"}';
60+
const buffer2 = setPngMetadata(await png.arrayBuffer(), {
61+
workflow: newWorkflow,
62+
});
63+
const file2 = new File([buffer2], png.name!);
64+
65+
const exif2 = getPngMetadata(await file2.arrayBuffer());
66+
const workflow_actual = JSON.stringify(JSON.parse(exif2.workflow));
67+
const workflow_expect = JSON.stringify(JSON.parse(newWorkflow));
68+
expect(workflow_expect).toEqual(workflow_actual);
69+
}
70+
});

0 commit comments

Comments
 (0)