{

   This program is free software; you can redistribute it and/or
  modify it under the terms of the GNU General Public License
  as published by the Free Software Foundation; either
  version 2 of the License, or (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
}
unit Ufrmprint;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Buttons, GmTypes, GmClasses, GmPropertyComboBox,
  ExtCtrls, GmPageNavigator, GmCanvas, GmPageList, GmPreview, GmConst, 
  GR32, GR32_Transforms, Spin, GR32_Resamplers, ComCtrls, imglist,
  FreeBitmap, FreeImage, FreeUtils;

type
  Pprintdata = ^Tprintdata;
  Tprintdata = record
    name:string;
    bitmap:tbitmap32;
    size:int64;
  end;

  Tfrmprint = class(TForm)
    GmPreview1: TGmPreview;
    GmPageNavigator1: TGmPageNavigator;
    Timer1: TTimer;
    PrintDialog1: TPrintDialog;
    Panel1: TPanel;
    PageControl1: TPageControl;
    TabSheet1: TTabSheet;
    TabSheet2: TTabSheet;
    Label3: TLabel;
    SpinEdit2: TSpinEdit;
    SpinEdit3: TSpinEdit;
    Button4: TButton;
    BitBtn4: TBitBtn;
    BitBtn5: TBitBtn;
    BitBtn6: TBitBtn;
    BitBtn7: TBitBtn;
    Panel2: TPanel;
    BitBtn2: TBitBtn;
    BitBtn1: TBitBtn;
    BitBtn3: TBitBtn;
    CheckBox2: TCheckBox;
    CheckBox1: TCheckBox;
    Edit1: TEdit;
    Button1: TButton;
    Label1: TLabel;
    SpinEdit1: TSpinEdit;
    Label2: TLabel;
    CheckBox3: TCheckBox;
    Label4: TLabel;
    Label5: TLabel;
    CheckBox4: TCheckBox;
    CheckBox5: TCheckBox;
    Panel3: TPanel;
    BitBtn8: TBitBtn;
    Label6: TLabel;
    SpinEdit4: TSpinEdit;
    Label7: TLabel;
    Label8: TLabel;
    SpinEdit5: TSpinEdit;
    TabSheet3: TTabSheet;
    Label9: TLabel;
    SpinEdit6: TSpinEdit;
    Label10: TLabel;
    SpinEdit7: TSpinEdit;
    Label11: TLabel;
    SpinEdit8: TSpinEdit;
    Label12: TLabel;
    SpinEdit9: TSpinEdit;
    Label13: TLabel;
    Button2: TButton;
    CheckBox6: TCheckBox;
    procedure FormCreate(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure BitBtn1Click(Sender: TObject);
    procedure GmPreview1PageMouseDown(Sender: TObject;
      Button: TMouseButton; Shift: TShiftState; X, Y: TGmValue);
    procedure GmPreview1PageMouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: TGmValue);
    procedure BitBtn2Click(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button4Click(Sender: TObject);
    procedure BitBtn5Click(Sender: TObject);
    procedure BitBtn4Click(Sender: TObject);
    procedure BitBtn6Click(Sender: TObject);
    procedure BitBtn7Click(Sender: TObject);
    procedure BitBtn8Click(Sender: TObject);
    procedure FormResize(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
    totalpage,currentpage,pagedx,pagedy:integer;
    printcancel:boolean;
    marginleft,margintop,marginright,marginbottom:integer;

    procedure drawbitmap(sourcebmp:tbitmap32);
    procedure resize(src_bitmap,dst:tbitmap32;w,h:integer);
    procedure drawpage(page:integer;enableconcel:boolean);
    procedure controldrawpage();
    procedure GmPreview1OnPagePrintBefore(Sender: TObject; pagenum: integer; var cancel:boolean);
  public
    { Public declarations }
    srcbmp:tbitmap32;
    srclist:tlist;
  end;

var
  frmprint:Tfrmprint;

implementation
uses Printers, Uconfig, Ufrmmain;
{$R *.dfm}

procedure Tfrmprint.FormCreate(Sender: TObject);
begin
  srclist:=tlist.Create;
  GmPreview1.Align:=alclient;
//  srcbmp:=tbitmap32.Create;
  totalpage:=0;
  currentpage:=0;
  GmPageNavigator1.ShowPageLabel:=true;

  self.CheckBox1.Checked:=boolean(config.getvaluebyinteger('print_title',1));
  self.CheckBox2.Checked:=boolean(config.getvaluebyinteger('print_fitpage',0));
  self.CheckBox3.Checked:=boolean(config.getvaluebyinteger('print_center',1));
  self.SpinEdit4.Value:=config.getvaluebyinteger('print_fontsize',12);

  self.SpinEdit2.Value:=config.getvaluebyinteger('print_multi_dx',2);
  self.SpinEdit3.Value:=config.getvaluebyinteger('print_multi_dy',2);
  self.CheckBox4.Checked:=boolean(config.getvaluebyinteger('print_multi_displayname',1));
  self.CheckBox5.Checked:=boolean(config.getvaluebyinteger('print_multi_displaysize',1));
  self.SpinEdit5.Value:=config.getvaluebyinteger('print_multi_fontsize',10);
  self.CheckBox6.Checked:=boolean(config.getvaluebyinteger('print_multi_fitsize',0));

  if config.getvaluebyinteger('print_width',0)=0 then begin
  end else begin
    self.Position:=poDesigned;
    self.Left:=config.getvaluebyinteger('print_left',0);
    self.Top:=config.getvaluebyinteger('print_top',0);
    self.Width:=config.getvaluebyinteger('print_width',0);
    self.Height:=config.getvaluebyinteger('print_height',0);
    if boolean(config.getvaluebyinteger('print_max',0)) then
      self.WindowState:=wsMaximized;
  end;

  SpinEdit6.Value:=config.getvaluebyinteger('print_marginleft',20);
  SpinEdit8.Value:=config.getvaluebyinteger('print_margintop',20);
  SpinEdit7.Value:=config.getvaluebyinteger('print_marginright',20);
  SpinEdit9.Value:=config.getvaluebyinteger('print_marginbottom',20);
  marginleft:=SpinEdit6.Value;
  margintop:=SpinEdit8.Value;
  marginright:=SpinEdit7.Value;
  marginbottom:=SpinEdit9.Value;

  BitBtn6.Glyph.LoadFromResourceName(HInstance,'FIRST');
  BitBtn4.Glyph.LoadFromResourceName(HInstance,'PREV');
  BitBtn5.Glyph.LoadFromResourceName(HInstance,'NEXT');
  BitBtn7.Glyph.LoadFromResourceName(HInstance,'LAST');

  frmmain.ImageList2.GetBitmap(3,BitBtn3.Glyph);
  frmmain.ImageList2.GetBitmap(3,BitBtn8.Glyph);
  frmmain.ImageList2.GetBitmap(16,BitBtn2.Glyph);
  frmmain.ImageList2.GetBitmap(15,BitBtn1.Glyph);
end;

procedure Tfrmprint.FormShow(Sender: TObject);
begin
  timer1.Enabled:=true;
  if srcbmp<>nil then begin
    TabSheet1.TabVisible:=true;
    TabSheet2.TabVisible:=false;
    self.PageControl1.ActivePage:=TabSheet1;
  end else begin
    TabSheet1.TabVisible:=false;
    TabSheet2.TabVisible:=true;
    self.PageControl1.ActivePage:=TabSheet2;
  end;
  TabSheet2.Caption:=format('%d개 이미지 출력',[srclist.Count]);
end;

procedure Tfrmprint.FormDestroy(Sender: TObject);
var
  i:integer;
  printdata:Pprintdata;
begin
  for i:=0 to srclist.Count-1 do begin
    printdata:=Pprintdata(srclist.Items[i]);
    freeandnil(printdata.bitmap);
    dispose(printdata);
  end;
  srclist.Free;

//  srcbmp.Free;
  config.setvaluebyinteger('print_marginleft',marginleft);
  config.setvaluebyinteger('print_margintop',margintop);
  config.setvaluebyinteger('print_marginright',marginright);
  config.setvaluebyinteger('print_marginbottom',marginbottom);

  config.setvaluebyinteger('print_zoom',GmPreview1.Zoom);
  config.setvaluebyinteger('print_title',integer(CheckBox1.Checked));
  config.setvaluebyinteger('print_fitpage',integer(CheckBox2.Checked));
  config.setvaluebyinteger('print_center',integer(CheckBox3.Checked));
  config.setvaluebyinteger('print_fontsize',self.SpinEdit4.Value);

  if (SpinEdit2.Value*SpinEdit3.Value<=6) then begin
    config.setvaluebyinteger('print_multi_dx',SpinEdit2.Value);
    config.setvaluebyinteger('print_multi_dy',SpinEdit3.Value);
  end;
  config.setvaluebyinteger('print_multi_displayname',integer(CheckBox4.Checked));
  config.setvaluebyinteger('print_multi_displaysize',integer(CheckBox5.Checked));
  config.setvaluebyinteger('print_multi_fontsize',self.SpinEdit5.Value);
  config.setvaluebyinteger('print_multi_fitsize',integer(CheckBox6.Checked));

  if WindowState=wsNormal then begin
    config.setvaluebyinteger('print_left',self.Left);
    config.setvaluebyinteger('print_top',self.Top);
    config.setvaluebyinteger('print_width',self.Width);
    config.setvaluebyinteger('print_height',self.Height);
    config.setvaluebyinteger('print_max',0);
  end else if WindowState=wsMaximized then
    config.setvaluebyinteger('print_max',1);
end;

procedure Tfrmprint.resize(src_bitmap,dst:tbitmap32;w,h:integer);
Var
  RectS: TRect;
  RectD: TRect;
  DstClipW,DstClipH:Trect;
  resampler:TCustomResampler;
Begin
  if w<1 then w:=1;
  if h<1 then h:=1;

  RectS.Top := 0;
  RectS.Left := 0;
  RectS.Right := src_bitmap.Width;
  RectS.Bottom := src_bitmap.Height;
  RectD.Top := 0;
  RectD.Left := 0;
  RectD.Right := w;
  RectD.Bottom := h;

  Dst.Clear(clGray32);
  Dst.Width:=w;
  Dst.Height:=h;

  resampler:=TKernelResampler.Create;
  (resampler as TKernelResampler).Kernel := TLanczosKernel.Create;
  try
    StretchTransfer(Dst, RectD, RectD, src_bitmap, RectS, resampler, dmCustom, nil);
  finally
    resampler.Free;
  end;
end;

procedure Tfrmprint.drawbitmap(sourcebmp:tbitmap32);
var
  bmp1,bmp2:tbitmap;
  size:TGmSize;
  dst,src:trect;
  w,h,h1,h2,x,y:integer;
  s:string;
  bmp3:tbitmap32;
  marginleftdiff:integer;
  fw,fh:integer;
  gmrect:Tgmrect;
begin
Screen.Cursor:=crHourGlass;
bmp1:=tbitmap.Create;
bmp2:=tbitmap.Create;
bmp3:=nil;
try
  GmPreview1.Clear;

  if (sourcebmp.Width=0) or (sourcebmp.Height=0) then
    exit;
  size:=GmPreview1.GetPageSize(gmPixels);
  size.Width:=size.Width-(marginleft+marginright);
  size.Height:=size.Height-(margintop+marginbottom);

  if self.CheckBox1.Checked then begin
    GmPreview1.Canvas.Font.Size:=self.SpinEdit4.Value;
    h2:=GmPreview1.Canvas.TextHeight('gh').AsPixels[SCREEN_PPI]+10;
  end else
    h2:=0;

  if (SpinEdit1.Value=100) and (self.CheckBox2.Checked) then begin
    bmp3:=tbitmap32.Create;
    if sourcebmp.Width>round(size.Width) then
      w:=round(size.Width)
    else
      w:=sourcebmp.Width;
    h:=(w*sourcebmp.Height) div sourcebmp.Width;
    if h>round(size.Height-h2) then begin
      h:=round(size.Height-h2);
      w:=(h*sourcebmp.Width) div sourcebmp.Height;
    end;

    resize(sourcebmp,bmp3,w,h);
    sourcebmp:=bmp3;

  end else if (SpinEdit1.Value<>100) then begin
    bmp3:=tbitmap32.Create;
    w:=round(sourcebmp.Width*(SpinEdit1.Value / 100));
    h:=(w*sourcebmp.Height) div sourcebmp.Width;

    resize(sourcebmp,bmp3,w,h);
    sourcebmp:=bmp3;

  end;

  h1:=0;
  while true do begin
    w:=sourcebmp.Width;
    h:=round(size.Height)-h2;

    src:=rect(0,h1,w,h+h1);
    if src.Bottom>sourcebmp.Height then src.Bottom:=sourcebmp.Height;
    dst:=rect(0,h2,w,h2+src.Bottom-src.Top);
    if self.CheckBox3.Checked then
      dst.Left:=round((size.Width / 2)-(sourcebmp.Width div 2))
    else
      dst.Left:=0;
    dst.Right:=dst.Left+w;

    bmp2.Width:=dst.Right-dst.Left;
    bmp2.Height:=dst.Bottom-dst.Top;
    bmp2.Canvas.Brush.Color:=clwhite;
    bmp2.Canvas.FillRect(rect(0,0,bmp2.Width,bmp2.Height));

    bmp2.Canvas.CopyRect(rect(0,0,bmp2.Width,bmp2.Height),
      sourcebmp.Canvas,src);

    marginleftdiff:=0;
    if self.CheckBox3.Checked then begin
      if dst.Left<0 then begin
        x:=marginleft;
        marginleftdiff:=abs(dst.Left);
      end else
        x:=dst.Left+marginleft;
    end else begin
      x:=dst.Left+marginleft;
    end;
    y:=dst.Top+margintop;

    if bmp2.Width>round(size.Width) then begin
      bmp1.Width:=round(size.Width);
      bmp1.Height:=bmp2.Height;
      bmp1.Canvas.CopyRect(rect(0,0,bmp1.Width,bmp1.Height),
        bmp2.Canvas,rect(marginleftdiff,0,bmp1.Width+marginleftdiff,bmp1.Height));
      GmPreview1.Canvas.Draw(x,y,bmp1,1,gmPixels);
    end else
      GmPreview1.Canvas.Draw(x,y,bmp2,1,gmPixels);

    h1:=h1+h;
    if h1<sourcebmp.Height then begin
      GmPreview1.NewPage;
    end else
      break;
    h2:=0;
  end;

  GmPreview1.FirstPage;
  if self.CheckBox1.Checked then begin
    s:=edit1.Text;
    fw:=GmPreview1.Canvas.TextWidth(s).AsPixels[SCREEN_PPI];
    fh:=GmPreview1.Canvas.TextHeight('gh').AsPixels[SCREEN_PPI];;

    if self.CheckBox3.Checked then begin
      x:=round((size.Width / 2)-(fw / 2));
    end else begin
      x:=0;
    end;
    y:=0;

    if fw<round(size.Width) then begin
      GmPreview1.Canvas.TextOut(x+marginleft,y+margintop,s,gmPixels);
    end else begin
      gmrect.Left:=marginleft;
      gmrect.Right:=marginleft+size.Width;
      gmrect.Top:=y+margintop;
      gmrect.Bottom:=y+margintop+fh;
      GmPreview1.Canvas.TextRect(gmrect,x+marginleft,y+margintop,s,gmPixels);
    end;
  end;

  GmPreview1.Update;
finally
  bmp1.Free;
  bmp2.Free;
  if assigned(bmp3) then
    bmp3.Free;
  Screen.Cursor:=crdefault;    
end;
end;

procedure Tfrmprint.Timer1Timer(Sender: TObject);
begin
  timer1.Enabled:=false;
  if config.getvaluebyinteger('print_width',0)=0 then begin
    config.setvaluebyinteger('print_left',self.Left);
    config.setvaluebyinteger('print_top',self.Top);
    config.setvaluebyinteger('print_width',self.Width);
    config.setvaluebyinteger('print_height',self.Height);
    config.setvaluebyinteger('print_max',0);
  end;

  try
    GmPreview1.PaperSize:=GmPreview1.GmPrinter.PrinterPaperSize;
    if Printer.Orientation=poPortrait then
      GmPreview1.Orientation:=gmPortrait
    else
      GmPreview1.Orientation:=gmLandscape;
    if GmPreview1.PaperSize=Custom then
      MessageDlg('표시할 수 없는 용지크기 입니다.', mtinformation, [mbOk], 0);
  except
    on E: Exception do MessageDlg(E.Message, mterror, [mbOk], 0);
  end;
  if srcbmp<>nil then
    drawbitmap(srcbmp)
  else
    Button4Click(nil);
  GmPreview1.Zoom:=config.getvaluebyinteger('print_zoom',100);
end;

procedure Tfrmprint.BitBtn1Click(Sender: TObject);
var
  i,j:integer;
  s:string;
begin
  GmPreview1.GmPrinter.PrintCopies:=PrintDialog1.Copies;
  if GmPreview1.GmPrinter.PrintCopies<=0 then GmPreview1.GmPrinter.PrintCopies:=1;
  GmPreview1.GmPrinter.Collate:=PrintDialog1.Collate;
  if srcbmp<>nil then begin
    if PrintDialog1.FromPage>GmPreview1.NumPages then
      PrintDialog1.FromPage:=GmPreview1.NumPages;
    if PrintDialog1.ToPage>GmPreview1.NumPages then
      PrintDialog1.ToPage:=GmPreview1.NumPages;
  end else begin
    if PrintDialog1.FromPage>totalpage then
      PrintDialog1.FromPage:=totalpage;
    if PrintDialog1.ToPage>totalpage then
      PrintDialog1.ToPage:=totalpage;
  end;

  if PrintDialog1.PrintRange=prPageNums then
    s:=format('인쇄범위: %d~%d 페이지',[PrintDialog1.FromPage, PrintDialog1.ToPage])
  else if PrintDialog1.PrintRange=prSelection then begin
    if srcbmp<>nil then
      s:=format('인쇄범위: 현재 %d 페이지',[GmPreview1.CurrentPageNum])
    else
      s:=format('인쇄범위: 현재 %d 페이지',[currentpage])
  end else
    s:='인쇄범위: 모든 페이지';
  s:=s+#13#10#13#10+format('인쇄매수: 각 %d장',[GmPreview1.GmPrinter.PrintCopies]);
  if (GmPreview1.GmPrinter.PrintCopies>1) and GmPreview1.GmPrinter.Collate then
    s:=s+' (한 부씩 인쇄)';

  s:=s+#13#10#13#10+'위 내용대로 인쇄 하시겠습니까?';
  if MessageDlg(s, mtConfirmation, [mbYes, mbNo], 0)<>mrYes then
    exit;

  Panel3.Visible:=true;
  label6.Caption:='인쇄준비중...';
  FormResize(nil);
  panel1.Enabled:=false;
  GmPageNavigator1.Enabled:=false;
  GmPreview1.Enabled:=false;
  printcancel:=false;
  try
    application.ProcessMessages;
    if self.srcbmp<>nil then begin
      GmPreview1.OnPagePrintBefore:=GmPreview1OnPagePrintBefore;
      try
        if PrintDialog1.PrintRange=prPageNums then
          GmPreview1.PrintRange(PrintDialog1.FromPage, PrintDialog1.ToPage)
        else if PrintDialog1.PrintRange=prSelection then
          GmPreview1.PrintCurrentPage
        else
          GmPreview1.Print;
      except
        on E: Exception do MessageDlg(E.Message, mterror, [mbOk], 0);
      end;
      GmPreview1.OnPagePrintBefore:=nil;
    end else begin
      GmPreview1.Clear;
      for i:=1 to totalpage-1 do
        GmPreview1.NewPage;
      GmPreview1.OnPagePrintBefore:=GmPreview1OnPagePrintBefore;
      GmPreview1.SetOnPageNumChanged(false);
      try
        if PrintDialog1.PrintRange=prPageNums then
          GmPreview1.PrintRange(PrintDialog1.FromPage, PrintDialog1.ToPage)
        else if PrintDialog1.PrintRange=prSelection then
          GmPreview1.PrintRange(currentpage, currentpage)
        else
          GmPreview1.Print;
      except
        on E: Exception do MessageDlg(E.Message, mterror, [mbOk], 0);
      end;
      GmPreview1.OnPagePrintBefore:=nil;
      GmPreview1.SetOnPageNumChanged(true);
      controldrawpage();
    end;
  finally
    Panel3.Visible:=false;
    panel1.Enabled:=true;
    GmPageNavigator1.Enabled:=true;
    GmPreview1.Enabled:=true;
  end;
end;

procedure Tfrmprint.GmPreview1PageMouseDown(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: TGmValue);
begin
  GmPreview1.StartPanning;
end;

procedure Tfrmprint.GmPreview1PageMouseUp(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: TGmValue);
begin
  GmPreview1.StopPanning;
end;

procedure Tfrmprint.BitBtn2Click(Sender: TObject);
var
  flag:boolean;
begin
  if GmPreview1.Orientation=gmPortrait then
    Printer.Orientation:=poPortrait
  else
    Printer.Orientation:=poLandscape;

  if srcbmp<>nil then begin
    if GmPreview1.NumPages>0 then begin
      PrintDialog1.MinPage:=1;
      PrintDialog1.MaxPage:=GmPreview1.NumPages;
    end else begin
      PrintDialog1.MinPage:=0;
      PrintDialog1.MaxPage:=0;
    end;
  end else begin
    if totalpage>0 then begin
      PrintDialog1.MinPage:=1;
      PrintDialog1.MaxPage:=totalpage;
    end else begin
      PrintDialog1.MinPage:=0;
      PrintDialog1.MaxPage:=0;
    end;
  end;
  if PrintDialog1.FromPage=0 then
    PrintDialog1.FromPage:=PrintDialog1.MinPage;
  if PrintDialog1.FromPage>PrintDialog1.MaxPage then
    PrintDialog1.FromPage:=PrintDialog1.MaxPage;

  if PrintDialog1.ToPage=0 then
    PrintDialog1.ToPage:=PrintDialog1.MaxPage;
  if PrintDialog1.ToPage>PrintDialog1.MaxPage then
    PrintDialog1.ToPage:=PrintDialog1.MaxPage;

  if PrintDialog1.Execute then begin
    flag:=false;
    if GmPreview1.GmPrinter.PrinterPaperSize<>GmPreview1.PaperSize then begin
      GmPreview1.PaperSize:=GmPreview1.GmPrinter.PrinterPaperSize;
      flag:=true;
    end;
    if ((Printer.Orientation=poPortrait) and (GmPreview1.Orientation=gmLandscape))
      or
      ((Printer.Orientation=poLandscape) and (GmPreview1.Orientation=gmPortrait))
    then begin
      if Printer.Orientation=poPortrait then
        GmPreview1.Orientation:=gmPortrait
      else
        GmPreview1.Orientation:=gmLandscape;
      flag:=true;
    end;
    if flag then begin
      if srcbmp<>nil then
        drawbitmap(srcbmp)
      else
        Button4Click(nil);
    end;
    if GmPreview1.PaperSize=Custom then
      MessageDlg('표시할 수 없는 용지크기 입니다.', mtinformation, [mbOk], 0);
  end;
end;

procedure Tfrmprint.Button1Click(Sender: TObject);
begin
  drawbitmap(srcbmp);
end;

procedure Tfrmprint.drawpage(page:integer;enableconcel:boolean);
var
  i,j,k,k1:integer;
  perpage,dx,dy,x,y,x2,y2,rx,ry,w,h:integer;
  printdata:Pprintdata;
  bmp1:tbitmap;
  destbmp:tbitmap32;
  size:Tgmsize;
  FBitmap:TFreeWinBitmap;
  FreeMemoryIO1:TFreeMemoryIO;
  NewWidth,NewHeight:integer;
  pdata:PByte;
  SizeInBytes: DWORD;
  stream:tmemorystream;
  thumbsize:integer;
  startsrclistidx:integer;
  totsize:int64;
  s:string;
  fontspace,diff,fh:integer;
  flag:boolean;

 procedure textout(x,y:integer;s:string);
 var
   gmrect:TGmRect;
   fw:integer;
 begin
    fw:=GmPreview1.Canvas.TextWidth(s).AsPixels[SCREEN_PPI];
    if fw<(rx-10) then begin
      x:=x+(rx div 2);
      GmPreview1.Canvas.TextOutCenter(x,y,s,gmPixels);
    end else begin
      gmrect.Left:=x+5;
      gmrect.Right:=x+rx-5;
      gmrect.Top:=y;
      gmrect.Bottom:=y+fh;
      GmPreview1.Canvas.TextRect(gmrect,x+5,y,s,gmPixels);
    end;
  end;
begin
  if (totalpage<=0) or (page<=0) or (page>totalpage) then
    exit;
  dx:=pagedx;
  dy:=pagedy;
  perpage:=dx*dy;
  size:=GmPreview1.GetPageSize(gmPixels);
  size.Width:=size.Width-(marginleft+marginright);
  size.Height:=size.Height-(margintop+marginbottom);

  rx:=round(size.Width / dx);
  ry:=round(size.Height / dy);

  k:=perpage*(page-1);
  k1:=0;
  startsrclistidx:=-1;
  fontspace:=3;

  FBitmap:=TFreeWinBitmap.Create;
  bmp1:=tbitmap.Create;
  destbmp:=tbitmap32.Create;
  try
   GmPreview1.Canvas.Font.Size:=self.SpinEdit5.Value;;
   fh:=GmPreview1.Canvas.TextHeight('gh').AsPixels[SCREEN_PPI];
   diff:=0;
   if self.CheckBox4.Checked then diff:=diff+fh+fontspace;
   if self.CheckBox5.Checked then diff:=diff+fh+fontspace;

   for i:=0 to dy-1 do begin
    for j:=0 to dx-1 do begin
      inc(k);
      if k>self.srclist.Count then break;
      if enableconcel and ((k mod 2)=0) then begin
        application.ProcessMessages;
        if printcancel then exit;
      end;

      if startsrclistidx=-1 then
        startsrclistidx:=k-1;
      try
        printdata:=Pprintdata(srclist.Items[k-1]);
        if printdata.bitmap=nil then begin
          printdata.bitmap:=Tbitmap32.Create;
          if FBitmap.Load(printdata.name) then begin
            if FBitmap.IsTransparent then
              FBitmap.SetTransparentBg(nil);
            FreeMemoryIO1:=TFreeMemoryIO.Create();
            try
              FBitmap.SaveToMemory(FIF_BMP,FreeMemoryIO1);
              FreeMemoryIO1.Acquire(pdata,SizeInBytes);
              stream:=tmemorystream.Create;
              try
                stream.Write(pdata^,SizeInBytes);
                stream.Position:=0;
                printdata.size:=stream.Size;
                printdata.bitmap.DrawMode:=dmblend;
                printdata.bitmap.LoadFromStream(stream);
              finally
                stream.Free;
              end;
            finally
              FreeMemoryIO1.Free;
            end;
          end;
        end;

        w:=printdata.bitmap.Width;
        h:=printdata.bitmap.Height;
        if (w>0) and (h>0) then begin
          if rx>ry then
            thumbsize:=ry-10-diff
          else
            thumbsize:=rx-10;
          if CheckBox6.Checked then begin
            if w > h then begin
              NewWidth:=thumbsize;
              NewHeight:=Round(thumbsize * h / w);
            end else begin
              NewWidth:=Round(thumbsize * w / h);
              NewHeight:=thumbsize;
            end;
            if NewWidth > w then NewWidth:=w;
            if NewHeight > h then NewHeight:=h;
            if NewWidth<=0 then NewWidth:=1;
            if NewHeight<=0 then NewHeight:=1;
            resize(printdata.bitmap,destbmp,NewWidth,Newheight)
          end else begin
            if thumbsize>w then begin
              NewWidth:=w;
              NewHeight:=h;
              flag:=false;
            end else begin
              NewWidth:=thumbsize;
              NewHeight:=Round(thumbsize * h / w);
              flag:=true;
            end;
            if NewHeight>ry-10-diff then begin
              NewWidth:=Round((ry-10-diff) * w / h);
              NewHeight:=ry-10-diff;
              flag:=true;
            end;
            if NewWidth<=0 then NewWidth:=1;
            if NewHeight<=0 then NewHeight:=1;
            if flag then
              resize(printdata.bitmap,destbmp,NewWidth,Newheight)
            else
              destbmp.Assign(printdata.bitmap);
          end;

          bmp1.Width:=NewWidth;
          bmp1.Height:=Newheight;
          destbmp.AssignTo(bmp1);
        end else begin
          bmp1.Width:=0;
          bmp1.Width:=22;
          bmp1.Height:=22;
          frmmain.ImageList1.Draw(bmp1.Canvas,0,0,0,dsTransparent,itImage);
        end;
        x:=j*rx;
        y:=i*ry;
        x:=x+((rx div 2)-(bmp1.Width div 2));
        y:=y+((ry div 2)-((bmp1.Height+diff) div 2));

        GmPreview1.Canvas.Draw(x+marginleft,y+margintop,bmp1,1,gmPixels);
        y:=y+bmp1.Height;

        if self.CheckBox4.Checked then begin
          s:=sysutils.ExtractFileName(printdata.name);
          x:=j*rx;
          y:=y+fontspace;
          textout(x+marginleft,y+margintop,s);
          y:=y+fh;
        end;
        if self.CheckBox5.Checked then begin
          s:=format('%d X %d',[printdata.bitmap.Width, printdata.bitmap.Height]);
          x:=j*rx;
          y:=y+fontspace;
          textout(x+marginleft,y+margintop,s);
        end;

      except
      end;
      inc(k1);
      if k1>=perpage then break;
    end;
    if k>self.srclist.Count then break;
    if k1>=perpage then break;
   end;

   //메모리줄이기
   if startsrclistidx>((srclist.Count-1) div 2) then begin
     totsize:=0;
     for i:=0 to startsrclistidx do begin
       printdata:=Pprintdata(srclist.Items[i]);
       if printdata.bitmap<>nil then
         totsize:=totsize+printdata.size;
     end;
     if totsize>1024*1024*100 then begin
       for i:=0 to startsrclistidx do begin
         printdata:=Pprintdata(srclist.Items[i]);
         if printdata.bitmap<>nil then begin
           freeandnil(printdata.bitmap);
           totsize:=totsize-printdata.size;
           if totsize<=1024*1024*100 then
             break;
         end;
       end;
     end;
   end else begin
     totsize:=0;
     for i:=startsrclistidx to srclist.Count-1 do begin
       printdata:=Pprintdata(srclist.Items[i]);
       if printdata.bitmap<>nil then
         totsize:=totsize+printdata.size;
     end;
     if totsize>1024*1024*100 then begin
       for i:=startsrclistidx to srclist.Count-1 do begin
         printdata:=Pprintdata(srclist.Items[i]);
         if printdata.bitmap<>nil then begin
           freeandnil(printdata.bitmap);
           totsize:=totsize-printdata.size;
           if totsize<=1024*1024*100 then
             break;
         end;
       end;
     end;
   end;

  finally
    bmp1.Free;
    FBitmap.Free;
    destbmp.Free;
  end;
end;

procedure Tfrmprint.BitBtn5Click(Sender: TObject);
begin
  inc(currentpage);
  if currentpage>totalpage then begin
    currentpage:=totalpage;
    exit;
  end;
  controldrawpage();
end;

procedure Tfrmprint.BitBtn4Click(Sender: TObject);
begin
  dec(currentpage);
  if currentpage<1 then begin
    currentpage:=1;
    exit;
  end;
  controldrawpage();
end;

procedure Tfrmprint.controldrawpage();
begin
Screen.Cursor:=crHourGlass;
try
  label3.Caption:=format('%d/%d (현재/총페이지)',[currentpage,totalpage]);
  BitBtn4.Enabled:=currentpage>1;
  BitBtn5.Enabled:=currentpage<totalpage;
  BitBtn6.Enabled:=currentpage>1;
  BitBtn7.Enabled:=currentpage<totalpage;

  GmPreview1.Clear;
  GmPreview1.CurrentPageNum:=1;
  drawpage(currentpage,false);
finally
  Screen.Cursor:=crdefault;
end;
end;

procedure Tfrmprint.Button4Click(Sender: TObject);
begin
  pagedx:=self.SpinEdit2.Value;
  pagedy:=self.SpinEdit3.Value;
  if (srclist.Count mod (pagedx*pagedy)=0) then
    totalpage:=srclist.Count div (pagedx*pagedy)
  else
    totalpage:=(srclist.Count div (pagedx*pagedy))+1;
  if currentpage<1 then
    currentpage:=1;
  if currentpage>totalpage then
    currentpage:=totalpage;
  controldrawpage();
end;

procedure Tfrmprint.GmPreview1OnPagePrintBefore(Sender: TObject; pagenum: integer; var cancel:boolean);
var
  i:integer;
begin
try
  if self.srcbmp=nil then begin
    for i:=1 to GmPreview1.NumPages do begin
      GmPreview1.Pages[i].Clear;
    end;
    currentpage:=pagenum;
    GmPreview1.CurrentPageNum:=pagenum;
    drawpage(pagenum,true);
    Label6.Caption:=format('%d/%d 페이지를 보냈습니다.',[pagenum, totalpage]);
  end else begin
    Label6.Caption:=format('%d/%d 페이지를 보냈습니다.',[pagenum, GmPreview1.NumPages]);
  end;
  application.ProcessMessages;
  cancel:=printcancel;
except
end;
end;

procedure Tfrmprint.BitBtn6Click(Sender: TObject);
begin
  currentpage:=1;
  controldrawpage();
end;

procedure Tfrmprint.BitBtn7Click(Sender: TObject);
begin
  currentpage:=totalpage;
  controldrawpage();
end;

procedure Tfrmprint.BitBtn8Click(Sender: TObject);
begin
  printcancel:=true;
end;

procedure Tfrmprint.FormResize(Sender: TObject);
begin
  if Panel3.Visible then begin
    Panel3.Left:=(self.ClientWidth div 2)-(panel3.Width div 2);
    Panel3.top:=(self.ClientHeight div 2)-(panel3.Height div 2);
  end;
end;

procedure Tfrmprint.Button2Click(Sender: TObject);
begin
  marginleft:=SpinEdit6.Value;
  margintop:=SpinEdit8.Value;
  marginright:=SpinEdit7.Value;
  marginbottom:=SpinEdit9.Value;
  if srcbmp<>nil then
    drawbitmap(srcbmp)
  else
    Button4Click(nil);
end;

end.
