fpexif: Add boolean property WriteJFIFandEXIF which forces writing JFIF and EXIF segments if both exist. (https://forum.lazarus.freepascal.org/index.php/topic,49648.0.html).

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@7427 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2020-05-03 20:32:43 +00:00
parent 4430a14489
commit 7e70201620

View File

@ -57,8 +57,9 @@ type
FImgHeight: Integer; FImgHeight: Integer;
FWarnings: TStrings; FWarnings: TStrings;
FMetadataKinds: TMetadataKinds; FMetadataKinds: TMetadataKinds;
FHeaderSegment: TBytes; FJFIFSegment: TBytes;
FJFXXThumbnail: TBytes; FJFXXThumbnail: TBytes;
FWriteJFIFandEXIF: Boolean;
FComment: String; FComment: String;
private private
FExifData: TExifData; FExifData: TExifData;
@ -110,6 +111,8 @@ type
property MetadataKinds: TMetadataKinds read FMetadataKinds write FMetadataKinds default mdkAll; property MetadataKinds: TMetadataKinds read FMetadataKinds write FMetadataKinds default mdkAll;
{ Warning message - NOTE: Reading of warnings is erasing the warnings list! } { Warning message - NOTE: Reading of warnings is erasing the warnings list! }
property Warnings: String read GetWarnings; property Warnings: String read GetWarnings;
{ Write both JFIF and EXIF data if available. Normally they are mutually exclusive. }
property WriteJFIFandEXIF: Boolean read FWriteJFIFandEXIF write FWriteJFIFandEXIF;
property ExifData: TExifData read FExifData; property ExifData: TExifData read FExifData;
property IptcData: TIptcData read FIptcData; // to do: rename to IptcData property IptcData: TIptcData read FIptcData; // to do: rename to IptcData
@ -394,16 +397,21 @@ type
Marker: byte; Marker: byte;
Size: Word; Size: Word;
end; end;
const
SOI_MARKER: array[0..1] of byte = ($FF, $D8);
var var
header: TSegmentHeader; header: TSegmentHeader;
n, count: Int64; n, count: Int64;
savedPos: Int64; savedPos: Int64;
jfif: TJpegJFIFSegment;
begin begin
// Write the header segment and all metadata segments stored in TImgInfo // Write the header segment and all metadata segments stored in TImgInfo
// to the beginning of the stream // to the beginning of the stream
AOutputStream.Position := 0; AOutputStream.Position := 0;
WriteJpeg(AOutputStream); WriteJpeg(AOutputStream);
AInputStream.Position := 0;
// Now write copy all other segments. // Now write copy all other segments.
AInputStream.Position := 0; AInputStream.Position := 0;
while AInputStream.Position < AInputStream.Size do begin while AInputStream.Position < AInputStream.Size do begin
@ -539,8 +547,8 @@ begin
if CompareMem(@Identifier[0], @sJFIF[1], Length(sJFIF)) then if CompareMem(@Identifier[0], @sJFIF[1], Length(sJFIF)) then
begin begin
// JFIF APP0 marker segment // JFIF APP0 marker segment
SetLength(FHeaderSegment, size); SetLength(FJFIFSegment, size);
Move(hdr[0], FHeaderSegment[0], size); Move(hdr[0], FJFIFSegment[0], size);
if (JFIFVersion[0] <> 1) then if (JFIFVersion[0] <> 1) then
exit; exit;
end else end else
@ -680,7 +688,7 @@ const
SOI_MARKER: array[0..1] of byte = ($FF, $D8); SOI_MARKER: array[0..1] of byte = ($FF, $D8);
COM_MARKER: array[0..1] of byte = ($FF, $FE); COM_MARKER: array[0..1] of byte = ($FF, $FE);
JFIF_MARKER: array[0..1] of byte = ($FF, $E0); JFIF_MARKER: array[0..1] of byte = ($FF, $E0);
JFIF: ansistring = 'JFIF'#0; JFIF_ID: ansistring = 'JFIF'#0;
var var
jfifSegment: TJpegJFIFSegment; jfifSegment: TJpegJFIFSegment;
writer: TBasicMetadataWriter; writer: TBasicMetadataWriter;
@ -691,29 +699,37 @@ begin
// Write Start-of-image segment (SOI) // Write Start-of-image segment (SOI)
AStream.WriteBuffer(SOI_MARKER, SizeOf(SOI_MARKER)); AStream.WriteBuffer(SOI_MARKER, SizeOf(SOI_MARKER));
// No Exif --> write an APP0 segment // No EXIF or JFIF requested: write APP0 segment
if not HasExif or (FMetadataKinds * [mdkExif, mdkExifNoMakerNotes] = []) then begin if (not HasExif) or
if Length(FHeaderSegment) = 0 then begin (FMetaDataKinds * [mdkExif, mdkExifNoMakerNotes] = []) or
Move(JFIF[1], {%H-}JFIFSegment.Identifier[0], Length(JFIF)); FWriteJFIFandEXIF then
JFIFSegment.JFIFVersion[0] := 1; begin
JFIFSegment.JFIFVersion[1] := 2; // No Exif, no JFIF --> write a default APP0 segment
JFIFSegment.DensityUnit := 1; // inch if Length(FJFIFSegment) = 0 then
JFIFSegment.XDensity := NtoBE(72); // 72 ppi begin
JFIFSegment.YDensity := NtoBE(72); Move(JFIF_ID[1], {%H-}jfifSegment.Identifier[0], Length(JFIF_ID));
JFIFSegment.ThumbnailWidth := 0; // no thumbnail in APP0 segment jfifSegment.JFIFVersion[0] := 1;
JFIFSegment.ThumbnailHeight := 0; jfifSegment.JFIFVersion[1] := 2;
AStream.WriteBuffer(JFIF_MARKER, SizeOf(JFIF_MARKER)); jfifSegment.DensityUnit := 1; // inch
WriteWord(AStream, NtoBE(Word(SizeOf(JFIFSegment) + 2))); jfifSegment.XDensity := NtoBE(72); // 72 ppi
AStream.WriteBuffer(JFIFSegment, SizeOf(JFIFSegment)); jfifSegment.YDensity := NtoBE(72);
end else jfifSegment.ThumbnailWidth := 0; // no thumbnail in APP0 segment
begin jfifSegment.ThumbnailHeight := 0;
AStream.WriteBuffer(JFIF_MARKER, SizeOf(JFIF_MARKER)); AStream.WriteBuffer(JFIF_MARKER, SizeOf(JFIF_MARKER));
WriteWord(AStream, NtoBE(Word(Length(FHeaderSegment) + 2))); WriteWord(AStream, NtoBE(Word(SizeOf(jfifSegment) + 2)));
AStream.WriteBuffer(FHeaderSegment[0], Length(FHeaderSegment)); AStream.WriteBuffer(jfifSegment, SizeOf(jfifSegment));
end; end
end else // No Exif, but JFIF --> write the JFIF segment of the file
else begin
AStream.WriteBuffer(JFIF_MARKER, SizeOf(JFIF_MARKER));
WriteWord(AStream, NToBE(Word(LengtH(FJFIFSegment) + 2)));
AStream.WriteBuffer(FJFIFSegment[0], Length(FJFIFSegment));
end;
end;
// Exif --> Write APP1 segment
if HasExif then
begin begin
// Exif --> Write APP1 segment
writer := TExifWriter.Create(Self); writer := TExifWriter.Create(Self);
try try
TExifWriter(writer).BigEndian:= FExifData.BigEndian; TExifWriter(writer).BigEndian:= FExifData.BigEndian;