cross-image 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +606 -0
  3. package/esm/mod.d.ts +33 -0
  4. package/esm/mod.d.ts.map +1 -0
  5. package/esm/mod.js +31 -0
  6. package/esm/package.json +3 -0
  7. package/esm/src/formats/ascii.d.ts +27 -0
  8. package/esm/src/formats/ascii.d.ts.map +1 -0
  9. package/esm/src/formats/ascii.js +172 -0
  10. package/esm/src/formats/bmp.d.ts +19 -0
  11. package/esm/src/formats/bmp.d.ts.map +1 -0
  12. package/esm/src/formats/bmp.js +174 -0
  13. package/esm/src/formats/gif.d.ts +40 -0
  14. package/esm/src/formats/gif.d.ts.map +1 -0
  15. package/esm/src/formats/gif.js +385 -0
  16. package/esm/src/formats/jpeg.d.ts +18 -0
  17. package/esm/src/formats/jpeg.d.ts.map +1 -0
  18. package/esm/src/formats/jpeg.js +414 -0
  19. package/esm/src/formats/png.d.ts +33 -0
  20. package/esm/src/formats/png.d.ts.map +1 -0
  21. package/esm/src/formats/png.js +544 -0
  22. package/esm/src/formats/raw.d.ts +23 -0
  23. package/esm/src/formats/raw.d.ts.map +1 -0
  24. package/esm/src/formats/raw.js +98 -0
  25. package/esm/src/formats/tiff.d.ts +58 -0
  26. package/esm/src/formats/tiff.d.ts.map +1 -0
  27. package/esm/src/formats/tiff.js +791 -0
  28. package/esm/src/formats/webp.d.ts +22 -0
  29. package/esm/src/formats/webp.d.ts.map +1 -0
  30. package/esm/src/formats/webp.js +403 -0
  31. package/esm/src/image.d.ts +124 -0
  32. package/esm/src/image.d.ts.map +1 -0
  33. package/esm/src/image.js +320 -0
  34. package/esm/src/types.d.ts +167 -0
  35. package/esm/src/types.d.ts.map +1 -0
  36. package/esm/src/types.js +1 -0
  37. package/esm/src/utils/gif_decoder.d.ts +42 -0
  38. package/esm/src/utils/gif_decoder.d.ts.map +1 -0
  39. package/esm/src/utils/gif_decoder.js +374 -0
  40. package/esm/src/utils/gif_encoder.d.ts +29 -0
  41. package/esm/src/utils/gif_encoder.d.ts.map +1 -0
  42. package/esm/src/utils/gif_encoder.js +226 -0
  43. package/esm/src/utils/jpeg_decoder.d.ts +39 -0
  44. package/esm/src/utils/jpeg_decoder.d.ts.map +1 -0
  45. package/esm/src/utils/jpeg_decoder.js +580 -0
  46. package/esm/src/utils/jpeg_encoder.d.ts +33 -0
  47. package/esm/src/utils/jpeg_encoder.d.ts.map +1 -0
  48. package/esm/src/utils/jpeg_encoder.js +1017 -0
  49. package/esm/src/utils/lzw.d.ts +43 -0
  50. package/esm/src/utils/lzw.d.ts.map +1 -0
  51. package/esm/src/utils/lzw.js +309 -0
  52. package/esm/src/utils/resize.d.ts +9 -0
  53. package/esm/src/utils/resize.d.ts.map +1 -0
  54. package/esm/src/utils/resize.js +52 -0
  55. package/esm/src/utils/tiff_lzw.d.ts +44 -0
  56. package/esm/src/utils/tiff_lzw.d.ts.map +1 -0
  57. package/esm/src/utils/tiff_lzw.js +306 -0
  58. package/esm/src/utils/webp_decoder.d.ts +39 -0
  59. package/esm/src/utils/webp_decoder.d.ts.map +1 -0
  60. package/esm/src/utils/webp_decoder.js +493 -0
  61. package/esm/src/utils/webp_encoder.d.ts +72 -0
  62. package/esm/src/utils/webp_encoder.d.ts.map +1 -0
  63. package/esm/src/utils/webp_encoder.js +627 -0
  64. package/package.json +41 -0
  65. package/script/mod.d.ts +33 -0
  66. package/script/mod.d.ts.map +1 -0
  67. package/script/mod.js +43 -0
  68. package/script/package.json +3 -0
  69. package/script/src/formats/ascii.d.ts +27 -0
  70. package/script/src/formats/ascii.d.ts.map +1 -0
  71. package/script/src/formats/ascii.js +176 -0
  72. package/script/src/formats/bmp.d.ts +19 -0
  73. package/script/src/formats/bmp.d.ts.map +1 -0
  74. package/script/src/formats/bmp.js +178 -0
  75. package/script/src/formats/gif.d.ts +40 -0
  76. package/script/src/formats/gif.d.ts.map +1 -0
  77. package/script/src/formats/gif.js +389 -0
  78. package/script/src/formats/jpeg.d.ts +18 -0
  79. package/script/src/formats/jpeg.d.ts.map +1 -0
  80. package/script/src/formats/jpeg.js +451 -0
  81. package/script/src/formats/png.d.ts +33 -0
  82. package/script/src/formats/png.d.ts.map +1 -0
  83. package/script/src/formats/png.js +548 -0
  84. package/script/src/formats/raw.d.ts +23 -0
  85. package/script/src/formats/raw.d.ts.map +1 -0
  86. package/script/src/formats/raw.js +102 -0
  87. package/script/src/formats/tiff.d.ts +58 -0
  88. package/script/src/formats/tiff.d.ts.map +1 -0
  89. package/script/src/formats/tiff.js +795 -0
  90. package/script/src/formats/webp.d.ts +22 -0
  91. package/script/src/formats/webp.d.ts.map +1 -0
  92. package/script/src/formats/webp.js +440 -0
  93. package/script/src/image.d.ts +124 -0
  94. package/script/src/image.d.ts.map +1 -0
  95. package/script/src/image.js +324 -0
  96. package/script/src/types.d.ts +167 -0
  97. package/script/src/types.d.ts.map +1 -0
  98. package/script/src/types.js +2 -0
  99. package/script/src/utils/gif_decoder.d.ts +42 -0
  100. package/script/src/utils/gif_decoder.d.ts.map +1 -0
  101. package/script/src/utils/gif_decoder.js +378 -0
  102. package/script/src/utils/gif_encoder.d.ts +29 -0
  103. package/script/src/utils/gif_encoder.d.ts.map +1 -0
  104. package/script/src/utils/gif_encoder.js +230 -0
  105. package/script/src/utils/jpeg_decoder.d.ts +39 -0
  106. package/script/src/utils/jpeg_decoder.d.ts.map +1 -0
  107. package/script/src/utils/jpeg_decoder.js +584 -0
  108. package/script/src/utils/jpeg_encoder.d.ts +33 -0
  109. package/script/src/utils/jpeg_encoder.d.ts.map +1 -0
  110. package/script/src/utils/jpeg_encoder.js +1021 -0
  111. package/script/src/utils/lzw.d.ts +43 -0
  112. package/script/src/utils/lzw.d.ts.map +1 -0
  113. package/script/src/utils/lzw.js +314 -0
  114. package/script/src/utils/resize.d.ts +9 -0
  115. package/script/src/utils/resize.d.ts.map +1 -0
  116. package/script/src/utils/resize.js +56 -0
  117. package/script/src/utils/tiff_lzw.d.ts +44 -0
  118. package/script/src/utils/tiff_lzw.d.ts.map +1 -0
  119. package/script/src/utils/tiff_lzw.js +311 -0
  120. package/script/src/utils/webp_decoder.d.ts +39 -0
  121. package/script/src/utils/webp_decoder.d.ts.map +1 -0
  122. package/script/src/utils/webp_decoder.js +497 -0
  123. package/script/src/utils/webp_encoder.d.ts +72 -0
  124. package/script/src/utils/webp_encoder.d.ts.map +1 -0
  125. package/script/src/utils/webp_encoder.js +631 -0
