// @ts-nocheck // 源于piexifjs import { cloneDeep } from '../cloneDeep' import { isString } from '../isString' const TAGS = { 'Image': { 11: { 'name': 'ProcessingSoftware', 'type': 'Ascii' }, 254: { 'name': 'NewSubfileType', 'type': 'Long' }, 255: { 'name': 'SubfileType', 'type': 'Short' }, 256: { 'name': 'ImageWidth', 'type': 'Long' }, 257: { 'name': 'ImageLength', 'type': 'Long' }, 258: { 'name': 'BitsPerSample', 'type': 'Short' }, 259: { 'name': 'Compression', 'type': 'Short' }, 262: { 'name': 'PhotometricInterpretation', 'type': 'Short' }, 263: { 'name': 'Threshholding', 'type': 'Short' }, 264: { 'name': 'CellWidth', 'type': 'Short' }, 265: { 'name': 'CellLength', 'type': 'Short' }, 266: { 'name': 'FillOrder', 'type': 'Short' }, 269: { 'name': 'DocumentName', 'type': 'Ascii' }, 270: { 'name': 'ImageDescription', 'type': 'Ascii' }, 271: { 'name': 'Make', 'type': 'Ascii' }, 272: { 'name': 'Model', 'type': 'Ascii' }, 273: { 'name': 'StripOffsets', 'type': 'Long' }, 274: { 'name': 'Orientation', 'type': 'Short' }, 277: { 'name': 'SamplesPerPixel', 'type': 'Short' }, 278: { 'name': 'RowsPerStrip', 'type': 'Long' }, 279: { 'name': 'StripByteCounts', 'type': 'Long' }, 282: { 'name': 'XResolution', 'type': 'Rational' }, 283: { 'name': 'YResolution', 'type': 'Rational' }, 284: { 'name': 'PlanarConfiguration', 'type': 'Short' }, 290: { 'name': 'GrayResponseUnit', 'type': 'Short' }, 291: { 'name': 'GrayResponseCurve', 'type': 'Short' }, 292: { 'name': 'T4Options', 'type': 'Long' }, 293: { 'name': 'T6Options', 'type': 'Long' }, 296: { 'name': 'ResolutionUnit', 'type': 'Short' }, 301: { 'name': 'TransferFunction', 'type': 'Short' }, 305: { 'name': 'Software', 'type': 'Ascii' }, 306: { 'name': 'DateTime', 'type': 'Ascii' }, 315: { 'name': 'Artist', 'type': 'Ascii' }, 316: { 'name': 'HostComputer', 'type': 'Ascii' }, 317: { 'name': 'Predictor', 'type': 'Short' }, 318: { 'name': 'WhitePoint', 'type': 'Rational' }, 319: { 'name': 'PrimaryChromaticities', 'type': 'Rational' }, 320: { 'name': 'ColorMap', 'type': 'Short' }, 321: { 'name': 'HalftoneHints', 'type': 'Short' }, 322: { 'name': 'TileWidth', 'type': 'Short' }, 323: { 'name': 'TileLength', 'type': 'Short' }, 324: { 'name': 'TileOffsets', 'type': 'Short' }, 325: { 'name': 'TileByteCounts', 'type': 'Short' }, 330: { 'name': 'SubIFDs', 'type': 'Long' }, 332: { 'name': 'InkSet', 'type': 'Short' }, 333: { 'name': 'InkNames', 'type': 'Ascii' }, 334: { 'name': 'NumberOfInks', 'type': 'Short' }, 336: { 'name': 'DotRange', 'type': 'Byte' }, 337: { 'name': 'TargetPrinter', 'type': 'Ascii' }, 338: { 'name': 'ExtraSamples', 'type': 'Short' }, 339: { 'name': 'SampleFormat', 'type': 'Short' }, 340: { 'name': 'SMinSampleValue', 'type': 'Short' }, 341: { 'name': 'SMaxSampleValue', 'type': 'Short' }, 342: { 'name': 'TransferRange', 'type': 'Short' }, 343: { 'name': 'ClipPath', 'type': 'Byte' }, 344: { 'name': 'XClipPathUnits', 'type': 'Long' }, 345: { 'name': 'YClipPathUnits', 'type': 'Long' }, 346: { 'name': 'Indexed', 'type': 'Short' }, 347: { 'name': 'JPEGTables', 'type': 'Undefined' }, 351: { 'name': 'OPIProxy', 'type': 'Short' }, 512: { 'name': 'JPEGProc', 'type': 'Long' }, 513: { 'name': 'JPEGInterchangeFormat', 'type': 'Long' }, 514: { 'name': 'JPEGInterchangeFormatLength', 'type': 'Long' }, 515: { 'name': 'JPEGRestartInterval', 'type': 'Short' }, 517: { 'name': 'JPEGLosslessPredictors', 'type': 'Short' }, 518: { 'name': 'JPEGPointTransforms', 'type': 'Short' }, 519: { 'name': 'JPEGQTables', 'type': 'Long' }, 520: { 'name': 'JPEGDCTables', 'type': 'Long' }, 521: { 'name': 'JPEGACTables', 'type': 'Long' }, 529: { 'name': 'YCbCrCoefficients', 'type': 'Rational' }, 530: { 'name': 'YCbCrSubSampling', 'type': 'Short' }, 531: { 'name': 'YCbCrPositioning', 'type': 'Short' }, 532: { 'name': 'ReferenceBlackWhite', 'type': 'Rational' }, 700: { 'name': 'XMLPacket', 'type': 'Byte' }, 18246: { 'name': 'Rating', 'type': 'Short' }, 18249: { 'name': 'RatingPercent', 'type': 'Short' }, 32781: { 'name': 'ImageID', 'type': 'Ascii' }, 33421: { 'name': 'CFARepeatPatternDim', 'type': 'Short' }, 33422: { 'name': 'CFAPattern', 'type': 'Byte' }, 33423: { 'name': 'BatteryLevel', 'type': 'Rational' }, 33432: { 'name': 'Copyright', 'type': 'Ascii' }, 33434: { 'name': 'ExposureTime', 'type': 'Rational' }, 34377: { 'name': 'ImageResources', 'type': 'Byte' }, 34665: { 'name': 'ExifTag', 'type': 'Long' }, 34675: { 'name': 'InterColorProfile', 'type': 'Undefined' }, 34853: { 'name': 'GPSTag', 'type': 'Long' }, 34857: { 'name': 'Interlace', 'type': 'Short' }, 34858: { 'name': 'TimeZoneOffset', 'type': 'Long' }, 34859: { 'name': 'SelfTimerMode', 'type': 'Short' }, 37387: { 'name': 'FlashEnergy', 'type': 'Rational' }, 37388: { 'name': 'SpatialFrequencyResponse', 'type': 'Undefined' }, 37389: { 'name': 'Noise', 'type': 'Undefined' }, 37390: { 'name': 'FocalPlaneXResolution', 'type': 'Rational' }, 37391: { 'name': 'FocalPlaneYResolution', 'type': 'Rational' }, 37392: { 'name': 'FocalPlaneResolutionUnit', 'type': 'Short' }, 37393: { 'name': 'ImageNumber', 'type': 'Long' }, 37394: { 'name': 'SecurityClassification', 'type': 'Ascii' }, 37395: { 'name': 'ImageHistory', 'type': 'Ascii' }, 37397: { 'name': 'ExposureIndex', 'type': 'Rational' }, 37398: { 'name': 'TIFFEPStandardID', 'type': 'Byte' }, 37399: { 'name': 'SensingMethod', 'type': 'Short' }, 40091: { 'name': 'XPTitle', 'type': 'Byte' }, 40092: { 'name': 'XPComment', 'type': 'Byte' }, 40093: { 'name': 'XPAuthor', 'type': 'Byte' }, 40094: { 'name': 'XPKeywords', 'type': 'Byte' }, 40095: { 'name': 'XPSubject', 'type': 'Byte' }, 50341: { 'name': 'PrintImageMatching', 'type': 'Undefined' }, 50706: { 'name': 'DNGVersion', 'type': 'Byte' }, 50707: { 'name': 'DNGBackwardVersion', 'type': 'Byte' }, 50708: { 'name': 'UniqueCameraModel', 'type': 'Ascii' }, 50709: { 'name': 'LocalizedCameraModel', 'type': 'Byte' }, 50710: { 'name': 'CFAPlaneColor', 'type': 'Byte' }, 50711: { 'name': 'CFALayout', 'type': 'Short' }, 50712: { 'name': 'LinearizationTable', 'type': 'Short' }, 50713: { 'name': 'BlackLevelRepeatDim', 'type': 'Short' }, 50714: { 'name': 'BlackLevel', 'type': 'Rational' }, 50715: { 'name': 'BlackLevelDeltaH', 'type': 'SRational' }, 50716: { 'name': 'BlackLevelDeltaV', 'type': 'SRational' }, 50717: { 'name': 'WhiteLevel', 'type': 'Short' }, 50718: { 'name': 'DefaultScale', 'type': 'Rational' }, 50719: { 'name': 'DefaultCropOrigin', 'type': 'Short' }, 50720: { 'name': 'DefaultCropSize', 'type': 'Short' }, 50721: { 'name': 'ColorMatrix1', 'type': 'SRational' }, 50722: { 'name': 'ColorMatrix2', 'type': 'SRational' }, 50723: { 'name': 'CameraCalibration1', 'type': 'SRational' }, 50724: { 'name': 'CameraCalibration2', 'type': 'SRational' }, 50725: { 'name': 'ReductionMatrix1', 'type': 'SRational' }, 50726: { 'name': 'ReductionMatrix2', 'type': 'SRational' }, 50727: { 'name': 'AnalogBalance', 'type': 'Rational' }, 50728: { 'name': 'AsShotNeutral', 'type': 'Short' }, 50729: { 'name': 'AsShotWhiteXY', 'type': 'Rational' }, 50730: { 'name': 'BaselineExposure', 'type': 'SRational' }, 50731: { 'name': 'BaselineNoise', 'type': 'Rational' }, 50732: { 'name': 'BaselineSharpness', 'type': 'Rational' }, 50733: { 'name': 'BayerGreenSplit', 'type': 'Long' }, 50734: { 'name': 'LinearResponseLimit', 'type': 'Rational' }, 50735: { 'name': 'CameraSerialNumber', 'type': 'Ascii' }, 50736: { 'name': 'LensInfo', 'type': 'Rational' }, 50737: { 'name': 'ChromaBlurRadius', 'type': 'Rational' }, 50738: { 'name': 'AntiAliasStrength', 'type': 'Rational' }, 50739: { 'name': 'ShadowScale', 'type': 'SRational' }, 50740: { 'name': 'DNGPrivateData', 'type': 'Byte' }, 50741: { 'name': 'MakerNoteSafety', 'type': 'Short' }, 50778: { 'name': 'CalibrationIlluminant1', 'type': 'Short' }, 50779: { 'name': 'CalibrationIlluminant2', 'type': 'Short' }, 50780: { 'name': 'BestQualityScale', 'type': 'Rational' }, 50781: { 'name': 'RawDataUniqueID', 'type': 'Byte' }, 50827: { 'name': 'OriginalRawFileName', 'type': 'Byte' }, 50828: { 'name': 'OriginalRawFileData', 'type': 'Undefined' }, 50829: { 'name': 'ActiveArea', 'type': 'Short' }, 50830: { 'name': 'MaskedAreas', 'type': 'Short' }, 50831: { 'name': 'AsShotICCProfile', 'type': 'Undefined' }, 50832: { 'name': 'AsShotPreProfileMatrix', 'type': 'SRational' }, 50833: { 'name': 'CurrentICCProfile', 'type': 'Undefined' }, 50834: { 'name': 'CurrentPreProfileMatrix', 'type': 'SRational' }, 50879: { 'name': 'ColorimetricReference', 'type': 'Short' }, 50931: { 'name': 'CameraCalibrationSignature', 'type': 'Byte' }, 50932: { 'name': 'ProfileCalibrationSignature', 'type': 'Byte' }, 50934: { 'name': 'AsShotProfileName', 'type': 'Byte' }, 50935: { 'name': 'NoiseReductionApplied', 'type': 'Rational' }, 50936: { 'name': 'ProfileName', 'type': 'Byte' }, 50937: { 'name': 'ProfileHueSatMapDims', 'type': 'Long' }, 50938: { 'name': 'ProfileHueSatMapData1', 'type': 'Float' }, 50939: { 'name': 'ProfileHueSatMapData2', 'type': 'Float' }, 50940: { 'name': 'ProfileToneCurve', 'type': 'Float' }, 50941: { 'name': 'ProfileEmbedPolicy', 'type': 'Long' }, 50942: { 'name': 'ProfileCopyright', 'type': 'Byte' }, 50964: { 'name': 'ForwardMatrix1', 'type': 'SRational' }, 50965: { 'name': 'ForwardMatrix2', 'type': 'SRational' }, 50966: { 'name': 'PreviewApplicationName', 'type': 'Byte' }, 50967: { 'name': 'PreviewApplicationVersion', 'type': 'Byte' }, 50968: { 'name': 'PreviewSettingsName', 'type': 'Byte' }, 50969: { 'name': 'PreviewSettingsDigest', 'type': 'Byte' }, 50970: { 'name': 'PreviewColorSpace', 'type': 'Long' }, 50971: { 'name': 'PreviewDateTime', 'type': 'Ascii' }, 50972: { 'name': 'RawImageDigest', 'type': 'Undefined' }, 50973: { 'name': 'OriginalRawFileDigest', 'type': 'Undefined' }, 50974: { 'name': 'SubTileBlockSize', 'type': 'Long' }, 50975: { 'name': 'RowInterleaveFactor', 'type': 'Long' }, 50981: { 'name': 'ProfileLookTableDims', 'type': 'Long' }, 50982: { 'name': 'ProfileLookTableData', 'type': 'Float' }, 51008: { 'name': 'OpcodeList1', 'type': 'Undefined' }, 51009: { 'name': 'OpcodeList2', 'type': 'Undefined' }, 51022: { 'name': 'OpcodeList3', 'type': 'Undefined' } }, 'Exif': { 33434: { 'name': 'ExposureTime', 'type': 'Rational' }, 33437: { 'name': 'FNumber', 'type': 'Rational' }, 34850: { 'name': 'ExposureProgram', 'type': 'Short' }, 34852: { 'name': 'SpectralSensitivity', 'type': 'Ascii' }, 34855: { 'name': 'ISOSpeedRatings', 'type': 'Short' }, 34856: { 'name': 'OECF', 'type': 'Undefined' }, 34864: { 'name': 'SensitivityType', 'type': 'Short' }, 34865: { 'name': 'StandardOutputSensitivity', 'type': 'Long' }, 34866: { 'name': 'RecommendedExposureIndex', 'type': 'Long' }, 34867: { 'name': 'ISOSpeed', 'type': 'Long' }, 34868: { 'name': 'ISOSpeedLatitudeyyy', 'type': 'Long' }, 34869: { 'name': 'ISOSpeedLatitudezzz', 'type': 'Long' }, 36864: { 'name': 'ExifVersion', 'type': 'Undefined' }, 36867: { 'name': 'DateTimeOriginal', 'type': 'Ascii' }, 36868: { 'name': 'DateTimeDigitized', 'type': 'Ascii' }, 37121: { 'name': 'ComponentsConfiguration', 'type': 'Undefined' }, 37122: { 'name': 'CompressedBitsPerPixel', 'type': 'Rational' }, 37377: { 'name': 'ShutterSpeedValue', 'type': 'SRational' }, 37378: { 'name': 'ApertureValue', 'type': 'Rational' }, 37379: { 'name': 'BrightnessValue', 'type': 'SRational' }, 37380: { 'name': 'ExposureBiasValue', 'type': 'SRational' }, 37381: { 'name': 'MaxApertureValue', 'type': 'Rational' }, 37382: { 'name': 'SubjectDistance', 'type': 'Rational' }, 37383: { 'name': 'MeteringMode', 'type': 'Short' }, 37384: { 'name': 'LightSource', 'type': 'Short' }, 37385: { 'name': 'Flash', 'type': 'Short' }, 37386: { 'name': 'FocalLength', 'type': 'Rational' }, 37396: { 'name': 'SubjectArea', 'type': 'Short' }, 37500: { 'name': 'MakerNote', 'type': 'Undefined' }, 37510: { 'name': 'UserComment', 'type': 'Ascii' }, 37520: { 'name': 'SubSecTime', 'type': 'Ascii' }, 37521: { 'name': 'SubSecTimeOriginal', 'type': 'Ascii' }, 37522: { 'name': 'SubSecTimeDigitized', 'type': 'Ascii' }, 40960: { 'name': 'FlashpixVersion', 'type': 'Undefined' }, 40961: { 'name': 'ColorSpace', 'type': 'Short' }, 40962: { 'name': 'PixelXDimension', 'type': 'Long' }, 40963: { 'name': 'PixelYDimension', 'type': 'Long' }, 40964: { 'name': 'RelatedSoundFile', 'type': 'Ascii' }, 40965: { 'name': 'InteroperabilityTag', 'type': 'Long' }, 41483: { 'name': 'FlashEnergy', 'type': 'Rational' }, 41484: { 'name': 'SpatialFrequencyResponse', 'type': 'Undefined' }, 41486: { 'name': 'FocalPlaneXResolution', 'type': 'Rational' }, 41487: { 'name': 'FocalPlaneYResolution', 'type': 'Rational' }, 41488: { 'name': 'FocalPlaneResolutionUnit', 'type': 'Short' }, 41492: { 'name': 'SubjectLocation', 'type': 'Short' }, 41493: { 'name': 'ExposureIndex', 'type': 'Rational' }, 41495: { 'name': 'SensingMethod', 'type': 'Short' }, 41728: { 'name': 'FileSource', 'type': 'Undefined' }, 41729: { 'name': 'SceneType', 'type': 'Undefined' }, 41730: { 'name': 'CFAPattern', 'type': 'Undefined' }, 41985: { 'name': 'CustomRendered', 'type': 'Short' }, 41986: { 'name': 'ExposureMode', 'type': 'Short' }, 41987: { 'name': 'WhiteBalance', 'type': 'Short' }, 41988: { 'name': 'DigitalZoomRatio', 'type': 'Rational' }, 41989: { 'name': 'FocalLengthIn35mmFilm', 'type': 'Short' }, 41990: { 'name': 'SceneCaptureType', 'type': 'Short' }, 41991: { 'name': 'GainControl', 'type': 'Short' }, 41992: { 'name': 'Contrast', 'type': 'Short' }, 41993: { 'name': 'Saturation', 'type': 'Short' }, 41994: { 'name': 'Sharpness', 'type': 'Short' }, 41995: { 'name': 'DeviceSettingDescription', 'type': 'Undefined' }, 41996: { 'name': 'SubjectDistanceRange', 'type': 'Short' }, 42016: { 'name': 'ImageUniqueID', 'type': 'Ascii' }, 42032: { 'name': 'CameraOwnerName', 'type': 'Ascii' }, 42033: { 'name': 'BodySerialNumber', 'type': 'Ascii' }, 42034: { 'name': 'LensSpecification', 'type': 'Rational' }, 42035: { 'name': 'LensMake', 'type': 'Ascii' }, 42036: { 'name': 'LensModel', 'type': 'Ascii' }, 42037: { 'name': 'LensSerialNumber', 'type': 'Ascii' }, 42240: { 'name': 'Gamma', 'type': 'Rational' } }, 'GPS': { 0: { 'name': 'GPSVersionID', 'type': 'Byte' }, 1: { 'name': 'GPSLatitudeRef', 'type': 'Ascii' }, 2: { 'name': 'GPSLatitude', 'type': 'Rational' }, 3: { 'name': 'GPSLongitudeRef', 'type': 'Ascii' }, 4: { 'name': 'GPSLongitude', 'type': 'Rational' }, 5: { 'name': 'GPSAltitudeRef', 'type': 'Byte' }, 6: { 'name': 'GPSAltitude', 'type': 'Rational' }, 7: { 'name': 'GPSTimeStamp', 'type': 'Rational' }, 8: { 'name': 'GPSSatellites', 'type': 'Ascii' }, 9: { 'name': 'GPSStatus', 'type': 'Ascii' }, 10: { 'name': 'GPSMeasureMode', 'type': 'Ascii' }, 11: { 'name': 'GPSDOP', 'type': 'Rational' }, 12: { 'name': 'GPSSpeedRef', 'type': 'Ascii' }, 13: { 'name': 'GPSSpeed', 'type': 'Rational' }, 14: { 'name': 'GPSTrackRef', 'type': 'Ascii' }, 15: { 'name': 'GPSTrack', 'type': 'Rational' }, 16: { 'name': 'GPSImgDirectionRef', 'type': 'Ascii' }, 17: { 'name': 'GPSImgDirection', 'type': 'Rational' }, 18: { 'name': 'GPSMapDatum', 'type': 'Ascii' }, 19: { 'name': 'GPSDestLatitudeRef', 'type': 'Ascii' }, 20: { 'name': 'GPSDestLatitude', 'type': 'Rational' }, 21: { 'name': 'GPSDestLongitudeRef', 'type': 'Ascii' }, 22: { 'name': 'GPSDestLongitude', 'type': 'Rational' }, 23: { 'name': 'GPSDestBearingRef', 'type': 'Ascii' }, 24: { 'name': 'GPSDestBearing', 'type': 'Rational' }, 25: { 'name': 'GPSDestDistanceRef', 'type': 'Ascii' }, 26: { 'name': 'GPSDestDistance', 'type': 'Rational' }, 27: { 'name': 'GPSProcessingMethod', 'type': 'Undefined' }, 28: { 'name': 'GPSAreaInformation', 'type': 'Undefined' }, 29: { 'name': 'GPSDateStamp', 'type': 'Ascii' }, 30: { 'name': 'GPSDifferential', 'type': 'Short' }, 31: { 'name': 'GPSHPositioningError', 'type': 'Rational' } }, 'Interop': { 1: { 'name': 'InteroperabilityIndex', 'type': 'Ascii' } }, }; const TYPES = { "Byte": 1, "Ascii": 2, "Short": 3, "Long": 4, "Rational": 5, "Undefined": 7, "SLong": 9, "SRational": 10 }; TAGS["0th"] = TAGS["Image"]; TAGS["1st"] = TAGS["Image"]; class Piexif { version = "1.0.4" TAGS = TAGS ImageIFD = { ProcessingSoftware: 11, NewSubfileType: 254, SubfileType: 255, ImageWidth: 256, ImageLength: 257, BitsPerSample: 258, Compression: 259, PhotometricInterpretation: 262, Threshholding: 263, CellWidth: 264, CellLength: 265, FillOrder: 266, DocumentName: 269, ImageDescription: 270, Make: 271, Model: 272, StripOffsets: 273, Orientation: 274, SamplesPerPixel: 277, RowsPerStrip: 278, StripByteCounts: 279, XResolution: 282, YResolution: 283, PlanarConfiguration: 284, GrayResponseUnit: 290, GrayResponseCurve: 291, T4Options: 292, T6Options: 293, ResolutionUnit: 296, TransferFunction: 301, Software: 305, DateTime: 306, Artist: 315, HostComputer: 316, Predictor: 317, WhitePoint: 318, PrimaryChromaticities: 319, ColorMap: 320, HalftoneHints: 321, TileWidth: 322, TileLength: 323, TileOffsets: 324, TileByteCounts: 325, SubIFDs: 330, InkSet: 332, InkNames: 333, NumberOfInks: 334, DotRange: 336, TargetPrinter: 337, ExtraSamples: 338, SampleFormat: 339, SMinSampleValue: 340, SMaxSampleValue: 341, TransferRange: 342, ClipPath: 343, XClipPathUnits: 344, YClipPathUnits: 345, Indexed: 346, JPEGTables: 347, OPIProxy: 351, JPEGProc: 512, JPEGInterchangeFormat: 513, JPEGInterchangeFormatLength: 514, JPEGRestartInterval: 515, JPEGLosslessPredictors: 517, JPEGPointTransforms: 518, JPEGQTables: 519, JPEGDCTables: 520, JPEGACTables: 521, YCbCrCoefficients: 529, YCbCrSubSampling: 530, YCbCrPositioning: 531, ReferenceBlackWhite: 532, XMLPacket: 700, Rating: 18246, RatingPercent: 18249, ImageID: 32781, CFARepeatPatternDim: 33421, CFAPattern: 33422, BatteryLevel: 33423, Copyright: 33432, ExposureTime: 33434, ImageResources: 34377, ExifTag: 34665, InterColorProfile: 34675, GPSTag: 34853, Interlace: 34857, TimeZoneOffset: 34858, SelfTimerMode: 34859, FlashEnergy: 37387, SpatialFrequencyResponse: 37388, Noise: 37389, FocalPlaneXResolution: 37390, FocalPlaneYResolution: 37391, FocalPlaneResolutionUnit: 37392, ImageNumber: 37393, SecurityClassification: 37394, ImageHistory: 37395, ExposureIndex: 37397, TIFFEPStandardID: 37398, SensingMethod: 37399, XPTitle: 40091, XPComment: 40092, XPAuthor: 40093, XPKeywords: 40094, XPSubject: 40095, PrintImageMatching: 50341, DNGVersion: 50706, DNGBackwardVersion: 50707, UniqueCameraModel: 50708, LocalizedCameraModel: 50709, CFAPlaneColor: 50710, CFALayout: 50711, LinearizationTable: 50712, BlackLevelRepeatDim: 50713, BlackLevel: 50714, BlackLevelDeltaH: 50715, BlackLevelDeltaV: 50716, WhiteLevel: 50717, DefaultScale: 50718, DefaultCropOrigin: 50719, DefaultCropSize: 50720, ColorMatrix1: 50721, ColorMatrix2: 50722, CameraCalibration1: 50723, CameraCalibration2: 50724, ReductionMatrix1: 50725, ReductionMatrix2: 50726, AnalogBalance: 50727, AsShotNeutral: 50728, AsShotWhiteXY: 50729, BaselineExposure: 50730, BaselineNoise: 50731, BaselineSharpness: 50732, BayerGreenSplit: 50733, LinearResponseLimit: 50734, CameraSerialNumber: 50735, LensInfo: 50736, ChromaBlurRadius: 50737, AntiAliasStrength: 50738, ShadowScale: 50739, DNGPrivateData: 50740, MakerNoteSafety: 50741, CalibrationIlluminant1: 50778, CalibrationIlluminant2: 50779, BestQualityScale: 50780, RawDataUniqueID: 50781, OriginalRawFileName: 50827, OriginalRawFileData: 50828, ActiveArea: 50829, MaskedAreas: 50830, AsShotICCProfile: 50831, AsShotPreProfileMatrix: 50832, CurrentICCProfile: 50833, CurrentPreProfileMatrix: 50834, ColorimetricReference: 50879, CameraCalibrationSignature: 50931, ProfileCalibrationSignature: 50932, AsShotProfileName: 50934, NoiseReductionApplied: 50935, ProfileName: 50936, ProfileHueSatMapDims: 50937, ProfileHueSatMapData1: 50938, ProfileHueSatMapData2: 50939, ProfileToneCurve: 50940, ProfileEmbedPolicy: 50941, ProfileCopyright: 50942, ForwardMatrix1: 50964, ForwardMatrix2: 50965, PreviewApplicationName: 50966, PreviewApplicationVersion: 50967, PreviewSettingsName: 50968, PreviewSettingsDigest: 50969, PreviewColorSpace: 50970, PreviewDateTime: 50971, RawImageDigest: 50972, OriginalRawFileDigest: 50973, SubTileBlockSize: 50974, RowInterleaveFactor: 50975, ProfileLookTableDims: 50981, ProfileLookTableData: 50982, OpcodeList1: 51008, OpcodeList2: 51009, OpcodeList3: 51022, NoiseProfile: 51041, } ExifIFD = { ExposureTime: 33434, FNumber: 33437, ExposureProgram: 34850, SpectralSensitivity: 34852, ISOSpeedRatings: 34855, OECF: 34856, SensitivityType: 34864, StandardOutputSensitivity: 34865, RecommendedExposureIndex: 34866, ISOSpeed: 34867, ISOSpeedLatitudeyyy: 34868, ISOSpeedLatitudezzz: 34869, ExifVersion: 36864, DateTimeOriginal: 36867, DateTimeDigitized: 36868, ComponentsConfiguration: 37121, CompressedBitsPerPixel: 37122, ShutterSpeedValue: 37377, ApertureValue: 37378, BrightnessValue: 37379, ExposureBiasValue: 37380, MaxApertureValue: 37381, SubjectDistance: 37382, MeteringMode: 37383, LightSource: 37384, Flash: 37385, FocalLength: 37386, SubjectArea: 37396, MakerNote: 37500, UserComment: 37510, SubSecTime: 37520, SubSecTimeOriginal: 37521, SubSecTimeDigitized: 37522, FlashpixVersion: 40960, ColorSpace: 40961, PixelXDimension: 40962, PixelYDimension: 40963, RelatedSoundFile: 40964, InteroperabilityTag: 40965, FlashEnergy: 41483, SpatialFrequencyResponse: 41484, FocalPlaneXResolution: 41486, FocalPlaneYResolution: 41487, FocalPlaneResolutionUnit: 41488, SubjectLocation: 41492, ExposureIndex: 41493, SensingMethod: 41495, FileSource: 41728, SceneType: 41729, CFAPattern: 41730, CustomRendered: 41985, ExposureMode: 41986, WhiteBalance: 41987, DigitalZoomRatio: 41988, FocalLengthIn35mmFilm: 41989, SceneCaptureType: 41990, GainControl: 41991, Contrast: 41992, Saturation: 41993, Sharpness: 41994, DeviceSettingDescription: 41995, SubjectDistanceRange: 41996, ImageUniqueID: 42016, CameraOwnerName: 42032, BodySerialNumber: 42033, LensSpecification: 42034, LensMake: 42035, LensModel: 42036, LensSerialNumber: 42037, Gamma: 42240, } GPSIFD = { GPSVersionID: 0, GPSLatitudeRef: 1, GPSLatitude: 2, GPSLongitudeRef: 3, GPSLongitude: 4, GPSAltitudeRef: 5, GPSAltitude: 6, GPSTimeStamp: 7, GPSSatellites: 8, GPSStatus: 9, GPSMeasureMode: 10, GPSDOP: 11, GPSSpeedRef: 12, GPSSpeed: 13, GPSTrackRef: 14, GPSTrack: 15, GPSImgDirectionRef: 16, GPSImgDirection: 17, GPSMapDatum: 18, GPSDestLatitudeRef: 19, GPSDestLatitude: 20, GPSDestLongitudeRef: 21, GPSDestLongitude: 22, GPSDestBearingRef: 23, GPSDestBearing: 24, GPSDestDistanceRef: 25, GPSDestDistance: 26, GPSProcessingMethod: 27, GPSAreaInformation: 28, GPSDateStamp: 29, GPSDifferential: 30, GPSHPositioningError: 31, } InteropIFD = { InteroperabilityIndex: 1, } GPSHelper = { degToDmsRational(degFloat : number) { const degAbs = Math.abs(degFloat); const minFloat = degAbs % 1 * 60; const secFloat = minFloat % 1 * 60; const deg = Math.floor(degAbs); const min = Math.floor(minFloat); const sec = Math.round(secFloat * 100); return [ [deg, 1], [min, 1], [sec, 100] ]; }, dmsRationalToDeg(dmsArray : number[][], ref : string) { const sign = (ref === 'S' || ref === 'W') ? -1.0 : 1.0; const deg = dmsArray[0][0] / dmsArray[0][1] + dmsArray[1][0] / dmsArray[1][1] / 60.0 + dmsArray[2][0] / dmsArray[2][1] / 3600.0; return deg * sign; }, } remove(jpeg) { let b64 = false; if (jpeg.slice(0, 2) == "\xff\xd8") { } else if (jpeg.slice(0, 23) == "data:image/jpeg;base64," || jpeg .slice(0, 22) == "data:image/jpg;base64,") { jpeg = atob(jpeg.split(",")[1]); b64 = true; } else { throw new Error("Given data is not jpeg."); } const segments = splitIntoSegments(jpeg); const newSegments = segments.filter(function (seg) { return !(seg.slice(0, 2) == "\xff\xe1" && seg.slice(4, 10) == "Exif\x00\x00"); }); let new_data = newSegments.join(""); if (b64) { new_data = "data:image/jpeg;base64," + btoa(new_data); } return new_data; } insert(exif, jpeg) { let b64 = false; if (exif.slice(0, 6) != "\x45\x78\x69\x66\x00\x00") { throw new Error("Given data is not exif."); } if (jpeg.slice(0, 2) == "\xff\xd8") { } else if (jpeg.slice(0, 23) == "data:image/jpeg;base64," || jpeg .slice(0, 22) == "data:image/jpg;base64,") { jpeg = atob(jpeg.split(",")[1]); b64 = true; } else { throw new Error("Given data is not jpeg."); } const exifStr = "\xff\xe1" + pack(">H", [exif.length + 2]) + exif; const segments = splitIntoSegments(jpeg); let new_data = mergeSegments(segments, exifStr); if (b64) { new_data = "data:image/jpeg;base64," + btoa(new_data); } return new_data; } load(data) { let input_data; if (isString(data)) { if (data.slice(0, 2) == "\xff\xd8") { input_data = data; } else if (data.slice(0, 23) == "data:image/jpeg;base64," || data.slice(0, 22) == "data:image/jpg;base64,") { input_data = atob(data.split(",")[1]); } else if (data.slice(0, 4) == "Exif") { input_data = data.slice(6); } else { throw new Error("'load' gots invalid file data."); } } else { throw new Error("'load' gots invalid type argument."); } let exifDict = {}; let exif_dict = { "0th": {}, "Exif": {}, "GPS": {}, "Interop": {}, "1st": {}, "thumbnail": null }; const exifReader = new ExifReader(input_data); if (exifReader.tiftag === null) { return exif_dict; } if (exifReader.tiftag.slice(0, 2) == "\x49\x49") { exifReader.endian_mark = "<"; } else { exifReader.endian_mark = ">"; } let pointer = unpack(exifReader.endian_mark + "L", exifReader.tiftag.slice(4, 8))[0]; exif_dict["0th"] = exifReader.get_ifd(pointer, "0th"); const first_ifd_pointer = exif_dict["0th"]["first_ifd_pointer"]; delete exif_dict["0th"]["first_ifd_pointer"]; if (34665 in exif_dict["0th"]) { pointer = exif_dict["0th"][34665]; exif_dict["Exif"] = exifReader.get_ifd(pointer, "Exif"); } if (34853 in exif_dict["0th"]) { pointer = exif_dict["0th"][34853]; exif_dict["GPS"] = exifReader.get_ifd(pointer, "GPS"); } if (40965 in exif_dict["Exif"]) { pointer = exif_dict["Exif"][40965]; exif_dict["Interop"] = exifReader.get_ifd(pointer, "Interop"); } if (first_ifd_pointer != "\x00\x00\x00\x00") { pointer = unpack(exifReader.endian_mark + "L", first_ifd_pointer)[0]; exif_dict["1st"] = exifReader.get_ifd(pointer, "1st"); if ((513 in exif_dict["1st"]) && (514 in exif_dict["1st"])) { var end = exif_dict["1st"][513] + exif_dict["1st"][514]; var thumb = exifReader.tiftag.slice(exif_dict["1st"][513], end); exif_dict["thumbnail"] = thumb; } } return exif_dict; } dump(exif_dict_original) { const TIFF_HEADER_LENGTH = 8; const exif_dict : any = cloneDeep(exif_dict_original); const header = "Exif\x00\x00\x4d\x4d\x00\x2a\x00\x00\x00\x08"; let exif_is = 0//false; let gps_is = 0 //false; let interop_is = 0//false; let first_is = false; let zeroth_ifd, exif_ifd, interop_ifd, gps_ifd, first_ifd; if ("0th" in exif_dict) { zeroth_ifd = exif_dict["0th"]; } else { zeroth_ifd = {}; } if ((("Exif" in exif_dict) && (Object.keys(exif_dict["Exif"]).length)) || (("Interop" in exif_dict) && (Object.keys(exif_dict["Interop"]).length))) { zeroth_ifd[34665] = 1; exif_is = 1//true; exif_ifd = exif_dict["Exif"]; if (("Interop" in exif_dict) && Object.keys(exif_dict["Interop"]).length) { exif_ifd[40965] = 1; interop_is = 1//true; interop_ifd = exif_dict["Interop"]; } else if (Object.keys(exif_ifd).indexOf(this.ExifIFD.InteroperabilityTag.toString()) > -1) { delete exif_ifd[40965]; } } else if (Object.keys(zeroth_ifd).indexOf(this.ImageIFD.ExifTag.toString()) > -1) { delete zeroth_ifd[34665]; } if (("GPS" in exif_dict) && (Object.keys(exif_dict["GPS"]).length)) { zeroth_ifd[this.ImageIFD.GPSTag] = 1; gps_is = 1 //true; gps_ifd = exif_dict["GPS"]; } else if (Object.keys(zeroth_ifd).indexOf(this.ImageIFD.GPSTag.toString()) > -1) { delete zeroth_ifd[this.ImageIFD.GPSTag]; } if (("1st" in exif_dict) && ("thumbnail" in exif_dict) && (exif_dict["thumbnail"] != null)) { first_is = true; exif_dict["1st"][513] = 1; exif_dict["1st"][514] = 1; first_ifd = exif_dict["1st"]; } const zeroth_set = _dict_to_bytes(zeroth_ifd, "0th", 0); const zeroth_length = (zeroth_set[0].length + exif_is * 12 + gps_is * 12 + 4 + zeroth_set[1].length); let exif_set, exif_bytes = "", exif_length = 0, gps_set, gps_bytes = "", gps_length = 0, interop_set, interop_bytes = "", interop_length = 0, first_set, first_bytes = "", thumbnail; if (exif_is) { exif_set = _dict_to_bytes(exif_ifd, "Exif", zeroth_length); exif_length = exif_set[0].length + interop_is * 12 + exif_set[1].length; } if (gps_is) { gps_set = _dict_to_bytes(gps_ifd, "GPS", zeroth_length + exif_length); gps_bytes = gps_set.join(""); gps_length = gps_bytes.length; } if (interop_is) { const offset = zeroth_length + exif_length + gps_length; interop_set = _dict_to_bytes(interop_ifd, "Interop", offset); interop_bytes = interop_set.join(""); interop_length = interop_bytes.length; } if (first_is) { const offset = zeroth_length + exif_length + gps_length + interop_length; first_set = _dict_to_bytes(first_ifd, "1st", offset); thumbnail = _get_thumbnail(exif_dict["thumbnail"]); if (thumbnail.length > 64000) { throw new Error("Given thumbnail is too large. max 64kB"); } } let exif_pointer = "", gps_pointer = "", interop_pointer = "", first_ifd_pointer = "\x00\x00\x00\x00"; if (exif_is) { const pointer_value = TIFF_HEADER_LENGTH + zeroth_length; const pointer_str = pack(">L", [pointer_value]); const key = 34665; const key_str = pack(">H", [key]); const type_str = pack(">H", [TYPES["Long"]]); const length_str = pack(">L", [1]); exif_pointer = key_str + type_str + length_str + pointer_str; } if (gps_is) { const pointer_value = TIFF_HEADER_LENGTH + zeroth_length + exif_length; const pointer_str = pack(">L", [pointer_value]); const key = 34853; const key_str = pack(">H", [key]); const type_str = pack(">H", [TYPES["Long"]]); const length_str = pack(">L", [1]); gps_pointer = key_str + type_str + length_str + pointer_str; } if (interop_is) { const pointer_value = (TIFF_HEADER_LENGTH + zeroth_length + exif_length + gps_length); const pointer_str = pack(">L", [pointer_value]); const key = 40965; const key_str = pack(">H", [key]); const type_str = pack(">H", [TYPES["Long"]]); const length_str = pack(">L", [1]); interop_pointer = key_str + type_str + length_str + pointer_str; } if (first_is) { const pointer_value = (TIFF_HEADER_LENGTH + zeroth_length + exif_length + gps_length + interop_length); first_ifd_pointer = pack(">L", [pointer_value]); const thumbnail_pointer = (pointer_value + first_set[0].length + 24 + 4 + first_set[1].length); const thumbnail_p_bytes = ("\x02\x01\x00\x04\x00\x00\x00\x01" + pack(">L", [thumbnail_pointer])); const thumbnail_length_bytes = ("\x02\x02\x00\x04\x00\x00\x00\x01" + pack(">L", [thumbnail.length])); first_bytes = (first_set[0] + thumbnail_p_bytes + thumbnail_length_bytes + "\x00\x00\x00\x00" + first_set[1] + thumbnail); } const zeroth_bytes = (zeroth_set[0] + exif_pointer + gps_pointer + first_ifd_pointer + zeroth_set[1]); if (exif_is) { exif_bytes = exif_set[0] + interop_pointer + exif_set[1]; } return (header + zeroth_bytes + exif_bytes + gps_bytes + interop_bytes + first_bytes); } } function _get_thumbnail(jpeg) { let segments = splitIntoSegments(jpeg); while (("\xff\xe0" <= segments[1].slice(0, 2)) && (segments[1].slice(0, 2) <= "\xff\xef")) { segments = [segments[0]].concat(segments.slice(2)); } return segments.join(""); } function _pack_byte(array) { return pack(">" + nStr("B", array.length), array); } function _pack_short(array) { return pack(">" + nStr("H", array.length), array); } function _pack_long(array) { return pack(">" + nStr("L", array.length), array); } function _value_to_bytes(raw_value, value_type, offset) { let four_bytes_over = ""; let value_str = ""; let length, new_value, num, den; if (value_type == "Byte") { length = raw_value.length; if (length <= 4) { value_str = (_pack_byte(raw_value) + nStr("\x00", 4 - length)); } else { value_str = pack(">L", [offset]); four_bytes_over = _pack_byte(raw_value); } } else if (value_type == "Short") { length = raw_value.length; if (length <= 2) { value_str = (_pack_short(raw_value) + nStr("\x00\x00", 2 - length)); } else { value_str = pack(">L", [offset]); four_bytes_over = _pack_short(raw_value); } } else if (value_type == "Long") { length = raw_value.length; if (length <= 1) { value_str = _pack_long(raw_value); } else { value_str = pack(">L", [offset]); four_bytes_over = _pack_long(raw_value); } } else if (value_type == "Ascii") { new_value = raw_value + "\x00"; length = new_value.length; if (length > 4) { value_str = pack(">L", [offset]); four_bytes_over = new_value; } else { value_str = new_value + nStr("\x00", 4 - length); } } else if (value_type == "Rational") { if (typeof (raw_value[0]) == "number") { length = 1; num = raw_value[0]; den = raw_value[1]; new_value = pack(">L", [num]) + pack(">L", [den]); } else { length = raw_value.length; new_value = ""; for (var n = 0; n < length; n++) { num = raw_value[n][0]; den = raw_value[n][1]; new_value += (pack(">L", [num]) + pack(">L", [den])); } } value_str = pack(">L", [offset]); four_bytes_over = new_value; } else if (value_type == "SRational") { if (typeof (raw_value[0]) == "number") { length = 1; num = raw_value[0]; den = raw_value[1]; new_value = pack(">l", [num]) + pack(">l", [den]); } else { length = raw_value.length; new_value = ""; for (var n = 0; n < length; n++) { num = raw_value[n][0]; den = raw_value[n][1]; new_value += (pack(">l", [num]) + pack(">l", [den])); } } value_str = pack(">L", [offset]); four_bytes_over = new_value; } else if (value_type == "Undefined") { length = raw_value.length; if (length > 4) { value_str = pack(">L", [offset]); four_bytes_over = raw_value; } else { value_str = raw_value + nStr("\x00", 4 - length); } } var length_str = pack(">L", [length]); return [length_str, value_str, four_bytes_over]; } function _dict_to_bytes(ifd_dict, ifd, ifd_offset) { const TIFF_HEADER_LENGTH = 8; const tag_count = Object.keys(ifd_dict).length; const entry_header = pack(">H", [tag_count]); let entries_length; if (["0th", "1st"].indexOf(ifd) > -1) { entries_length = 2 + tag_count * 12 + 4; } else { entries_length = 2 + tag_count * 12; } let entries = ""; let values = ""; let key; for (key in ifd_dict) { if (typeof (key) == "string") { key = parseInt(key); } if ((ifd == "0th") && ([34665, 34853].indexOf(key) > -1)) { continue; } else if ((ifd == "Exif") && (key == 40965)) { continue; } else if ((ifd == "1st") && ([513, 514].indexOf(key) > -1)) { continue; } var raw_value = ifd_dict[key]; var key_str = pack(">H", [key]); var value_type = TAGS[ifd][key]["type"]; var type_str = pack(">H", [TYPES[value_type]]); if (typeof (raw_value) == "number") { raw_value = [raw_value]; } var offset = TIFF_HEADER_LENGTH + entries_length + ifd_offset + values.length; var b = _value_to_bytes(raw_value, value_type, offset); var length_str = b[0]; var value_str = b[1]; var four_bytes_over = b[2]; entries += key_str + type_str + length_str + value_str; values += four_bytes_over; } return [entry_header + entries, values]; } class ExifReader { tiftag = null endian_mark = '' constructor(data) { let segments, app1; if (data.slice(0, 2) == "\xff\xd8") { // JPEG segments = splitIntoSegments(data); app1 = getExifSeg(segments); if (app1) { this.tiftag = app1.slice(10); } } else if (["\x49\x49", "\x4d\x4d"].indexOf(data.slice(0, 2)) > -1) { // TIFF this.tiftag = data; } else if (data.slice(0, 4) == "Exif") { // Exif this.tiftag = data.slice(6); } else { throw new Error("Given file is neither JPEG nor TIFF."); } } get_ifd(pointer, ifd_name) { let ifd_dict = {}; let tag_count = unpack(this.endian_mark + "H", this.tiftag.slice(pointer, pointer + 2))[0]; const offset = pointer + 2; let t; if (["0th", "1st"].indexOf(ifd_name) > -1) { t = "Image"; } else { t = ifd_name; } for (let x = 0; x < tag_count; x++) { pointer = offset + 12 * x; const tag = unpack(this.endian_mark + "H", this.tiftag.slice(pointer, pointer + 2))[0]; const value_type = unpack(this.endian_mark + "H", this.tiftag.slice(pointer + 2, pointer + 4))[0]; const value_num = unpack(this.endian_mark + "L", this.tiftag.slice(pointer + 4, pointer + 8))[0]; const value = this.tiftag.slice(pointer + 8, pointer + 12); const v_set = [value_type, value_num, value]; if (tag in TAGS[t]) { ifd_dict[tag] = this.convert_value(v_set); } } if (ifd_name == "0th") { pointer = offset + 12 * tag_count; ifd_dict["first_ifd_pointer"] = this.tiftag.slice(pointer, pointer + 4); } return ifd_dict; } convert_value(val) { let data = null; const t = val[0]; const length = val[1]; const value = val[2]; let pointer; if (t == 1) { // BYTE if (length > 4) { pointer = unpack(this.endian_mark + "L", value)[0]; data = unpack(this.endian_mark + nStr("B", length), this.tiftag.slice(pointer, pointer + length)); } else { data = unpack(this.endian_mark + nStr("B", length), value.slice(0, length)); } } else if (t == 2) { // ASCII if (length > 4) { pointer = unpack(this.endian_mark + "L", value)[0]; data = this.tiftag.slice(pointer, pointer + length - 1); } else { data = value.slice(0, length - 1); } } else if (t == 3) { // SHORT if (length > 2) { pointer = unpack(this.endian_mark + "L", value)[0]; data = unpack(this.endian_mark + nStr("H", length), this.tiftag.slice(pointer, pointer + length * 2)); } else { data = unpack(this.endian_mark + nStr("H", length), value.slice(0, length * 2)); } } else if (t == 4) { // LONG if (length > 1) { pointer = unpack(this.endian_mark + "L", value)[0]; data = unpack(this.endian_mark + nStr("L", length), this.tiftag.slice(pointer, pointer + length * 4)); } else { data = unpack(this.endian_mark + nStr("L", length), value); } } else if (t == 5) { // RATIONAL pointer = unpack(this.endian_mark + "L", value)[0]; if (length > 1) { data = []; for (let x = 0; x < length; x++) { data.push([unpack(this.endian_mark + "L", this.tiftag.slice(pointer + x * 8, pointer + 4 + x * 8))[0], unpack(this.endian_mark + "L", this.tiftag.slice(pointer + 4 + x * 8, pointer + 8 + x * 8))[0] ]); } } else { data = [unpack(this.endian_mark + "L", this.tiftag.slice(pointer, pointer + 4))[0], unpack(this.endian_mark + "L", this.tiftag.slice(pointer + 4, pointer + 8))[0] ]; } } else if (t == 7) { // UNDEFINED BYTES if (length > 4) { pointer = unpack(this.endian_mark + "L", value)[0]; data = this.tiftag.slice(pointer, pointer + length); } else { data = value.slice(0, length); } } else if (t == 9) { // SLONG if (length > 1) { pointer = unpack(this.endian_mark + "L", value)[0]; data = unpack(this.endian_mark + nStr("l", length), this.tiftag.slice(pointer, pointer + length * 4)); } else { data = unpack(this.endian_mark + nStr("l", length), value); } } else if (t == 10) { // SRATIONAL pointer = unpack(this.endian_mark + "L", value)[0]; if (length > 1) { data = []; for (let x = 0; x < length; x++) { data.push([unpack(this.endian_mark + "l", this.tiftag.slice(pointer + x * 8, pointer + 4 + x * 8))[0], unpack(this.endian_mark + "l", this.tiftag.slice(pointer + 4 + x * 8, pointer + 8 + x * 8))[0] ]); } } else { data = [unpack(this.endian_mark + "l", this.tiftag.slice(pointer, pointer + 4))[0], unpack(this.endian_mark + "l", this.tiftag.slice(pointer + 4, pointer + 8))[0] ]; } } else { throw new Error("Exif might be wrong. Got incorrect value " + "type to decode. type:" + t); } if ((data instanceof Array) && (data.length == 1)) { return data[0]; } else { return data; } } } function pack(mark, array) { if (!(array instanceof Array)) { throw new Error("'pack' error. Got invalid type argument."); } if ((mark.length - 1) != array.length) { throw new Error("'pack' error. " + (mark.length - 1) + " marks, " + array.length + " elements."); } let littleEndian; if (mark[0] == "<") { littleEndian = true; } else if (mark[0] == ">") { littleEndian = false; } else { throw new Error(""); } let packed = ""; let p = 1; let val = null; let c = null; let valStr = null; while (c = mark[p]) { if (c.toLowerCase() == "b") { val = array[p - 1]; if ((c == "b") && (val < 0)) { val += 0x100; } if ((val > 0xff) || (val < 0)) { throw new Error("'pack' error."); } else { valStr = String.fromCharCode(val); } } else if (c == "H") { val = array[p - 1]; if ((val > 0xffff) || (val < 0)) { throw new Error("'pack' error."); } else { valStr = String.fromCharCode(Math.floor((val % 0x10000) / 0x100)) + String.fromCharCode(val % 0x100); if (littleEndian) { valStr = valStr.split("").reverse().join(""); } } } else if (c.toLowerCase() == "l") { val = array[p - 1]; if ((c == "l") && (val < 0)) { val += 0x100000000; } if ((val > 0xffffffff) || (val < 0)) { throw new Error("'pack' error."); } else { valStr = String.fromCharCode(Math.floor(val / 0x1000000)) + String.fromCharCode(Math.floor((val % 0x1000000) / 0x10000)) + String.fromCharCode(Math.floor((val % 0x10000) / 0x100)) + String.fromCharCode(val % 0x100); if (littleEndian) { valStr = valStr.split("").reverse().join(""); } } } else { throw new Error("'pack' error."); } packed += valStr; p += 1; } return packed; } function unpack(mark, str) { if (typeof (str) != "string") { throw new Error("'unpack' error. Got invalid type argument."); } let l = 0; for (let markPointer = 1; markPointer < mark.length; markPointer++) { if (mark[markPointer].toLowerCase() == "b") { l += 1; } else if (mark[markPointer].toLowerCase() == "h") { l += 2; } else if (mark[markPointer].toLowerCase() == "l") { l += 4; } else { throw new Error("'unpack' error. Got invalid mark."); } } if (l != str.length) { throw new Error("'unpack' error. Mismatch between symbol and string length. " + l + ":" + str.length); } let littleEndian; if (mark[0] == "<") { littleEndian = true; } else if (mark[0] == ">") { littleEndian = false; } else { throw new Error("'unpack' error."); } let unpacked = []; let strPointer = 0; let p = 1; let val = null; let c = null; let length = null; let sliced = ""; while (c = mark[p]) { if (c.toLowerCase() == "b") { length = 1; sliced = str.slice(strPointer, strPointer + length); val = sliced.charCodeAt(0); if ((c == "b") && (val >= 0x80)) { val -= 0x100; } } else if (c == "H") { length = 2; sliced = str.slice(strPointer, strPointer + length); if (littleEndian) { sliced = sliced.split("").reverse().join(""); } val = sliced.charCodeAt(0) * 0x100 + sliced.charCodeAt(1); } else if (c.toLowerCase() == "l") { length = 4; sliced = str.slice(strPointer, strPointer + length); if (littleEndian) { sliced = sliced.split("").reverse().join(""); } val = sliced.charCodeAt(0) * 0x1000000 + sliced.charCodeAt(1) * 0x10000 + sliced.charCodeAt(2) * 0x100 + sliced.charCodeAt(3); if ((c == "l") && (val >= 0x80000000)) { val -= 0x100000000; } } else { throw new Error("'unpack' error. " + c); } unpacked.push(val); strPointer += length; p += 1; } return unpacked; } function nStr(ch, num) { let str = ""; for (let i = 0; i < num; i++) { str += ch; } return str; } function splitIntoSegments(data) { if (data.slice(0, 2) != "\xff\xd8") { throw new Error("Given data isn't JPEG."); } let head = 2; const segments = ["\xff\xd8"]; while (true) { if (data.slice(head, head + 2) == "\xff\xda") { segments.push(data.slice(head)); break; } else { var length = unpack(">H", data.slice(head + 2, head + 4))[0]; var endPoint = head + length + 2; segments.push(data.slice(head, endPoint)); head = endPoint; } if (head >= data.length) { throw new Error("Wrong JPEG data."); } } return segments; } function getExifSeg(segments) { let seg; for (let i = 0; i < segments.length; i++) { seg = segments[i]; if (seg.slice(0, 2) == "\xff\xe1" && seg.slice(4, 10) == "Exif\x00\x00") { return seg; } } return null; } function mergeSegments(segments, exif) { let hasExifSegment = false; let additionalAPP1ExifSegments = []; segments.forEach(function (segment, i) { // Replace first occurence of APP1:Exif segment if (segment.slice(0, 2) == "\xff\xe1" && segment.slice(4, 10) == "Exif\x00\x00" ) { if (!hasExifSegment) { segments[i] = exif; hasExifSegment = true; } else { additionalAPP1ExifSegments.unshift(i); } } }); // Remove additional occurences of APP1:Exif segment additionalAPP1ExifSegments.forEach(function (segmentIndex) { segments.splice(segmentIndex, 1); }); if (!hasExifSegment && exif) { segments = [segments[0], exif].concat(segments.slice(1)); } return segments.join(""); }