Resource

Resource Basics

Resource Name Edit Lens Information
Resource Version 1.2
Category Metadata
Downloads 955
Release Date 8/18/2021 10:52:17 AM
 

Resource Details

Description Allows the user to update the lens metadata for selected thumbnails
Installation Instructions n.a.
Usage Instructions 1. Right click on a thumb in Photo Supreme
2. Select Run Script from Repository
3. Select Metadata
4. Select Edit Lens Information
Changes in this version 1.1 Now also write Exif tag 0xA434 for LensModel
1.2 Added progress bar
Special Requirements
 

Author Information

Author Name IDimager Support
 

Available Attachments

Attachment #1 Edit Lens Info.png
 

Script

(*
' *************************************************************************
' * Script name:
' *   Edit Lens Info
' *   Version: 1.2
' *
' * Description:
' *   Allows the user to update the lens metadata for selected thumbnails
' *
' * Version History:
' *   IDimager;
' *     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;