unit uImageToPDF;
{$I ..\..\..\Lib\PDFiumVcl.inc}

interface

uses
{$IFDEF XE2+}
  Winapi.Windows,
  System.SysUtils,
  System.Classes,
  System.Types,
  System.Math,
  Vcl.Graphics,
  Vcl.Controls,
  Vcl.Forms,
  Vcl.Dialogs,
  Vcl.StdCtrls,
  Vcl.ExtCtrls,
  Vcl.ComCtrls,
  Vcl.ExtDlgs,
  Vcl.Imaging.jpeg,
  Vcl.Imaging.pngimage,
  Vcl.Imaging.GIFImg,
{$ELSE}
  Windows,
  SysUtils,
  Classes,
  Types,
  Math,
  Graphics,
  Controls,
  Forms,
  Dialogs,
  StdCtrls,
  ExtCtrls,
  ComCtrls,
  ExtDlgs,
  jpeg,
  pngimage,
  GIFImg,
{$ENDIF}
  PDFium;

type
  TFormMain= class(TForm)
    PanelTop: TPanel;
    PanelCenter: TPanel;
    PanelBottom: TPanel;

    // Top panel controls
    BtnAddImages: TButton;
    BtnClearList: TButton;
    BtnRemoveSelected: TButton;

    // Center panel controls
    ListView: TListView;
    ImagePreview: TImage;
    Splitter: TSplitter;

    // Bottom panel controls
    BtnCreatePDF: TButton;
    ProgressBar: TProgressBar;
    LabelStatus: TLabel;

    // Dialogs
    OpenPictureDialog: TOpenPictureDialog;
    SaveDialog: TSaveDialog;

    // PDF component
    Pdf: TPdf;

    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure BtnAddImagesClick(Sender: TObject);
    procedure BtnClearListClick(Sender: TObject);
    procedure BtnRemoveSelectedClick(Sender: TObject);
    procedure BtnCreatePDFClick(Sender: TObject);
    procedure ListViewSelectItem(
      Sender  : TObject;
      Item    : TListItem;
      Selected: Boolean);

  private
    FImageList: TStringList;
    procedure UpdateUI;
    procedure LoadImagePreview(const FileName: string);
    procedure CreatePDFFromImages;

  public
    { Public declarations }
  end;

var
  FormMain: TFormMain;

implementation

{$R *.dfm}

function GetFileSizeHelper(const FileName: string): Int64;
var
  SearchRec: TSearchRec;
begin
  Result:= 0;
  if FindFirst(FileName, faAnyFile, SearchRec)= 0
  then
  begin
    Result:= SearchRec.Size;
    FindClose(SearchRec);
  end;
end;

procedure TFormMain.FormCreate(Sender: TObject);
begin
  FImageList:= TStringList.Create;

  // Setup ListView
  ListView.ViewStyle:= vsReport;
  ListView.Columns.Add.Caption:= 'File Name';
  ListView.Columns.Add.Caption:= 'Path';
  ListView.Columns.Add.Caption:= 'Size';
  ListView.Columns[0].Width:= 200;
  ListView.Columns[1].Width:= 300;
  ListView.Columns[2].Width:= 100;
  ListView.RowSelect:= True;
  ListView.MultiSelect:= True; // CRITICAL FIX: Enable multi-selection

  // Setup dialogs
  OpenPictureDialog.Filter:= 'Image Files|*.jpg;*.jpeg;*.png;*.bmp;*.gif;*.tiff;*.tif|'+ 'JPEG Files|*.jpg;*.jpeg|'+ 'PNG Files|*.png|'+ 'Bitmap Files|*.bmp|'+
    'GIF Files|*.gif|'+ 'TIFF Files|*.tiff;*.tif|'+ 'All Files|*.*';
  OpenPictureDialog.Options:= [ofAllowMultiSelect, ofPathMustExist, ofFileMustExist];

  SaveDialog.Filter:= 'PDF Files|*.pdf|All Files|*.*';
  SaveDialog.DefaultExt:= 'pdf';

  UpdateUI;
  LabelStatus.Caption:= 'Ready - Click "Add Images" to start';
end;

procedure TFormMain.FormDestroy(Sender: TObject);
begin
  FImageList.Free;
end;

procedure TFormMain.UpdateUI;
begin
  BtnRemoveSelected.Enabled:= (ListView.Items.Count> 0);
  BtnClearList.Enabled:= (ListView.Items.Count> 0);
  BtnCreatePDF.Enabled:= (ListView.Items.Count> 0);
