@crewhaus/tool-media 0.7.0 → 0.8.1

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.
@@ -21,6 +21,59 @@ export declare function readJpegSegments(bytes: Uint8Array): {
21
21
  segments: JpegSegment[];
22
22
  scanStart: number;
23
23
  };
24
+ /** One piece of a JPEG, in file order: a marker segment, or a scan's entropy-coded bytes. */
25
+ export type JpegPart = {
26
+ readonly type: "segment";
27
+ readonly segment: JpegSegment;
28
+ } | {
29
+ readonly type: "scan";
30
+ readonly start: number;
31
+ readonly end: number;
32
+ };
33
+ export type JpegWalk = {
34
+ /** Every piece after SOI, in order, up to and including EOI. */
35
+ readonly parts: ReadonlyArray<JpegPart>;
36
+ /** The marker segments alone, in order. */
37
+ readonly segments: ReadonlyArray<JpegSegment>;
38
+ /** Offset just past EOI, or null when the file ends without one. */
39
+ readonly eoiEnd: number | null;
40
+ /** Bytes after EOI: where an appended image or video lives. 0 when EOI is missing. */
41
+ readonly trailing: number;
42
+ /**
43
+ * Where the walk stopped short of EOI after the first scan, and why: a
44
+ * segment the end of the file cuts off, or bytes where a marker should
45
+ * be. Null when it reached EOI or the file's end in scan data. The bytes
46
+ * from here on were not walked.
47
+ */
48
+ readonly stopped: {
49
+ readonly offset: number;
50
+ readonly reason: string;
51
+ } | null;
52
+ };
53
+ /**
54
+ * Walk the WHOLE codestream: every marker segment, including those between
55
+ * the scans of a progressive or multi-scan file, to EOI.
56
+ *
57
+ * `readJpegSegments` stops at the first start-of-scan, which is all a header
58
+ * reader needs, and 0.7.0's ExifStrip copied everything after that point
59
+ * verbatim. But an APPn or COM segment may sit between scans, and a camera
60
+ * appends whole images after EOI (MPF previews, HDR gain maps, stereo pairs)
61
+ * and phones append motion-photo video; each can carry its own EXIF and GPS.
62
+ *
63
+ * Inside a scan, `0xFF` followed by `0x00` (stuffing), `0xD0`–`0xD7` (restart
64
+ * markers) or another `0xFF` (fill) belongs to the scan; any other `0xFF xx`
65
+ * is the next marker. The walk is linear: the scan search is a native
66
+ * `indexOf` from where the last one stopped.
67
+ *
68
+ * A broken header (before the first scan) is refused. Past the first scan
69
+ * the picture is already there, and a file cut short there — a partial
70
+ * download, a truncated progressive JPEG — is still a photo: the walk stops
71
+ * at the break and says where (`stopped`), rather than refusing a file
72
+ * 0.7.0 read, which only ever looked as far as the first scan.
73
+ */
74
+ export declare function walkJpeg(bytes: Uint8Array): JpegWalk;
75
+ /** A hint at what the bytes after EOI are, for a person reading the result. */
76
+ export declare function describeTrailer(trailer: Uint8Array): string;
24
77
  export type GpsFix = {
25
78
  readonly latitude: number;
26
79
  readonly longitude: number;
@@ -72,10 +125,95 @@ export type StripResult = {
72
125
  bytes: number;
73
126
  }>;
74
127
  readonly bytesRemoved: number;
128
+ /** Bytes after EOI that were dropped: an appended image or video, and its metadata. */
129
+ readonly trailingBytesRemoved: number;
130
+ /** What those bytes looked like, when there were any. */
131
+ readonly trailingKind?: string;
132
+ /** The file ends without EOI; everything up to its end was walked and kept. */
133
+ readonly eoiMissing: boolean;
134
+ /**
135
+ * Where the file breaks off after the first scan, when it does: the walk
136
+ * stopped there, and the bytes after it were kept verbatim (or, when they
137
+ * begin a metadata segment, dropped).
138
+ */
139
+ readonly breaksOffAt?: {
140
+ readonly offset: number;
141
+ readonly reason: string;
142
+ };
75
143
  };