@@ -0,0 +1,451 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.JPEGFormat = void 0;
37
+ // Constants for unit conversions
38
+ const CM_PER_INCH = 2.54;
39
+ /**
40
+ * JPEG format handler
41
+ * Implements a basic JPEG decoder and encoder
42
+ */
43
+ class JPEGFormat {
44
+ constructor() {
45
+ Object.defineProperty(this, "name", {
46
+ enumerable: true,
47
+ configurable: true,
48
+ writable: true,
49
+ value: "jpeg"
50
+ });
51
+ Object.defineProperty(this, "mimeType", {
52
+ enumerable: true,
53
+ configurable: true,
54
+ writable: true,
55
+ value: "image/jpeg"
56
+ });
57
+ }
58
+ canDecode(data) {
59
+ // JPEG signature: FF D8 FF
60
+ return data.length >= 3 &&
61
+ data[0] === 0xff && data[1] === 0xd8 && data[2] === 0xff;
62
+ }
63
+ async decode(data) {
64
+ if (!this.canDecode(data)) {
65
+ throw new Error("Invalid JPEG signature");
66
+ }
67
+ // Parse JPEG structure to get dimensions and metadata
68
+ let pos = 2; // Skip initial FF D8
69
+ let width = 0;
70
+ let height = 0;
71
+ const metadata = {};
72
+ while (pos < data.length - 1) {
73
+ if (data[pos] !== 0xff) {
74
+ pos++;
75
+ continue;
76
+ }
77
+ const marker = data[pos + 1];
78
+ pos += 2;
79
+ // SOF markers (Start of Frame)
80
+ if (marker >= 0xc0 && marker <= 0xcf &&
81
+ marker !== 0xc4 && marker !== 0xc8 && marker !== 0xcc) {
82
+ const _length = (data[pos] << 8) | data[pos + 1];
83
+ // precision at pos+2
84
+ height = (data[pos + 3] << 8) | data[pos + 4];
85
+ width = (data[pos + 5] << 8) | data[pos + 6];
86
+ break;
87
+ }
88
+ // APP0 marker (JFIF)
89
+ if (marker === 0xe0) {
90
+ const length = (data[pos] << 8) | data[pos + 1];
91
+ const appData = data.slice(pos + 2, pos + length);
92
+ this.parseJFIF(appData, metadata, width, height);
93
+ pos += length;
94
+ continue;
95
+ }
96
+ // APP1 marker (EXIF)
97
+ if (marker === 0xe1) {
98
+ const length = (data[pos] << 8) | data[pos + 1];
99
+ const appData = data.slice(pos + 2, pos + length);
100
+ this.parseEXIF(appData, metadata);
101
+ pos += length;
102
+ continue;
103
+ }
104
+ // Skip other markers
105
+ if (marker === 0xd9 || marker === 0xda)
106
+ break; // EOI or SOS
107
+ if (marker >= 0xd0 && marker <= 0xd8)
108
+ continue; // RST markers have no length
109
+ if (marker === 0x01)
110
+ continue; // TEM has no length
111
+ const length = (data[pos] << 8) | data[pos + 1];
112
+ pos += length;
113
+ }
114
+ if (width === 0 || height === 0) {
115
+ throw new Error("Could not determine JPEG dimensions");
116
+ }
117
+ // For a pure JS implementation, we'd need to implement full JPEG decoding
118
+ // which is very complex. Instead, we'll use the browser/runtime's decoder.
119
+ const rgba = await this.decodeUsingRuntime(data, width, height);
120
+ return {
121
+ width,
122
+ height,
123
+ data: rgba,
124
+ metadata: Object.keys(metadata).length > 0 ? metadata : undefined,
125
+ };
126
+ }
127
+ async encode(imageData) {
128
+ const { width, height, data, metadata } = imageData;
129
+ // Try to use runtime encoding if available (better quality)
130
+ if (typeof OffscreenCanvas !== "undefined") {
131
+ try {
132
+ const canvas = new OffscreenCanvas(width, height);
133
+ const ctx = canvas.getContext("2d");
134
+ if (ctx) {
135
+ const imgData = ctx.createImageData(width, height);
136
+ const imgDataData = new Uint8ClampedArray(data);
137
+ imgData.data.set(imgDataData);
138
+ ctx.putImageData(imgData, 0, 0);
139
+ const blob = await canvas.convertToBlob({
140
+ type: "image/jpeg",
141
+ quality: 0.9,
142
+ });
143
+ const arrayBuffer = await blob.arrayBuffer();
144
+ const encoded = new Uint8Array(arrayBuffer);
145
+ // If we have metadata, we need to inject it into the JPEG
146
+ if (metadata && Object.keys(metadata).length > 0) {
147
+ return this.injectMetadata(encoded, metadata);
148
+ }
149
+ return encoded;
150
+ }
151
+ }
152
+ catch (_error) {
153
+ // Fall through to pure JS encoder
154
+ }
155
+ }
156
+ // Fallback to pure JavaScript encoder
157
+ const { JPEGEncoder } = await Promise.resolve().then(() => __importStar(require("../utils/jpeg_encoder.js")));
158
+ const dpiX = metadata?.dpiX ?? 72;
159
+ const dpiY = metadata?.dpiY ?? 72;
160
+ const encoder = new JPEGEncoder(85); // Quality 85
161
+ const encoded = encoder.encode(width, height, data, dpiX, dpiY);
162
+ // Add EXIF metadata if present
163
+ if (metadata && Object.keys(metadata).length > 0) {
164
+ return this.injectMetadata(encoded, metadata);
165
+ }
166
+ return encoded;
167
+ }
168
+ injectMetadata(encoded, metadata) {
169
+ // Find the position after SOI and APP0 to inject APP1 (EXIF)
170
+ let pos = 2; // After SOI (0xFF 0xD8)
171
+ // Skip APP0 if present
172
+ if (pos + 2 < encoded.length && encoded[pos] === 0xff &&
173
+ encoded[pos + 1] === 0xe0) {
174
+ const length = (encoded[pos + 2] << 8) | encoded[pos + 3];
175
+ pos += length + 2;
176
+ }
177
+ // Create EXIF data
178
+ const exifData = this.createEXIFData(metadata);
179
+ if (exifData.length === 0) {
180
+ return encoded;
181
+ }
182
+ // Create APP1 marker with EXIF data
183
+ // APP1 structure: FF E1 [length 2 bytes] "Exif\0\0" [exif data]
184
+ const app1Length = 2 + 6 + exifData.length; // length field + "Exif\0\0" + data
185
+ const app1 = new Uint8Array(2 + 2 + 6 + exifData.length); // marker + length + "Exif\0\0" + data
186
+ app1[0] = 0xff;
187
+ app1[1] = 0xe1; // APP1 marker
188
+ app1[2] = (app1Length >> 8) & 0xff;
189
+ app1[3] = app1Length & 0xff;
190
+ app1[4] = 0x45; // 'E'
191
+ app1[5] = 0x78; // 'x'
192
+ app1[6] = 0x69; // 'i'
193
+ app1[7] = 0x66; // 'f'
194
+ app1[8] = 0x00;
195
+ app1[9] = 0x00;
196
+ app1.set(exifData, 10);
197
+ // Inject APP1 into the JPEG
198
+ const result = new Uint8Array(encoded.length + app1.length);
199
+ result.set(encoded.slice(0, pos), 0);
200
+ result.set(app1, pos);
201
+ result.set(encoded.slice(pos), pos + app1.length);
202
+ return result;
203
+ }
204
+ async decodeUsingRuntime(data, _width, _height) {
205
+ // Try to use ImageDecoder API if available (Deno, modern browsers)
206
+ if (typeof ImageDecoder !== "undefined") {
207
+ try {
208
+ const decoder = new ImageDecoder({ data, type: "image/jpeg" });
209
+ const result = await decoder.decode();
210
+ const bitmap = result.image;
211
+ // Create a canvas to extract pixel data
212
+ const canvas = new OffscreenCanvas(bitmap.displayWidth, bitmap.displayHeight);
213
+ const ctx = canvas.getContext("2d");
214
+ if (!ctx)
215
+ throw new Error("Could not get canvas context");
216
+ ctx.drawImage(bitmap, 0, 0);
217
+ const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
218
+ bitmap.close();
219
+ return new Uint8Array(imageData.data.buffer);
220
+ }
221
+ catch (error) {
222
+ // ImageDecoder API failed, fall through to pure JS decoder
223
+ console.warn("JPEG decoding with ImageDecoder failed, using pure JS decoder:", error);
224
+ }
225
+ }
226
+ // Fallback to pure JavaScript decoder
227
+ try {
228
+ const { JPEGDecoder } = await Promise.resolve().then(() => __importStar(require("../utils/jpeg_decoder.js")));
229
+ const decoder = new JPEGDecoder(data);
230
+ return decoder.decode();
231
+ }
232
+ catch (error) {
233
+ throw new Error(`JPEG decoding failed: ${error}`);
234
+ }
235
+ }
236
+ // Metadata parsing and creation methods
237
+ parseJFIF(data, metadata, width, height) {
238
+ // JFIF format: "JFIF\0" version units xDensity yDensity
239
+ if (data.length < 14)
240
+ return;
241
+ // Check for JFIF identifier
242
+ if (data[0] !== 0x4a || data[1] !== 0x46 || data[2] !== 0x49 ||
243
+ data[3] !== 0x46 || data[4] !== 0x00)
244
+ return;
245
+ const units = data[7]; // 0=no units, 1=dpi, 2=dpcm
246
+ const xDensity = (data[8] << 8) | data[9];
247
+ const yDensity = (data[10] << 8) | data[11];
248
+ if (units === 1 && xDensity > 0 && yDensity > 0) {
249
+ // Units are DPI
250
+ metadata.dpiX = xDensity;
251
+ metadata.dpiY = yDensity;
252
+ metadata.physicalWidth = width / xDensity;
253
+ metadata.physicalHeight = height / yDensity;
254
+ }
255
+ else if (units === 2 && xDensity > 0 && yDensity > 0) {
256
+ // Units are dots per cm, convert to DPI
257
+ metadata.dpiX = Math.round(xDensity * CM_PER_INCH);
258
+ metadata.dpiY = Math.round(yDensity * CM_PER_INCH);
259
+ metadata.physicalWidth = width / metadata.dpiX;
260
+ metadata.physicalHeight = height / metadata.dpiY;
261
+ }
262
+ }
263
+ parseEXIF(data, metadata) {
264
+ // Check for EXIF identifier
265
+ if (data.length < 6 || data[0] !== 0x45 || data[1] !== 0x78 ||
266
+ data[2] !== 0x69 || data[3] !== 0x66 || data[4] !== 0x00 ||
267
+ data[5] !== 0x00)
268
+ return;
269
+ // Skip "Exif\0\0" header
270
+ const exifData = data.slice(6);
271
+ if (exifData.length < 8)
272
+ return;
273
+ try {
274
+ const byteOrder = String.fromCharCode(exifData[0], exifData[1]);
275
+ const littleEndian = byteOrder === "II";
276
+ // Read IFD0 offset
277
+ const ifd0Offset = littleEndian
278
+ ? exifData[4] | (exifData[5] << 8) | (exifData[6] << 16) |
279
+ (exifData[7] << 24)
280
+ : (exifData[4] << 24) | (exifData[5] << 16) | (exifData[6] << 8) |
281
+ exifData[7];
282
+ if (ifd0Offset + 2 > exifData.length)
283
+ return;
284
+ // Read number of entries with bounds check
285
+ const numEntries = littleEndian
286
+ ? exifData[ifd0Offset] | (exifData[ifd0Offset + 1] << 8)
287
+ : (exifData[ifd0Offset] << 8) | exifData[ifd0Offset + 1];
288
+ // Parse entries
289
+ for (let i = 0; i < numEntries; i++) {
290
+ const entryOffset = ifd0Offset + 2 + i * 12;
291
+ if (entryOffset + 12 > exifData.length)
292
+ break;
293
+ const tag = littleEndian
294
+ ? exifData[entryOffset] | (exifData[entryOffset + 1] << 8)
295
+ : (exifData[entryOffset] << 8) | exifData[entryOffset + 1];
296
+ // DateTime tag (0x0132)
297
+ if (tag === 0x0132) {
298
+ const valueOffset = littleEndian
299
+ ? exifData[entryOffset + 8] | (exifData[entryOffset + 9] << 8) |
300
+ (exifData[entryOffset + 10] << 16) |
301
+ (exifData[entryOffset + 11] << 24)
302
+ : (exifData[entryOffset + 8] << 24) |
303
+ (exifData[entryOffset + 9] << 16) |
304
+ (exifData[entryOffset + 10] << 8) | exifData[entryOffset + 11];
305
+ if (valueOffset < exifData.length) {
306
+ const endIndex = exifData.indexOf(0, valueOffset);
307
+ if (endIndex > valueOffset) {
308
+ const dateStr = new TextDecoder().decode(exifData.slice(valueOffset, endIndex));
309
+ const match = dateStr.match(/^(\d{4}):(\d{2}):(\d{2}) (\d{2}):(\d{2}):(\d{2})$/);
310
+ if (match) {
311
+ metadata.creationDate = new Date(parseInt(match[1]), parseInt(match[2]) - 1, parseInt(match[3]), parseInt(match[4]), parseInt(match[5]), parseInt(match[6]));
312
+ }
313
+ }
314
+ }
315
+ }
316
+ // ImageDescription tag (0x010E)
317
+ if (tag === 0x010e) {
318
+ const valueOffset = littleEndian
319
+ ? exifData[entryOffset + 8] | (exifData[entryOffset + 9] << 8) |
320
+ (exifData[entryOffset + 10] << 16) |
321
+ (exifData[entryOffset + 11] << 24)
322
+ : (exifData[entryOffset + 8] << 24) |
323
+ (exifData[entryOffset + 9] << 16) |
324
+ (exifData[entryOffset + 10] << 8) | exifData[entryOffset + 11];
325
+ if (valueOffset < exifData.length) {
326
+ const endIndex = exifData.indexOf(0, valueOffset);
327
+ if (endIndex > valueOffset) {
328
+ metadata.description = new TextDecoder().decode(exifData.slice(valueOffset, endIndex));
329
+ }
330
+ }
331
+ }
332
+ // Artist tag (0x013B)
333
+ if (tag === 0x013b) {
334
+ const valueOffset = littleEndian
335
+ ? exifData[entryOffset + 8] | (exifData[entryOffset + 9] << 8) |
336
+ (exifData[entryOffset + 10] << 16) |
337
+ (exifData[entryOffset + 11] << 24)
338
+ : (exifData[entryOffset + 8] << 24) |
339
+ (exifData[entryOffset + 9] << 16) |
340
+ (exifData[entryOffset + 10] << 8) | exifData[entryOffset + 11];
341
+ if (valueOffset < exifData.length) {
342
+ const endIndex = exifData.indexOf(0, valueOffset);
343
+ if (endIndex > valueOffset) {
344
+ metadata.author = new TextDecoder().decode(exifData.slice(valueOffset, endIndex));
345
+ }
346
+ }
347
+ }
348
+ // Copyright tag (0x8298)
349
+ if (tag === 0x8298) {
350
+ const valueOffset = littleEndian
351
+ ? exifData[entryOffset + 8] | (exifData[entryOffset + 9] << 8) |
352
+ (exifData[entryOffset + 10] << 16) |
353
+ (exifData[entryOffset + 11] << 24)
354
+ : (exifData[entryOffset + 8] << 24) |
355
+ (exifData[entryOffset + 9] << 16) |
356
+ (exifData[entryOffset + 10] << 8) | exifData[entryOffset + 11];
357
+ if (valueOffset < exifData.length) {
358
+ const endIndex = exifData.indexOf(0, valueOffset);
359
+ if (endIndex > valueOffset) {
360
+ metadata.copyright = new TextDecoder().decode(exifData.slice(valueOffset, endIndex));
361
+ }
362
+ }
363
+ }
364
+ }
365
+ }
366
+ catch (_e) {
367
+ // Ignore EXIF parsing errors
368
+ }
369
+ }
370
+ createEXIFData(metadata) {
371
+ const entries = [];
372
+ // Add DateTime if available
373
+ if (metadata.creationDate) {
374
+ const date = metadata.creationDate;
375
+ const dateStr = `${date.getFullYear()}:${String(date.getMonth() + 1).padStart(2, "0")}:${String(date.getDate()).padStart(2, "0")} ${String(date.getHours()).padStart(2, "0")}:${String(date.getMinutes()).padStart(2, "0")}:${String(date.getSeconds()).padStart(2, "0")}\0`;
376
+ entries.push({
377
+ tag: 0x0132,
378
+ type: 2, // ASCII
379
+ value: new TextEncoder().encode(dateStr),
380
+ });
381
+ }
382
+ // Add ImageDescription
383
+ if (metadata.description) {
384
+ entries.push({
385
+ tag: 0x010e,
386
+ type: 2,
387
+ value: new TextEncoder().encode(metadata.description + "\0"),
388
+ });
389
+ }
390
+ // Add Artist
391
+ if (metadata.author) {
392
+ entries.push({
393
+ tag: 0x013b,
394
+ type: 2,
395
+ value: new TextEncoder().encode(metadata.author + "\0"),
396
+ });
397
+ }
398
+ // Add Copyright
399
+ if (metadata.copyright) {
400
+ entries.push({
401
+ tag: 0x8298,
402
+ type: 2,
403
+ value: new TextEncoder().encode(metadata.copyright + "\0"),
404
+ });
405
+ }
406
+ if (entries.length === 0)
407
+ return [];
408
+ // Build EXIF structure
409
+ const exif = [];
410
+ // Byte order marker (little endian)
411
+ exif.push(0x49, 0x49); // "II"
412
+ exif.push(0x2a, 0x00); // 42
413
+ // Offset to IFD0 (8 bytes from start)
414
+ exif.push(0x08, 0x00, 0x00, 0x00);
415
+ // Number of entries
416
+ exif.push(entries.length & 0xff, (entries.length >> 8) & 0xff);
417
+ // Calculate data offset
418
+ let dataOffset = 8 + 2 + entries.length * 12 + 4;
419
+ for (const entry of entries) {
420
+ // Tag
421
+ exif.push(entry.tag & 0xff, (entry.tag >> 8) & 0xff);
422
+ // Type
423
+ exif.push(entry.type & 0xff, (entry.type >> 8) & 0xff);
424
+ // Count
425
+ const count = entry.value.length;
426
+ exif.push(count & 0xff, (count >> 8) & 0xff, (count >> 16) & 0xff, (count >> 24) & 0xff);
427
+ // Value/Offset
428
+ if (entry.value.length <= 4) {
429
+ for (let i = 0; i < 4; i++) {
430
+ exif.push(i < entry.value.length ? entry.value[i] : 0);
431
+ }
432
+ }
433
+ else {
434
+ exif.push(dataOffset & 0xff, (dataOffset >> 8) & 0xff, (dataOffset >> 16) & 0xff, (dataOffset >> 24) & 0xff);
435
+ dataOffset += entry.value.length;
436
+ }
437
+ }
438
+ // Next IFD offset (0 = no more IFDs)
439
+ exif.push(0x00, 0x00, 0x00, 0x00);
440
+ // Append data for entries that didn't fit in value field
441
+ for (const entry of entries) {
442
+ if (entry.value.length > 4) {
443
+ for (const byte of entry.value) {
444
+ exif.push(byte);
445
+ }
446
+ }
447
+ }
448
+ return exif;
449
+ }
450
+ }
451
+ exports.JPEGFormat = JPEGFormat;
@@ -0,0 +1,33 @@
1
+ import type { ImageData, ImageFormat } from "../types.js";
2
+ /**
3
+ * PNG format handler
4
+ * Implements a pure JavaScript PNG decoder and encoder
5
+ */
6
+ export declare class PNGFormat implements ImageFormat {
7
+ readonly name = "png";
8
+ readonly mimeType = "image/png";
9
+ canDecode(data: Uint8Array): boolean;
10
+ decode(data: Uint8Array): Promise<ImageData>;
11
+ encode(imageData: ImageData): Promise<Uint8Array>;
12
+ private readUint32;
13
+ private writeUint32;
14
+ private concatenateChunks;
15
+ private inflate;
16
+ private deflate;
17
+ private unfilterAndConvert;
18
+ private unfilterScanline;
19
+ private paethPredictor;
20
+ private filterData;
21
+ private getBytesPerPixel;
22
+ private getBitsPerPixel;
23
+ private createChunk;
24
+ private crc32;
25
+ private parsePhysChunk;
26
+ private parseTextChunk;
27
+ private parseITxtChunk;
28
+ private parseExifChunk;
29
+ private createPhysChunk;
30
+ private createTextChunk;
31
+ private createExifChunk;
32
+ }
33
+ //# sourceMappingURL=png.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"png.d.ts","sourceRoot":"","sources":["../../../src/src/formats/png.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAiB,MAAM,aAAa,CAAC;AAKzE;;;GAGG;AACH,qBAAa,SAAU,YAAW,WAAW;IAC3C,QAAQ,CAAC,IAAI,SAAS;IACtB,QAAQ,CAAC,QAAQ,eAAe;IAEhC,SAAS,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO;IAS9B,MAAM,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC;IA+E5C,MAAM,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC;IAuGvD,OAAO,CAAC,UAAU;IAKlB,OAAO,CAAC,WAAW;IAOnB,OAAO,CAAC,iBAAiB;YAgBX,OAAO;YAQP,OAAO;IAQrB,OAAO,CAAC,kBAAkB;IA0D1B,OAAO,CAAC,gBAAgB;IAiCxB,OAAO,CAAC,cAAc;IAWtB,OAAO,CAAC,UAAU;IAmBlB,OAAO,CAAC,gBAAgB;IAKxB,OAAO,CAAC,eAAe;IAiBvB,OAAO,CAAC,WAAW;IAanB,OAAO,CAAC,KAAK;IAab,OAAO,CAAC,cAAc;IAqBtB,OAAO,CAAC,cAAc;IA6BtB,OAAO,CAAC,cAAc;IA4CtB,OAAO,CAAC,cAAc;IAmEtB,OAAO,CAAC,eAAe;IAkBvB,OAAO,CAAC,eAAe;IAYvB,OAAO,CAAC,eAAe;CAkFxB"}