end;

procedure TFormMain.BtnAddImagesClick(Sender: TObject);
var
  i: Integer;
  ListItem: TListItem;
  FileSize: Int64;
  FileSizeStr: string;
begin
  if OpenPictureDialog.Execute
  then
  begin
    for i:= 0 to OpenPictureDialog.Files.Count- 1 do
    begin
      FImageList.Add(OpenPictureDialog.Files[i]);

      // Add to ListView
      ListItem:= ListView.Items.Add;
      ListItem.Caption:= ExtractFileName(OpenPictureDialog.Files[i]);
      ListItem.SubItems.Add(OpenPictureDialog.Files[i]);

      // Get file size
      try
        FileSize:= GetFileSizeHelper(OpenPictureDialog.Files[i]);
        if FileSize>= 1024* 1024
        then
          FileSizeStr:= Format('%.1f MB', [FileSize/ (1024* 1024)])
        else if FileSize>= 1024
        then
          FileSizeStr:= Format('%.1f KB', [FileSize/ 1024])
        else
          FileSizeStr:= Format('%d bytes', [FileSize]);
      except
        FileSizeStr:= 'Unknown';
      end;
      ListItem.SubItems.Add(FileSizeStr);
    end;

    UpdateUI;
    LabelStatus.Caption:= Format('Added %d image(s). Total: %d images', [OpenPictureDialog.Files.Count, FImageList.Count]);
  end;
end;

procedure TFormMain.BtnClearListClick(Sender: TObject);
begin
  FImageList.Clear;
  ListView.Clear;
  ImagePreview.Picture.Assign(nil);
  UpdateUI;
  LabelStatus.Caption:= 'Image list cleared - Ready to add new images';
end;

procedure TFormMain.BtnRemoveSelectedClick(Sender: TObject);
var
  i: Integer;
  RemovedCount: Integer;
begin
  RemovedCount:= 0;
  if ListView.SelCount> 0
  then
  begin
    // Remove from back to front to avoid index issues
    for i:= ListView.Items.Count- 1 downto 0 do
    begin
      if ListView.Items[i].Selected
      then
      begin
        FImageList.Delete(i);
        ListView.Items.Delete(i);
        Inc(RemovedCount);
      end;
    end;

    // Clear preview if no items left or if current preview was removed
    if (FImageList.Count= 0)or (ListView.Selected= nil)
    then
      ImagePreview.Picture.Assign(nil);

    UpdateUI;
    LabelStatus.Caption:= Format('Removed %d item(s). Total: %d images', [RemovedCount, FImageList.Count]);
  end
  else
  begin
    LabelStatus.Caption:= 'No items selected for removal';
  end;
end;

procedure TFormMain.ListViewSelectItem(
  Sender  : TObject;
  Item    : TListItem;
  Selected: Boolean);
begin
  if Selected and (Item<> nil)
  then
  begin
    LoadImagePreview(Item.SubItems[0]); // Full path is in SubItems[0]
  end
  else if not Selected
  then
  begin
    ImagePreview.Picture.Assign(nil);
  end;
end;

procedure TFormMain.LoadImagePreview(const FileName: string);
begin
  try
    if FileExists(FileName)
    then
    begin
      ImagePreview.Picture.LoadFromFile(FileName);
      ImagePreview.Proportional:= True;
      ImagePreview.Stretch:= True;
      LabelStatus.Caption:= Format('Preview: %s', [ExtractFileName(FileName)]);
    end;
  except
    on E: Exception do
    begin
      ImagePreview.Picture.Assign(nil);
      LabelStatus.Caption:= Format('Error loading preview: %s', [E.Message]);
    end;
  end;
end;

procedure TFormMain.BtnCreatePDFClick(Sender: TObject);
begin
  if FImageList.Count= 0
  then
  begin
    ShowMessage('Please add some images first!');
    Exit;
  end;

  if SaveDialog.Execute
  then
  begin
    try
      CreatePDFFromImages;
    except
      on E: Exception do
      begin
        ShowMessage('Error creating PDF: '+ E.Message + sLineBreak + 'Copy the DLL to the system or program directory first.');
        LabelStatus.Caption:= 'Error creating PDF: '+ E.Message + ' Copy the DLL to the system or program directory first.';
      end;
    end;
  end;
