diff --git a/components/fpexif/examples/file_renamer/frmain.pas b/components/fpexif/examples/file_renamer/frmain.pas index ccbdb3035..399693499 100644 --- a/components/fpexif/examples/file_renamer/frmain.pas +++ b/components/fpexif/examples/file_renamer/frmain.pas @@ -179,17 +179,17 @@ begin imgInfo := TImgInfo.Create; try imgInfo.LoadFromFile(ShellTreeView.Path + Item.Caption); - if not (imgInfo.HasExif and imgInfo.HasThumbnail) then begin + if not (imgInfo.HasThumbnail) then begin ThumbImg.Picture.LoadFromFile(ShellTreeView.Path + Item.Caption); exit; end; ms := TMemoryStream.Create; try - imgInfo.ExifData.SaveThumbnailToStream(ms); + imgInfo.SaveThumbnailToStream(ms); if ms.Size > 0 then begin ms.Position := 0; - ThumbImg.Picture.LoadfromStream(ms); + ThumbImg.Picture.LoadFromStream(ms); end else ThumbImg.Picture.Clear; finally diff --git a/components/fpexif/examples/metadata_viewer/MetadataViewer.lpi b/components/fpexif/examples/metadata_viewer/MetadataViewer.lpi index 8d11694b5..30cc45ad1 100644 --- a/components/fpexif/examples/metadata_viewer/MetadataViewer.lpi +++ b/components/fpexif/examples/metadata_viewer/MetadataViewer.lpi @@ -66,6 +66,11 @@ + + + + + diff --git a/components/fpexif/examples/metadata_viewer/mdvmain.pas b/components/fpexif/examples/metadata_viewer/mdvmain.pas index ff375454a..435d9ee25 100644 --- a/components/fpexif/examples/metadata_viewer/mdvmain.pas +++ b/components/fpexif/examples/metadata_viewer/mdvmain.pas @@ -320,10 +320,10 @@ begin end; end; - if FImgInfo.HasThumbnail and Assigned(FImgInfo.ExifData) then begin + if FImgInfo.HasThumbnail then begin ms := TMemoryStream.Create; try - FImgInfo.ExifData.SaveThumbnailToStream(ms); + FImgInfo.SaveThumbnailToStream(ms); ms.Position := 0; PreviewImage.Picture.LoadFromStream(ms); RotateBitmap(PreviewImage.Picture.Bitmap, FImageOrientation); diff --git a/components/fpexif/examples/simple_demo/ExifSimpleDemo.lpi b/components/fpexif/examples/simple_demo/ExifSimpleDemo.lpi index 9e620f9a0..8a0da9465 100644 --- a/components/fpexif/examples/simple_demo/ExifSimpleDemo.lpi +++ b/components/fpexif/examples/simple_demo/ExifSimpleDemo.lpi @@ -1,11 +1,13 @@ - + + + + - <ResourceType Value="res"/> <UseXPManifest Value="True"/> diff --git a/components/fpexif/examples/simple_demo/sdmain.lfm b/components/fpexif/examples/simple_demo/sdmain.lfm index aefe179e7..423db2b39 100644 --- a/components/fpexif/examples/simple_demo/sdmain.lfm +++ b/components/fpexif/examples/simple_demo/sdmain.lfm @@ -130,7 +130,7 @@ object MainForm: TMainForm Left = 312 Height = 19 Top = 10 - Width = 128 + Width = 127 BorderSpacing.Left = 8 Caption = 'Truncate binary tags' Checked = True @@ -143,7 +143,7 @@ object MainForm: TMainForm AnchorSideLeft.Side = asrBottom AnchorSideTop.Control = CbVerbosity AnchorSideTop.Side = asrCenter - Left = 448 + Left = 447 Height = 19 Top = 10 Width = 123 @@ -233,7 +233,7 @@ object MainForm: TMainForm Left = 620 Height = 15 Top = 347 - Width = 19 + Width = 18 Anchors = [akLeft, akBottom] BorderSpacing.Bottom = 4 Caption = 'Tag' diff --git a/components/fpexif/examples/simple_demo/sdmain.pas b/components/fpexif/examples/simple_demo/sdmain.pas index 952c3fa4d..813882b22 100644 --- a/components/fpexif/examples/simple_demo/sdmain.pas +++ b/components/fpexif/examples/simple_demo/sdmain.pas @@ -308,12 +308,16 @@ procedure TMainForm.LoadThumbnail; var ms: TMemoryStream; begin - if (FImgInfo.ExifData = nil) or (not FImgInfo.Exifdata.HasThumbnail) then + if not FImgInfo.HasThumbnail then exit; + //if (FImgInfo.ExifData = nil) or (not FImgInfo.Exifdata.HasThumbnail) then + // exit; + ms := TMemoryStream.Create; try - FImgInfo.ExifData.SaveThumbnailToStream(ms); + FImgInfo.SaveThumbnailToStream(ms); +// FImgInfo.ExifData.SaveThumbnailToStream(ms); ms.Position := 0; Thumbnail.Picture.LoadfromStream(ms); finally diff --git a/components/fpexif/fpemetadata.pas b/components/fpexif/fpemetadata.pas index f0937fdb3..a9fd9fd80 100644 --- a/components/fpexif/fpemetadata.pas +++ b/components/fpexif/fpemetadata.pas @@ -58,6 +58,7 @@ type FWarnings: TStrings; FMetadataKinds: TMetadataKinds; FHeaderSegment: TBytes; + FJFXXThumbnail: TBytes; FComment: String; private FExifData: TExifData; @@ -80,6 +81,7 @@ type procedure LoadFromStream(AStream: TStream); procedure Save; procedure SaveToFile(const AFileName: String; AImgFile: String = ''); + procedure SaveThumbnailToStream(AStream: TStream); function CreateExifData(ABigEndian: Boolean = false): TExifData; function CreateIptcData: TIptcData; @@ -132,6 +134,13 @@ type end; PJpegJFIFSegment = ^TJpegJFIFSegment; + TJpegJFXXSegment = packed record + Identifier: packed array[0..4] of AnsiChar; // 'JFXX'#0 + ThumbnailFormat: byte; // 10: JPEG, 11: 1 byte-per-pixel palettized, 12: 3 byte-per-pixel RGB + end; + // ThumbnailData are following + PJpegJFXXSegment = ^TJpegJFXXSegment; + TJpegSOF0Segment = packed record DataPrecision: Byte; ImageHeight: Word; @@ -331,7 +340,8 @@ end; function TImgInfo.HasThumbnail: boolean; begin - Result := (FExifData <> nil) and FExifData.HasThumbnail; + Result := ((FExifData <> nil) and FExifData.HasThumbnail) + or (Length(FJFXXThumbnail) > 0); end; function TImgInfo.HasWarnings: boolean; @@ -433,6 +443,9 @@ begin end; procedure TImgInfo.ReadJpeg(AStream: TStream); +const + sJFIF: String[5] = 'JFIF'#0; + sJFXX: String[5] = 'JFXX'#0; var marker: Byte; size: Word; @@ -441,6 +454,7 @@ var buf: TBytes; reader: TBasicMetadataReader; bigEndian: Boolean; + hdr: TBytes; {$IFNDEF FPC} sa: ansistring; {$ENDIF} @@ -517,17 +531,32 @@ begin end; M_JFIF: begin - SetLength(FHeaderSegment, size); - AStream.Read(FHeaderSegment[0], size); - with PJpegJFIFSegment(@FHeaderSegment[0])^ do begin - if not ( - (Identifier[0]='J') and (Identifier[1]='F') and - (Identifier[2]='I') and (Identifier[3]='F') and - (Identifier[4]=#0) ) - then - exit; - if (JFIFVersion[0] <> 1) then - exit; + SetLength(hdr, size); + AStream.Read(hdr[0], size); + with PJpegJFIFSegment(@hdr[0])^ do begin + if CompareMem(@Identifier[0], @sJFIF[1], Length(sJFIF)) then + begin + // JFIF APP0 marker segment + SetLength(FHeaderSegment, size); + Move(hdr[0], FHeaderSegment[0], size); + if (JFIFVersion[0] <> 1) then + exit; + end else + if CompareMem(@Identifier[0], @sJFXX[1], Length(sJFXX)) then + begin + // JFXX extension APP0 marker segment (optional) + // alternative location of a thumbnail image: + // https://en.wikipedia.org/wiki/JPEG_File_Interchange_Format#JFIF_extension_APP0_marker_segment + { + // --- not supported at the moment. + SetLength(FJFXXHeaderSegment, size); + Move(hdr[0], FJFXXHeaderSegment[0], size); + } + // --- not working... not a valid jpeg structure. + + SetLength(FJFXXThumbnail, size - SizeOf(TJpegJFXXSegment)); //(AStream.Position - p)); + Move(hdr[SizeOf(TJpegJFXXSegment)], FJFXXThumbnail[0], Length(FJFXXThumbnail)); + end; end; end; M_SOF0: @@ -573,6 +602,15 @@ begin SaveToFile(FFileName); end; +procedure TImgInfo.SaveThumbnailToStream(AStream: TStream); +begin + if (FExifData <> nil) and ExifData.HasThumbnail then + FExifData.SaveThumbnailToStream(AStream) + else + if Length(FJFXXThumbnail) > 0 then + AStream.Write(FJFXXThumbnail[0], Length(FJFXXThumbnail)); +end; + procedure TImgInfo.SaveToFile(const AFileName: String; AImgFile: String = ''); var ms: TMemoryStream;