76
144
  /**
77
- * A JPEG with its metadata segments dropped. The scan data is copied
78
- * verbatim — nothing is re-encoded, so the image is bit-for-bit the same
79
- * picture, just without the record of who took it and where.
145
+ * A JPEG with its metadata dropped: every APPn and COM segment the options
146
+ * do not keep, wherever it sits (before the first scan or between scans),
147
+ * and everything after EOI. The scans' entropy-coded bytes are copied
148
+ * verbatim — nothing is re-encoded, so the primary image is bit-for-bit the
149
+ * same picture, without the record of who took it and where.
150
+ *
151
+ * Dropping the trailer loses an appended preview, gain map or motion-photo
152
+ * video, and the metadata inside each. Nothing in the stripped file points
153
+ * at them any more: the MPF index (APP2) and the XMP (APP1) that did are
154
+ * metadata segments and are removed.
80
155
  */
81
156
  export declare function stripJpegMetadata(bytes: Uint8Array, options?: StripOptions): StripResult;
157
+ /** One EXIF block found anywhere in a file, with where it was. */
158
+ export type ExifLocation = {
159
+ readonly where: "before the first scan" | "between scans" | "in an appended image" | "in bytes no walk accounts for";
160
+ readonly offset: number;
161
+ readonly exif: ExifData | null;
162
+ /** Why `exif` is null: the block is there but could not be parsed. */
163
+ readonly unreadable?: string;
164
+ };
165
+ export type JpegMetadataReport = {
166
+ /** Every EXIF block in the file, the primary one first. */
167
+ readonly exifBlocks: ReadonlyArray<ExifLocation>;
168
+ /** APPn and COM segments that sit between or after the scans of the main image. */
169
+ readonly interScanMetadataSegments: ReadonlyArray<string>;
170
+ /** APPn segments before the first scan, by name. */
171
+ readonly metadataSegments: ReadonlyArray<string>;
172
+ readonly trailingBytes: number;
173
+ readonly trailingKind?: string;
174
+ /** JPEGs found in the trailer and walked for EXIF. */
175
+ readonly embeddedImages: number;
176
+ /** JPEG starts in the trailer beyond the ones walked; their metadata is unknown. */
177
+ readonly embeddedImagesNotInspected: number;
178
+ readonly eoiMissing: boolean;
179
+ /** Where the main image breaks off after its first scan, when it does. */
180
+ readonly breaksOffAt?: {
181
+ readonly offset: number;
182
+ readonly reason: string;
183
+ };
184
+ /**
185
+ * Bytes no walk accounts for and that are not padding: after a break,
186
+ * after EOI outside every appended image walked (a video, data this does
187
+ * not recognise, an image that would not walk), or past the end of what
188
+ * was read. They were searched for EXIF blocks, not parsed, so they may
189
+ * hold a location this report does not see.
190
+ */
191
+ readonly unaccountedBytes: number;
192
+ /** EXIF signatures in those bytes beyond the ones parsed. */
193
+ readonly exifSignaturesNotParsed: number;
194
+ /** Every APPn segment other than EXIF, in the main image and the appended ones walked. */
195
+ readonly otherMetadata: ReadonlyArray<OtherMetadata>;
196
+ };
197
+ /**
198
+ * A metadata segment other than EXIF, and whether it names a location.
199
+ * `location` is true when it does, false when it was read (or its format
200
+ * has no place for one) and it does not, and null when this reader does
201
+ * not parse it, so it may.
202
+ */
203
+ export type OtherMetadata = {
204
+ /** `"APP1 XMP"`, `"APP13 Photoshop"`, or the marker and its identifier. */
205
+ readonly segment: string;
206
+ readonly where: ExifLocation["where"];
207
+ readonly offset: number;
208
+ readonly location: boolean | null;
209
+ };
210
+ /**
211
+ * Every place in a JPEG that can carry metadata: EXIF before and between
212
+ * scans, other APPn/COM segments, and the JPEGs appended after EOI (walked
213
+ * once each, not their own trailers again). Bytes no walk accounts for —
214
+ * after a break, a video, unrecognised data, an appended image that would
215
+ * not walk — are counted and searched for EXIF blocks, which are parsed
216
+ * where found. What this reports is what ExifRead answers from, so it
217
+ * cannot call a file clean on the strength of bytes it never read.
218
+ */
219
+ export declare function inspectJpegMetadata(bytes: Uint8Array): JpegMetadataReport;