(* ' ************************************************************************* ' * Script name: ' * Edit Lens Info ' * Version: 1.2 ' * ' * Description: ' * Allows the user to update the lens metadata for selected thumbnails ' * ' * Version History: ' * Hert van Zwietering; ' * 1.0 Initial version ' * 1.1 The script now also write Edif Tag 0xA434 (LensModel) ' * 1.2 Added progress ' * ' *************************************************************************/ *) var AFNumberBox: TvxTextBox; AFocalLengthBox: TvxTextBox; ALensInfoBox: TvxTextBox; var gFNumber: Double; gFocalLength: Double; gLensInfo: WideString; function IsRational(AVal: WideString): Boolean; var ALen, i: Integer; begin // a rational exists of numbers or a single slash (e.g. 2800/1000)... // NOTE: we'll also accept spaces because some tools write it (falsely) as 2800 / 1000 ALen := Length(AVal); result := False; if ALen = 0 then exit; for i := 1 to ALen do begin if (AVal[i] <> '0') and (AVal[i] <> '1') and (AVal[i] <> '2') and (AVal[i] <> '3') and (AVal[i] <> '4') and (AVal[i] <> '5') and (AVal[i] <> '6') and (AVal[i] <> '7') and (AVal[i] <> '8') and (AVal[i] <> '9') and (AVal[i] <> '-') and (AVal[i] <> '/') and (AVal[i] <> ' ') then exit; end; result := True; end; function XMPRationalStringToDouble(AVal: WideString): Double; var ANumStr: WideString; begin if IsRational(Trim(AVal)) then begin ANumStr := FormatRationalToDecimal(AVal, '%d' + WideString(DecimalSeparator) + '%s', 10000000); try result := StrToFloat(ANumStr); except result := 0 end; end else result := 0; end; function DoubleToXMPRationalString(AVal: Double): WideString; const cFactor = 1000000; begin result := IntToStr(Trunc(AVal * cFactor)) + '/' + IntToStr(cFactor); end; procedure HandleImage(AImage: TImageItem); var ATif: TTif; ATag: TTifTag; AXmp: TXMP; AFNumber: TMacroParam; AFLength: TMacroParam; ALensModel: TMacroParam; ACanUpdateExif, ASaveTif: Boolean; begin ASaveTif := False; ACanUpdateExif := AImage.CanUpdateExif; AXmp := TXMP.Create(False); ATif := TTif.Create(nil); try if ACanUpdateExif then begin // load the exif ATif.FileName := AImage.ExifFileName; ATif.Load(True, False); end; PublicCatalog.LoadXMPForImage(AImage, AXmp, PublicOptions.CachedXMP); AFNumber := AXmp.QuickSetProperty('http://ns.adobe.com/exif/1.0/', 'exif:FNumber', DoubleToXMPRationalString(gFNumber)); if Assigned(AFNumber) then begin if ACanUpdateExif then begin ATag := ATif.ExifIFD.FindTag($829D); // FNumber if Assigned(ATag) then begin ATag.Value := AFNumber.StrContent; ASaveTif := True; end; end; end; AFLength := AXmp.QuickSetProperty('http://ns.adobe.com/exif/1.0/', 'exif:FocalLength', DoubleToXMPRationalString(gFocalLength)); if Assigned(AFLength) then begin if ACanUpdateExif then begin ATag := ATif.ExifIFD.FindTag($920A); // FocalLength if Assigned(ATag) then begin ATag.Value := AFLength.StrContent; ASaveTif := True; end; end; end; ALensmodel := AXmp.QuickSetProperty('http://ns.adobe.com/exif/1.0/aux/', 'aux:Lens', gLensInfo); AXmp.QuickSetProperty('http://cipa.jp/exif/1.0/', 'exifEX:LensModel', gLensInfo); if Assigned(ALensModel) then begin if ACanUpdateExif then begin ATag := ATif.ExifIFD.FindTag($A434); // LensModel if Assigned(ATag) then begin ATag.Value := ALensModel.StrContent; ASaveTif := True; end; end; end; PublicCatalog.SaveXMPForImage(AImage, AXmp, PublicOptions.CachedXMP); if ASaveTif then ATif.Save; except ATif.Free; AXmp.Free; end; end; function CreateForm: TvxSceneForm; const cSpacing = 20; var ARoot: TvxVisualObject; ALabel: TvxLabel; ATextBox: TvxTextBox; begin result := TvxSceneForm.Create(nil); result.Caption := UTF8Encode('Edit Lens Info'); result.Position := poScreenCenter; result.Height := 200 * result.DPIFactor; ARoot := result.Root; ALabel := TvxLabel.Create(ARoot); ALabel.Text := 'F-Number'; ALabel.TextAlign := vgTextAlignNear; ALabel.Padding.Left := 10; ALabel.Padding.Top := cSpacing; ALabel.Align := vaTop; ARoot.AddObject(ALabel); ATextBox := TvxTextBox.Create(ARoot) ATextBox.Padding.Left := 13; ATextBox.Padding.Right := 240; ATextBox.Padding.Top := 3; ATextBox.Align := vaTop; ATextBox.Text := ''; ARoot.AddObject(ATextBox); AFNumberBox := ATextBox; ALabel := TvxLabel.Create(ARoot); ALabel.Text := 'Focal Length (mm)'; ALabel.TextAlign := vgTextAlignNear; ALabel.Padding.Left := 10; ALabel.Padding.Top := cSpacing; ALabel.Align := vaTop; ARoot.AddObject(ALabel); ATextBox := TvxTextBox.Create(ARoot) ATextBox.Padding.Left := 13; ATextBox.Padding.Right := 240; ATextBox.Padding.Top := 3; ATextBox.Align := vaTop; ATextBox.Text := ''; ARoot.AddObject(ATextBox); AFocalLengthBox := ATextBox; ALabel := TvxLabel.Create(ARoot); ALabel.Text := 'Lens Information'; ALabel.TextAlign := vgTextAlignNear; ALabel.Padding.Left := 10; ALabel.Padding.Top := cSpacing; ALabel.Align := vaTop; ARoot.AddObject(ALabel); ATextBox := TvxTextBox.Create(ARoot) ATextBox.Padding.Left := 13; ATextBox.Padding.Right := 13; ATextBox.Padding.Top := 3; ATextBox.Align := vaTop; ATextBox.Text := ''; ARoot.AddObject(ATextBox); ALensInfoBox := ATextBox; end; procedure ReadDefaults; var AImage: TImageItem; AXmp: TXMP; begin AImage := selected.Items[0]; AXmp := TXMP.Create(False); try PublicCatalog.LoadXMPForImage(AImage, AXmp, PublicOptions.CachedXMP); AParam := AXmp.FindDesignProperty('http://ns.adobe.com/exif/1.0/', 'exif:FNumber'); if Assigned(AParam) then AFNumberBox.Text := FloatToStr(XMPRationalStringToDouble(AParam.StrContent)); AParam := AXmp.FindDesignProperty('http://ns.adobe.com/exif/1.0/', 'exif:FocalLength'); if Assigned(AParam) then AFocalLengthBox.Text := FloatToStr(XMPRationalStringToDouble(AParam.StrContent)); AParam := AXmp.FindDesignProperty('http://ns.adobe.com/exif/1.0/aux/', 'aux:Lens'); if Assigned(AParam) then ALensInfoBox.Text := AParam.StrContent else begin AParam := AXmp.FindDesignProperty('http://cipa.jp/exif/1.0/', 'exifEX:LensModel'); if Assigned(AParam) then ALensInfoBox.Text := AParam.StrContent; end; finally AXmp.Free; end; end; var AForm: TvxSceneForm; AGo: Boolean; i: Integer; APro: TxomProgress; AImg: TImageItem; begin if Selected.Count = 0 then begin Say('Please select at least one thumbnail'); exit; end; AForm := CreateForm; try ReadDefaults; AForm.PrepareModal([mbOK, mbCancel]); AForm.ShowModal; if AForm.ModalResult = mrOk then begin AGo := True; if not IsValidNumberString(AFNumberBox.Text, False, DecimalSeparator) then begin Say('No valid "F-Number" value entered'); AGo := False; end; if not IsValidNumberString(AFocalLengthBox.Text, False, DecimalSeparator) then begin Say('No valid "Focal Length" value entered'); AGo := False; end; if AGo then begin gFNumber := StrToFloat(AFNumberBox.Text); gFocalLength := StrToFloat(AFocalLengthBox.Text); gLensInfo := ALensInfoBox.Text; if AskYN( 'Are you sure you want to change the lens metadata for the ' + IntToStr(Selected.Count) + ' selected file(s)?' + CrLf + '' + CrLf + 'F-Number: F' + FloatToStr(gFNumber) + CrLf + 'Focal Length: ' + FloatToStr(gFocalLength) + 'mm' + CrLf + 'Lens Information: ' + gLensInfo ) then begin APro := TxomProgress.Create(nil); try APro.Max := Selected.Count; APro.Pos := 0; APro.Caption := 'Edit Lens Info'; for i := 0 to Selected.Count - 1 do begin AImg := Selected.Items[i]; APro.Pos := i + 1; APro.ProgressText := AImg.FileNameOnScreen(50, True); HandleImage(AImg); end; finally APro.Free; end; end; end; end; finally AForm.Free; end; end;