devongovett / exif-reader Goto Github PK
View Code? Open in Web Editor NEWA small EXIF image metadata reader
License: MIT License
A small EXIF image metadata reader
License: MIT License
Hi there!
I just ran into the same issue as #11 ("Buffer must start with Exif") when using exif-reader on the "exif data" from a PNG. I'm using sharp to extract the metadata (as per the example in that other thread)
const sharp = require('sharp');
const exifReader = require('exif-reader');
sharp('example.png')
.metadata()
.then(({ exif }) => {
const exifProperties = exifReader(exif);
console.log(exifProperties);
});
is this supposed to work? Is exif-reader capable of reading the exif data coming from a PNG? I'm assuming the error is thrown because of the format differences between exif data in JPEGs vs "exif" data under metadata chunks in PNGs? 🤔
According to the spec, exif only supports ASCII.
2 = ASCII An 8-bit byte containing one 7-bit ASCII code. The final byte is terminated with NULL.
Yet in practice, a lot of tools write UTF8 text into fields such as ImageDescription
.
My suggestion would be to optionally support different charsets when decoding exif type 2.
If that's a change you're willing to accept, I'd gladly submit a PR.
Hey.
Gives an error when trying to read exif data from .avif
format
Error: Invalid EXIF data: buffer should start with "Exif", "MM" or "II".
Does this library not support avif parsing?
Hi,
great piece of code. Works well with images. however I was looking into getting exif data from video formats. Do you know anything about it? I would love to make that work.
Note: This is a RFC and not a real issue. My intention is to get
feedback about an idea I had when tried to solve my own issue #27 for better
Typescript support.
While working on this issue I noticed some things that made me scratch my head
So I spent some time looking at exiftool
which seems to be the original inspiration to this project.
Then I found the nice Exiv2 project with
a working link to the Exif spec and
a well structured list of all standard Exif tags together with type information.
When I realised that the exiv2 cli was already installed on my machine, I used it
to compare the ouptut of exiv2 and exif-reader with exiv2 -p e some.jpg
.
IMHO the exiv2 project is trying to follow the spec one to one, while the exiftool project
diverges a bit. For example the tag 0x0132 is called DateTime
in Exif and Exiv2, but ModifyDate
in exiftool and exif-reader. Exiftool lists tags that are not found in the specs.
The exiv2 project provides structured access to the data type of each tag, my
original motiviation for starting all this.
So my proposal is, can we just generate the tags.js
file and the type definition index.d.ts
based on the metadata from eviv2? That would give us some benefits
It also has some drawbacks:
InkSet
, InkNames
or FaxRecvParams
) now no longer have a tag name (but wouldI prepared a branch of my proposed change. Please have a look.
I think this change would align this project with the Exif Spec,
but I fully understand that this is a breaking change that will cause effort for users of this project.
In most cases it's just adjusting tag names. The following table of changed tag names
will help with that. Only if someone relied on the edge case, when a file really contains
different values for different tags with the same name (e.g. WhiteBalance),
they will experience a behavioural change.
This is most probable a bug in exif-reader, as those tags would return wrong results if
they exist in the same file:
Tag Name | TagIds |
---|---|
ImageWidth | 0x0100, 0xbc80 |
ImageHeight | 0x0101, 0xbc81 |
JPEGTables | 0x015b, 0x01b5 |
FlashEnergy | 0x920b, 0xa20b |
SpatialFrequencyResponse | 0x920c, 0xa20c |
Noise | 0x920d, 0xa20d |
FocalPlaneXResolution | 0x920e, 0xa20e |
FocalPlaneYResolution | 0x920f, 0xa20f |
FocalPlaneResolutionUnit | 0x9210, 0xa210 |
ImageNumber | 0x9211, 0xa211 |
SecurityClassification | 0x9212, 0xa212 |
ImageHistory | 0x9213, 0xa213 |
ExposureIndex | 0x9215, 0xa215 |
TIFF-EPStandardID | 0x9216, 0xa216 |
SensingMethod | 0x9217, 0xa217 |
WhiteBalance | 0xa403, 0xfe4e |
Contrast | 0xa408, 0xfe54 |
Saturation | 0xa409, 0xfe55 |
Sharpness | 0xa40a, 0xfe56 |
Current Tag Name | Proposed Tag Name Exiv2/Exif |
---|---|
InteropIndex | InteroperabilityIndex |
InteropVersion | InteroperabilityVersion |
OldSubfileType | SubfileType |
ImageHeight | ImageLength |
ModifyDate | DateTime |
SubIFD | SubIFDs |
NumberofInks | NumberOfInks |
ThumbnailOffset | JPEGInterchangeFormat |
ThumbnailLength | JPEGInterchangeFormatLength |
ApplicationNotes | XMLPacket |
RelatedImageHeight | RelatedImageLength |
CFAPattern2 | CFAPattern |
IPTC-NAA | IPTCNAA |
PhotoshopSettings | ImageResources |
ExifOffset | ExifTag |
ICC_Profile | InterColorProfile |
GPSInfo | GPSTag |
ISO | ISOSpeedRatings |
Opto-ElectricConvFactor | OECF |
TIFF-EPStandardID | TIFFEPStandardID |
InteropOffset | InteroperabilityTag |
FocalLengthIn35mmFormat | FocalLengthIn35mmFilm |
PrintIM | PrintImageMatching |
DNGLensInfo | LensInfo |
CameraCalibrationSig | CameraCalibrationSignature |
ProfileCalibrationSig | ProfileCalibrationSignature |
ProfileIFD | ExtraCameraProfiles |
OriginalBestQualitySize | OriginalBestQualityFinalSize |
Current Tag Name | Proposed Tag Name Exiv2/Exif |
---|---|
28722 | VignettingCorrParams |
28725 | ChromaticAberrationCorrParams |
28727 | DistortionCorrParams |
51177 | DepthFormat |
51178 | DepthNear |
51179 | DepthFar |
51180 | DepthUnits |
51181 | DepthMeasureType |
51182 | EnhanceParams |
52525 | ProfileGainTableMap |
52526 | SemanticName |
52528 | SemanticInstanceID |
52529 | CalibrationIlluminant3 |
52530 | CameraCalibration3 |
52531 | ColorMatrix3 |
52532 | ForwardMatrix3 |
52533 | IlluminantData1 |
52534 | IlluminantData2 |
52535 | IlluminantData3 |
52536 | MaskSubArea |
52537 | ProfileHueSatMapData3 |
52538 | ReductionMatrix3 |
52539 | RGBTables |
36880 | OffsetTime |
36881 | OffsetTimeOriginal |
36882 | OffsetTimeDigitized |
37888 | Temperature |
37889 | Humidity |
37890 | Pressure |
37891 | WaterDepth |
37892 | Acceleration |
37893 | CameraElevationAngle |
42080 | CompositeImage |
42081 | SourceImageNumberOfCompositeImage |
42082 | SourceExposureTimesOfCompositeImage |
A sampling of those values on https://exiftool.org/TagNames/EXIF.html indicates that they are either
offsets to vendor specific IFDs or are not part of any IFD (basicly undocumented on exiftool)
Current Tag Name | Generic Tag Id, not present in Exif Standard |
---|---|
MinSampleValue | 280 |
MaxSampleValue | 281 |
FreeOffsets | 288 |
FreeByteCounts | 289 |
ColorResponseUnit | 300 |
BadFaxLines | 326 |
CleanFaxData | 327 |
ConsecutiveBadFaxLines | 328 |
GlobalParametersIFD | 400 |
ProfileType | 401 |
FaxProfile | 402 |
CodingMethods | 403 |
VersionYear | 404 |
ModeNumber | 405 |
Decode | 433 |
DefaultImageColor | 434 |
T82Options | 435 |
StripRowCounts | 559 |
USPTOMiscellaneous | 999 |
XP_DIP_XML | 18247 |
StitchInfo | 18248 |
WangTag1 | 32931 |
WangAnnotation | 32932 |
WangTag3 | 32933 |
WangTag4 | 32934 |
Matteing | 32995 |
DataType | 32996 |
ImageDepth | 32997 |
TileDepth | 32998 |
Model2 | 33405 |
KodakIFD | 33424 |
MDFileTag | 33445 |
MDScalePixel | 33446 |
MDColorTable | 33447 |
MDLabName | 33448 |
MDSampleInfo | 33449 |
MDPrepDate | 33450 |
MDPrepTime | 33451 |
MDFileUnits | 33452 |
PixelScale | 33550 |
AdventScale | 33589 |
AdventRevision | 33590 |
UIC1Tag | 33628 |
UIC2Tag | 33629 |
UIC3Tag | 33630 |
UIC4Tag | 33631 |
IntergraphPacketData | 33918 |
IntergraphFlagRegisters | 33919 |
IntergraphMatrix | 33920 |
INGRReserved | 33921 |
ModelTiePoint | 33922 |
Site | 34016 |
ColorSequence | 34017 |
IT8Header | 34018 |
RasterPadding | 34019 |
BitsPerRunLength | 34020 |
BitsPerExtendedRunLength | 34021 |
ColorTable | 34022 |
ImageColorIndicator | 34023 |
BackgroundColorIndicator | 34024 |
ImageColorValue | 34025 |
BackgroundColorValue | 34026 |
PixelIntensityRange | 34027 |
TransparencyIndicator | 34028 |
ColorCharacterization | 34029 |
HCUsage | 34030 |
TrapIndicator | 34031 |
CMYKEquivalent | 34032 |
SEMInfo | 34118 |
AFCP_IPTC | 34152 |
PixelMagicJBIGOptions | 34232 |
ModelTransform | 34264 |
WB_GRGBLevels | 34306 |
LeafData | 34310 |
TIFF_FXExtensions | 34687 |
MultiProfiles | 34688 |
SharedData | 34689 |
T88Options | 34690 |
ImageLayer | 34732 |
GeoTiffDirectory | 34735 |
GeoTiffDoubleParams | 34736 |
GeoTiffAsciiParams | 34737 |
FaxRecvParams | 34908 |
FaxSubAddress | 34909 |
FaxRecvTime | 34910 |
LeafSubIFD | 34954 |
CIP3DataFile | 37434 |
CIP3Sheet | 37435 |
CIP3Side | 37436 |
StoNits | 37439 |
MSDocumentText | 37679 |
MSPropertySetStorage | 37680 |
MSDocumentTextPosition | 37681 |
ImageSourceData | 37724 |
TIFF-EPStandardID | 41494 |
GDALMetadata | 42112 |
GDALNoData | 42113 |
ExpandSoftware | 44992 |
ExpandLens | 44993 |
ExpandFilm | 44994 |
ExpandFilterLens | 44995 |
ExpandScanner | 44996 |
ExpandFlashLamp | 44997 |
PixelFormat | 48129 |
Transformation | 48130 |
Uncompressed | 48131 |
ImageType | 48132 |
ImageHeight | 48257 |
WidthResolution | 48258 |
HeightResolution | 48259 |
ImageOffset | 48320 |
ImageByteCount | 48321 |
AlphaOffset | 48322 |
AlphaByteCount | 48323 |
ImageDataDiscard | 48324 |
AlphaDataDiscard | 48325 |
OceScanjobDesc | 50215 |
OceApplicationSelector | 50216 |
OceIDNumber | 50217 |
OceImageLogic | 50218 |
Annotations | 50255 |
USPTOOriginalContentType | 50560 |
RawImageSegmentation | 50752 |
AliasLayerMetadata | 50784 |
PanasonicTitle | 50898 |
PanasonicTitle2 | 50899 |
Padding | 59932 |
OffsetSchema | 59933 |
OwnerName | 65000 |
SerialNumber | 65001 |
Lens | 65002 |
KDC_IFD | 65024 |
RawFile | 65100 |
Converter | 65101 |
Exposure | 65105 |
Shadows | 65106 |
Brightness | 65107 |
Smoothness | 65111 |
MoireFilter | 65112 |
The current export of the type definition in index.d.ts
looks like this:
export default function exif(buffer: Buffer): Exif;
However, the current implementation in index.js
does not export any default. The type definition should actually look like this (similar to the old type definitions):
declare function exif(buffer: Buffer): Exif;
export = exif;
or you need to adapt the exports in index.js
appropriately.
When I import the library using import exifReader from 'exif-reader';
I will get following runtime error:
(0 , exif_reader_1.default) is not a function
It only works when I import and use it the following way:
import * as exifReader from 'exif-reader';
const metadata = await sharp(file).metadata();
// @ts-ignore
const exif = exifReader(metadata.exif);
(I cannot set "esModuleInterop": true
in tsconfig due to some other library.)
If you use this project with typescript, you have to create a declaration file every time to prevent this error:
Could not find a declaration file for module 'exif-reader'. '/home/Code/node_modules/exif-reader/index.js' implicitly has an 'any' type.
Try `npm i --save-dev @types/exif-reader` if it exists or add a new declaration (.d.ts) file containing `declare module 'exif-reader';`
It will be easier to add type definitions directly to this project instead of maintaining a mirror in the already too large DefinetelyTyped (@types) project.
const sharpImage = sharp(src, { animated: true });
sharpImage.png({ quality: quality });
sharpImage.withExif({
IFD0: {
Software: `sharp:${quality}`,
},
});
sharpImage.keepMetadata();
sharpImage.keepExif();
const buffer = await sharpImage.toBuffer();
const { exif } = await sharp(buffer).metadata();
console.log(exif?.toString());
try {
if (exif !== undefined) {
console.log(exif.toString().substring(0, 2) === "II");
console.log(ExifReader.load(exif));
}
} catch (err) {
console.log(err);
}
II↕☺♥☺☺→☺♣☺b☺♣☺(☺♥☺☻1☺☻ r‼☻♥☺☺i�♦☺|x2☻�♥x2☻�♥sharp:80♠�♦0210☺�♦☺☻♥�♦0100☺�♥☺��☻�♦☺�☻♥�♦☺ ♥
Error: Invalid EXIF data: buffer should start with "Exif", "MM" or "II".
at module.exports (\node_modules\exif-reader\index.js:8:13)
at test (\vite.config.ts:130:50)
at async FSWatcher.<anonymous> (\vite.config.ts:102:5)
import axios from "axios";
import exifReader from "exif-reader";
async function fetchAndParseEXIF(url) {
try {
const response = await axios.get(url, {
responseType: "arraybuffer",
});
console.log("Buffer length:", response.data.byteLength);
const exifData = exifReader(response.data);
console.log(exifData);
} catch (error) {
console.error("Error fetching or parsing EXIF data:", error);
}
}
fetchAndParseEXIF("http://lzwlkj.oss-cn-shenzhen.aliyuncs.com/jahy/vc-upload-1713346760053-4.jpg");
If Exif offsets happen to be random garbage - package tries to split at those random positions without validation. This will cause "Out Of Memory" error and process stop node itself.
I'd suggest as first command in readTag function add validation of abnormal valueOffset and numValues:
function readTag(buffer, offset, bigEndian) {
...
// Prevent damaged Exif data to cause Out Of Memory
if (valueOffset >= buffer.length || numValues > 1000000 || valueOffset + numValues * valueSize >= buffer.length)
throw new Error("Damaged Exif data");
numValues > 1000000 (just assumed very large number) - but without this check - last comparsion may not always work - it can rollower.
Attached damaged header Exif information
damaged_header.zip
trying to read a buffer created using:
var exifreader = Npm.require('exif-reader');
var buf = Buffer.from(fileObj.blob, 'binary');
var meta = exifreader(buf);
e.g the following top image contains EXIF metadata
http://www.w3.org/MarkUp/Test/xhtml-print/20050519/tests/A_2_1-BF-01.htm
but triggers an exception.
'Invalid EXIF data: buffer should start with "Exif".'
Having tested several node/js exif parsers there appears to be a bug in your implementation.
Certain Images report having an Orientation of 6 when all other readers report a value of 8. I'm not sure why this would be but I have been able to confirm that it is yours that is incorrect as transforming the resulting image based upon the your retrieved Orientation value shows an incorrectly rotated image.
Hi.
In the function parseDate, date.setUTCMonth() is called after new Date() (means current local time).
But, if the current date is 31st and the parsing value is like "2016:09:17 09:36:23",
then date.setUTCMonth('09'-1) cannot change the month to September but October since the day
has been set to 31st in the constructor. So, the parsed day is "Mon Oct 17 2016 18:36:23"
I think if the value date is initialized not new date(), but something fix day time like
new Date(2001,0,2,0,0,0,0); (Jan, 2nd 2001 00:00:00 in the local time),
this illegal value will not be occurred.
You can easily reproduce if new Date() changes to new Date(2016,11,31,23,59,59,0); for example.
Currently dates are always parsed in the local timezone, but since that changes it's unlikely to be what you'd actually want. Would you be willing to accept a PR for working with dates in UTC so it'd always serialise to the same time across timezones?
Relevant source code: https://github.com/devongovett/exif-reader/blob/master/index.js#L132-L149
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.