end;

procedure TFormMain.CreatePDFFromImages;
var
  i: Integer;
  PageWidth, PageHeight: Double;
  ImageWidth, ImageHeight: Double;
  ScaleX, ScaleY, Scale: Double;
  ImageSize: TSize;
  X, Y: Double;
  Picture: TPicture;
begin
  if FImageList.Count= 0
  then
    Exit;

  ProgressBar.Visible:= True;
  ProgressBar.Min:= 0;
  ProgressBar.Max:= FImageList.Count;
  ProgressBar.Position:= 0;

  try
    // Create new PDF document
    Pdf.CreateDocument;
    Pdf.Active:= True;

    // Create pages for all images explicitly
    for i:= 0 to FImageList.Count- 1 do
    begin
      try
        LabelStatus.Caption:= Format('Processing image %d of %d: %s', [i+ 1, FImageList.Count, ExtractFileName(FImageList[i])]);
        Application.ProcessMessages;

        // Load the actual image
        Picture:= TPicture.Create;
        try
          Picture.LoadFromFile(FImageList[i]);

          // Get image dimensions from the loaded picture
          ImageSize.cx:= Picture.Width;
          ImageSize.cy:= Picture.Height;

          // Set page size based on image aspect ratio (for calculations)
          if ImageSize.cx> ImageSize.cy
          then
          begin
            // Landscape
            PageWidth:= 842; // A4 width in points (landscape)
            PageHeight:= 595; // A4 height in points (landscape)
          end
          else
          begin
            // Portrait
            PageWidth:= 595; // A4 width in points (portrait)
            PageHeight:= 842; // A4 height in points (portrait)
          end;

          // Create page for each image
          Pdf.AddPage(i+ 1, PageWidth, PageHeight);
          Pdf.PageNumber:= i+ 1;

          // Calculate scaling to fit image on page with margins
          ImageWidth:= ImageSize.cx;
          ImageHeight:= ImageSize.cy;

          ScaleX:= (PageWidth- 80)/ ImageWidth; // 40pt margin on each side
          ScaleY:= (PageHeight- 120)/ ImageHeight; // 40pt margin top/bottom + space for text
          Scale:= Min(ScaleX, ScaleY);

          ImageWidth:= ImageWidth* Scale;
          ImageHeight:= ImageHeight* Scale;

          // Center the image on the page
          X:= (PageWidth- ImageWidth)/ 2;
          Y:= (PageHeight- ImageHeight)/ 2;

          // Add the actual image to PDF using AddPicture
          Pdf.AddPicture(Picture, X, Y, ImageWidth, ImageHeight);

          // Add image information below the image
          Pdf.AddText(Format('File: %s', [ExtractFileName(FImageList[i])]), 'Arial', 10, X, Y+ ImageHeight+ 10, clBlack, $FF, 0.0);
          Pdf.AddText(Format('Original size: %dx%d pixels', [ImageSize.cx, ImageSize.cy]), 'Arial', 8, X, Y+ ImageHeight+ 25, clGray, $FF, 0.0);
          Pdf.AddText(Format('Scale: %.1f%% (Page: %.0fx%.0f)', [Scale* 100, PageWidth, PageHeight]), 'Arial', 8, X, Y+ ImageHeight+ 40, clGray, $FF, 0.0);

          // Update status with scaling info
          LabelStatus.Caption:= Format('Image %d: %.0fx%.0f → %.0fx%.0f (%.1f%%)', [i+ 1, ImageSize.cx, ImageSize.cy, ImageWidth, ImageHeight, Scale* 100]);

        finally
          Picture.Free;
        end;

        ProgressBar.Position:= i+ 1;

      except
        on E: Exception do
        begin
          LabelStatus.Caption:= Format('Error processing %s: %s', [ExtractFileName(FImageList[i]), E.Message]);
          Application.ProcessMessages;
          // Continue with next image
        end;
      end;
    end;

    // Save the PDF
    Pdf.SaveAs(SaveDialog.FileName);
    LabelStatus.Caption:= Format('PDF created successfully: %s (%d pages)', [ExtractFileName(SaveDialog.FileName), FImageList.Count]);
    ShowMessage(Format('PDF created successfully with %d pages!', [FImageList.Count]));

  finally
    ProgressBar.Visible:= False;
    Pdf.Active:= False;
  end;
end;

end.
