Browse Source

* Patch from Pascal Riekenberg with some fixes
* fixed: group footer with position fpStackAtBottom
* refactored: footer handling
* fixed: if StartOnNewSection is set to rsColumn in more than one group in multi column mode

git-svn-id: trunk@37506 -

michael 7 years ago
parent
commit
46da90ce8e
1 changed files with 200 additions and 252 deletions
  1. 200 252
      packages/fcl-report/src/fpreport.pp

+ 200 - 252
packages/fcl-report/src/fpreport.pp

@@ -889,7 +889,7 @@ type
     procedure   BeginRuntimeProcessing; virtual;
     procedure   EndRuntimeProcessing; virtual;
     function    NeedsUpdateYPos: Boolean; virtual;
-    procedure   AfterPrintBand(aLayouter: TFPReportLayouter; aBand: TFPReportCustomBand); virtual;
+    procedure   AfterPrintBand(aList : TBandList; aBand: TFPReportCustomBand); virtual;
     procedure   BeforePrintWithChilds; virtual;
     procedure   MovedToNextPageWithChilds; virtual;
     procedure   AfterPrintWithChilds; virtual;
@@ -1055,6 +1055,7 @@ type
   protected
     procedure   DoWriteLocalProperties(AWriter: TFPReportStreamer; AOriginal: TFPReportElement = nil); override;
     function    GetReportBandName: string; override;
+    procedure   AfterPrintBand(aList : TBandList; aBand: TFPReportCustomBand); override;
     property    FooterPosition: TFPReportFooterPosition read FFooterPosition write SetFooterPosition default fpStackAtBottom;
   public
     constructor Create(AOwner: TComponent); override;
@@ -1134,6 +1135,7 @@ type
     { if first data band flows over to next page/column, also move this header }
     property    OverflowWithFirstDataBand: TFPReportSections read FOverflowWithFirstDataBand write SetOverflowWithFirstDataBand default [rsPage, rsColumn];
     function    NeedsOverflowWithFirstDataBand(pIsLastColumn: Boolean): Boolean;
+    function    NeedsIntermediateFooter(pIsLastColumn: Boolean): Boolean;
   public
     constructor Create(AOwner: TComponent); override;
     destructor Destroy; override;
@@ -1230,7 +1232,7 @@ type
     procedure EndRuntimeProcessing; override;
     function  NeedsUpdateYPos: Boolean; override;
     procedure BeforePrintWithChilds; override;
-    procedure AfterPrintBand(aLayouter: TFPReportLayouter; aBand: TFPReportCustomBand); override;
+    procedure AfterPrintBand(aList : TBandList; aBand: TFPReportCustomBand); override;
     procedure AfterPrintWithChilds; override;
     property  GroupHeader: TFPReportCustomGroupHeaderBand read FGroupHeader write SetGroupHeader;
   public
@@ -1558,6 +1560,7 @@ type
     FNewPage: boolean;  // indicates if a new ReportPage needs to be created - used if DataBand spans multiple pages for example
     FNewColumn: boolean;
     FLastDsgnDataBand: TFPReportCustomDataBand;
+    FPageFooter: TFPReportCustomBand;
     FSpaceLeft: TFPReportUnits;
     FColumnYStartPos: TFPReportUnits;
     FLastYPos: TFPReportUnits;
@@ -1570,17 +1573,16 @@ type
     FHasGroupFooter: boolean;
     FHasReportSummaryBand: boolean;
     FDataHeaderPrinted: boolean;
-    FPageDetailsPrinted: Boolean;
+    FColumnDetailsPrinted: Boolean;
     FRTCurrentColumn: UInt8;
     FRTIsMultiColumn: boolean;
     FPageHeader: TFPReportCustomPageHeaderBand;
     FTitle: TFPReportCustomTitleBand;
     FColumnHeader: TFPReportCustomColumnHeaderBand;
-    FFooterList: TBandList;
+    FGroupFooterList: TBandList;
     FColumnFooter: TFPReportCustomColumnFooterBand;
-    FRTColumnFooterList: TBandList;
     FGroupHeaderList: TBandList;
-    FBottomStackedFooterList: TBandList;
+    FRTBottomStackedFooterList: TBandList;
     FRTPage: TFPReportCustomPage;
     FCurrentRTColumnFooterBand: TFPReportCustomColumnFooterBand;
     FDataLevelStack: UInt8;
@@ -1615,6 +1617,7 @@ type
     procedure PrepareFooter(APage: TFPReportCustomPage); virtual;
     procedure PrepareHeader(APage: TFPReportCustomPage);virtual;
     procedure PrepareGroupHeader(APage: TFPReportCustomPage);virtual;
+    procedure PrepareBottomStackedFooters; virtual;
     procedure UpdateSpaceRemaining(const ABand: TFPReportCustomBand; const AUpdateYPos: boolean = True);virtual;
     function CommonRuntimeBandProcessing(const aBand: TFPReportCustomBand): TFPReportCustomBand; virtual;
     procedure ShowDataBand(const aBand: TFPReportCustomDataBand);virtual;
@@ -1622,8 +1625,8 @@ type
     procedure ShowDetailBand(const AMasterBand: TFPReportCustomDataBand);virtual;
     procedure ShowColumnFooterBand(aBand: TFPReportCustomColumnFooterBand); virtual;
     function HandleHeaderBands: Boolean; virtual;
-    Procedure HandleFooterBands(pNewPage: Boolean); virtual;
-    Procedure HandleRTColumnFooterBands; virtual;
+    Procedure HandleFooterBands; virtual;
+    procedure HandleBottomStackedFooters; virtual;
     procedure HandleRepeatedGroupHeaderBands(pNewPage: Boolean); virtual;
     procedure HandleDataBands; virtual;
     procedure HandleGroupBands; virtual;
@@ -5043,6 +5046,17 @@ begin
   Result := 'ColumnFooterBand';
 end;
 
+procedure TFPReportCustomColumnFooterBand.AfterPrintBand(
+  aList : TBandList; aBand: TFPReportCustomBand);
+begin
+  inherited AfterPrintBand(aList, aBand);
+  if FFooterPosition = fpStackAtBottom then
+  begin
+    aBand.RTLayout.Top := -1;
+    aList.Add(aBand);
+  end;
+end;
+
 constructor TFPReportCustomColumnFooterBand.Create(AOwner: TComponent);
 begin
   inherited Create(AOwner);
@@ -5090,7 +5104,8 @@ procedure TFPReportCustomGroupHeaderBand.SetFooterPosition(
 begin
   if FFooterPosition = pFooterPosition then Exit;
   FFooterPosition := pFooterPosition;
-  if FFooterPosition = fpStackAtBottom then
+  if (FFooterPosition = fpStackAtBottom)
+  and (FStartOnNewSection = rsNone) then
     if Page.IsMultiColumn then
       FStartOnNewSection := rsColumn
     else
@@ -5171,6 +5186,20 @@ begin
   (rsColumn in FOverflowWithFirstDataBand);
 end;
 
+function TFPReportCustomGroupHeaderBand.NeedsIntermediateFooter(
+  pIsLastColumn: Boolean): Boolean;
+begin
+  Result :=
+  FNeedsIntermediateFooter and
+  (
+    (
+      pIsLastColumn and
+      (rsPage in IntermediateFooter)
+    ) or
+    (rsColumn in IntermediateFooter)
+  );
+end;
+
 constructor TFPReportCustomGroupHeaderBand.Create(AOwner: TComponent);
 begin
   inherited Create(AOwner);
@@ -7981,8 +8010,7 @@ begin
   Result := True;
 end;
 
-procedure TFPReportCustomBand.AfterPrintBand(aLayouter: TFPReportLayouter;
-  aBand: TFPReportCustomBand);
+procedure TFPReportCustomBand.AfterPrintBand(aList: TBandList; aBand: TFPReportCustomBand);
 begin
   // Do nothing
 end;
@@ -8234,27 +8262,33 @@ begin
 end;
 
 procedure TFPReportCustomGroupFooterBand.AfterPrintBand(
-  aLayouter: TFPReportLayouter; aBand: TFPReportCustomBand);
+  aList : TBandList; aBand: TFPReportCustomBand);
 begin
-  inherited AfterPrintBand(aLayouter, aBand);
+  inherited AfterPrintBand(aList, aBand);
   if GroupHeader.FooterPosition = fpStackAtBottom then
-    aLayouter.FBottomStackedFooterList.Add(aBand);
+  begin
+    { mark for handling in HandleBootomStackedFooters }
+    aBand.RTLayout.Top := -1;
+    aList.Add(aBand);
+  end;
 end;
 
 procedure TFPReportCustomGroupFooterBand.AfterPrintWithChilds;
 begin
   inherited AfterPrintWithChilds;
-  { if the footer is printed then it doesn't need a repeated
-    group header if page break occurs due to no space        }
-  GroupHeader.FNeedsReprintedHeader := False;
-  { the old group is finished so next repeated group header
-    does not need to use previous varaible values            }
-  GroupHeader.FNeedsPrevVariables := False;
-  { the old group is finished
-    so an intermediate group footer is needed  }
-  GroupHeader.FNeedsIntermediateFooter := False;
+  if not Report.FRTInIntermediateGroupFooter then begin
+    { if the footer is printed then it doesn't need a repeated
+      group header if page break occurs due to no space        }
+    GroupHeader.FNeedsReprintedHeader := False;
+    { the old group is finished so next repeated group header
+      does not need to use previous varaible values            }
+    GroupHeader.FNeedsPrevVariables := False;
+    { the old group is finished
+      so an intermediate group footer is needed  }
+    GroupHeader.FNeedsIntermediateFooter := False;
 
-  Report.FRTGroupDetailsPrinted := False;
+    Report.FRTGroupDetailsPrinted := False;
+  end;
 end;
 
 procedure TFPReportCustomGroupFooterBand.ReadElement(AReader: TFPReportStreamer);
@@ -9256,7 +9290,6 @@ begin
 end;
 
 procedure TFPReportData.First;
-
 begin
   if Assigned(FOnFirst) then
     FOnFirst(Self);
@@ -9266,8 +9299,6 @@ begin
 end;
 
 procedure TFPReportData.Next;
-var
-  I: Integer;
 begin
   Inc(FRecNo);
   if Assigned(FOnNext) then
@@ -9618,15 +9649,25 @@ begin
     Result:=TFPReportCustomBand(aBand.PrepareObject(FRTPage));
     Result.RecalcLayout;
     Result.BeforePrint;
-    Result.RTLayout.Top := FLastYPos;
-    Result.RTLayout.Left := FLastXPos;
-    if not Result.EvaluateVisibility then
-       begin
+    if Result.EvaluateVisibility then
+    begin
+      if aBand.MainBand is TFPReportCustomPageFooterBand then
+      begin
+        FPageFooterYPos := FPageFooterYPos - Result.RTLayout.Height;
+        Result.RTLayout.Top := FPageFooterYPos;
+      end
+      else
+        Result.RTLayout.Top := FLastYPos;
+      if aBand.FIsColumnType then
+        Result.RTLayout.Left := FLastXPos;
+    end
+    else
+    begin
        { remove band from current page }
        Result.Page.RemoveChild(Result);
        { free mem }
        FreeAndNil(Result);
-       end;
+    end;
   finally
     aBand.MainBand.EndRuntimeProcessing;
   end;
@@ -9643,169 +9684,88 @@ begin
     ShowBandWithChilds(FTitle);
 end;
 
-procedure TFPReportLayouter.HandleFooterBands(pNewPage: Boolean);
+procedure TFPReportLayouter.HandleFooterBands;
 
 Var
-  lFooter, lRTBand: TFPReportCustomBand;
-  i, j: Integer;
+  lFooter, lRTBand, lBand: TFPReportCustomBand;
+  i: Integer;
   lFooterPosition: TFPReportFooterPosition;
   lList: TBandList;
   lGrp: TFPReportCustomGroupHeaderBand;
 
-  function IntermediateFooter: Boolean;
-  begin
-    Result := (
-      pNewPage and
-      (rsPage in lGrp.IntermediateFooter)
-    ) or
-    (rsColumn in lGrp.IntermediateFooter);
-  end;
-
 begin
   //WriteLn('HandleFooterBands:');
-  lList:=TBandList.Create;
-  try
-    { 1. from bottom to top for page footer and group footer with fpStackAtBottom }
-    for i:=0 to FFooterList.Count-1 do
+  { 1. from bottom to top for page footer }
+  if Assigned(FPageFooter) and RTIsLastColumn then
+  begin
+    lList:=TBandList.Create;
     try
-      lFooter:=FFooterList[i];
-      lFooterPosition := fpStackAtBottom;
-      if lFooter is TFPReportCustomGroupFooterBand then begin
-        lGrp := TFPReportCustomGroupFooterBand(lFooter).GroupHeader;
-        if not IntermediateFooter or
-        not lGrp.FNeedsIntermediateFooter then
-          Continue;
-        lFooterPosition := lGrp.FooterPosition;
-        Report.FRTInIntermediateGroupFooter := True;
-      end
-      else
+      lBand := FPageFooter;
+      while Assigned(lBand) do
       begin
-        lGrp := nil;
-        if not pNewPage then
-          Continue;
+        lList.Add(lBand);
+        lBand := lBand.ChildBand;
       end;
-      if lFooterPosition <> fpStackAtBottom then
-        Continue;
-      if Assigned(lGrp) and
-      lGrp.FNeedsPrevVariables then
-        Report.RTBeginUsePrevVariableValues;
+      FPageFooter.BeforePrintWithChilds;
       try
-        lList.Clear;
-        while Assigned(lFooter) do
-        begin
-          lList.Add(lFooter);
-          lFooter := lFooter.ChildBand;
-        end;
-        for j:=lList.Count-1 downto 0 do
-        begin
-          lFooter := lList[j];
-          //WriteLn('   1: ',lFooter.ClassName);
-          lRTBand := TFPReportCustomBand(lFooter.PrepareObject(FRTPage));
-          lRTBand.RecalcLayout;
-          lRTBand.BeforePrint;
-          if not lRTBand.EvaluateVisibility then
-          begin
-            //WriteLn('      discarded');
-            lRTBand.Page.RemoveChild(lRTBand);
-            lRTBand.Free;
-            continue;
-          end;
-          FPageFooterYPos := FPageFooterYPos - lRTBand.RTLayout.Height;
-          lRTBand.RTLayout.Top := FPageFooterYPos;
-          if lRTBand.FIsColumnType then
-            lRTBand.RTLayout.Left := FLastXPos;
-          //WriteLn('      bottom: page/top/height: ',IntToStr(Report.FPageNumber),'/',FormatFloat('#,##0.0', lRTBand.RTLayout.Top), '/',FormatFloat('#,##0.0', lRTBand.RTLayout.Height));
-          //WriteLn('        FooterYPos: ',FormatFloat('#,##0.0', FPageFooterYPos));
-        end;
+      for i := lList.Count-1 downto 0 do
+      begin
+        lRTBand := CommonRuntimeBandProcessing(lList[i]);
+        if Assigned(lRTBand) then
+          FPageFooter.AfterPrintBand(FRTBottomStackedFooterList , lRTBand);
+      end
       finally
-        if Assigned(lGrp) and
-        lGrp.FNeedsPrevVariables then
-          Report.RTendUsePrevVariableValues;
+        FPageFooter.AfterPrintWithChilds;
       end;
     finally
-      Report.FRTInIntermediateGroupFooter := False;
+      lList.Free;
     end;
-    { 2. from top to bottom for group footer with fpNormal }
-    for i:=FFooterList.Count-1 downto 0 do
-    try
-      lFooter:=FFooterList[i];
-      lFooterPosition := fpStackAtBottom;
-      if lFooter is TFPReportCustomGroupFooterBand then
-      begin
-        lGrp := TFPReportCustomGroupFooterBand(lFooter).GroupHeader;
-        if not IntermediateFooter or
-        not lGrp.FNeedsIntermediateFooter then
-          Continue;
-        lFooterPosition := lGrp.FooterPosition;
-        Report.FRTInIntermediateGroupFooter := True;
-      end
-      else
-      begin
-        lGrp := nil;
-        if not pNewPage then
-          Continue;
-      end;
-      if lFooterPosition <> fpNormal then
-        Continue;
-      if Assigned(lGrp) and
-      lGrp.FNeedsPrevVariables then
-        Report.RTBeginUsePrevVariableValues;
-      try
-        while Assigned(lFooter) do
-        try
-          //WriteLn('   2: ',lFooter.ClassName);
-          lRTBand := TFPReportCustomBand(lFooter.PrepareObject(FRTPage));
-          lRTBand.RecalcLayout;
-          lRTBand.BeforePrint;
-          if not lRTBand.EvaluateVisibility then
-          begin
-            //WriteLn('      discarded');
-            lRTBand.Page.RemoveChild(lRTBand);
-            lRTBand.Free;
-            continue;
-          end;
-          lRTBand.RTLayout.Top := FLastYPos;
-          if lRTBand.FIsColumnType then
-            lRTBand.RTLayout.Left := FLastXPos;
+  end;
+  { 2. from top to bottom for group footer }
+  for i:=FGroupFooterList.Count-1 downto 0 do
+  try
+    lFooter:=FGroupFooterList[i];
+    lGrp := TFPReportCustomGroupFooterBand(lFooter).GroupHeader;
+    if not lGrp.NeedsIntermediateFooter(RTIsLastColumn) then
+      Continue;
+    lFooterPosition := lGrp.FooterPosition;
+    Report.FRTInIntermediateGroupFooter := True;
+    lFooter.BeforePrintWithChilds;
+    lBand := lFooter;
+    while Assigned(lBand) do
+    begin
+      lRTBand := CommonRuntimeBandProcessing(lBand);
+      if Assigned(lRTBand) then begin
+        if lFooterPosition = fpNormal then
           UpdateSpaceRemaining(lRTBand);
-          //WriteLn('      normal: page/top/height: ',IntToStr(Report.FPageNumber),'/',FormatFloat('#,##0.0', lRTBand.RTLayout.Top), '/',FormatFloat('#,##0.0', lRTBand.RTLayout.Height));
-          //WriteLn('        LastYPos: ',FormatFloat('#,##0.0', FLastYPos));
-        finally
-          lFooter := lFooter.ChildBand;
-        end;
-      finally
-        if Assigned(lGrp) and
-        lGrp.FNeedsPrevVariables then
-          Report.RTEndUsePrevVariableValues;
+        lFooter.AfterPrintBand(FRTBottomStackedFooterList, lRTBand);
       end;
-    finally
-      Report.FRTInIntermediateGroupFooter := False;
+      lBand := lBand.ChildBand;
     end;
-    { 3. Handle column footer }
-    if Assigned(FColumnFooter) then
-      ShowColumnFooterBand(FColumnFooter);
+    lFooter.AfterPrintWithChilds;
   finally
-    lList.Free;
+    Report.FRTInIntermediateGroupFooter := False;
   end;
+  { 3. Handle column footer }
+  if Assigned(FColumnFooter) then
+    ShowColumnFooterBand(FColumnFooter);
 end;
 
-procedure TFPReportLayouter.HandleRTColumnFooterBands;
-
+procedure TFPReportLayouter.HandleBottomStackedFooters;
 var
   lOffset: TFPReportUnits;
   i: Integer;
-  lBand: TFPReportCustomBand;
-
+  lFooter: TFPReportCustomBand;
 begin
-  { move all allready layouted column footers (only fpStackAtBottom)
+  { move all allready layouted group footers (only fpStackAtBottom)
     up by offset of page footer                                      }
   lOffset := FRTPage.RTLayout.Top + FRTPage.RTLayout.Height - FPageFooterYPos;
-  for i:=0 to FRTColumnFooterList.Count-1 do
+  for i:=0 to FRTBottomStackedFooterList.Count-1 do
   begin
-    lBand := FRTColumnFooterList[i];
-    lBand.RTLayout.Top := lBand.RTLayout.Top - lOffset;
+    lFooter := FRTBottomStackedFooterList[i];
+    lFooter.RTLayout.Top := lFooter.RTLayout.Top - lOffset;
   end;
-  FRTColumnFooterList.Clear;
+  FRTBottomStackedFooterList.Clear;
 end;
 
 procedure TFPReportLayouter.HandleRepeatedGroupHeaderBands(pNewPage: Boolean);
@@ -9859,47 +9819,24 @@ end;
 procedure TFPReportLayouter.ShowColumnFooterBand(aBand: TFPReportCustomColumnFooterBand);
 
 var
-  lPageFooterYPos: TFPreportUnits;
   lBand, lRTBand : TFPReportCustomBand;
-  lBandList: TBandList;
-  i: Integer;
 
 begin
-  lPageFooterYPos := -1;
-  lBandList := TBandList.Create;
+  aBand.BeforePrintWithChilds;
   try
     lBand := aBand;
     while Assigned(lBand) do
     begin
       lRTBand := CommonRuntimeBandProcessing(lBand);
       if Assigned(lRTBand) then
-        Case aBand.FooterPosition of
-        fpNormal:
-          begin
-          { correct LastYPos }
-          FLastYPos := FLastYPos + lRTBand.RTLayout.Height;
-          end;
-        fpStackAtBottom:
-          begin
-          // save for later reverse order handling
-          lBandList.Add(lRTBand);
-          // save for later moving when page footer is printed
-          FRTColumnFooterList.Add(lRTBand);
-          end;
-        end;
+      begin
+        UpdateSpaceRemaining(lRTBand);
+        aBand.AfterPrintBand(FRTBottomStackedFooterList, lRTBand);
+      end;
       lBand := lBand.ChildBand;
     end;
-    // handle fpStackAtBottom from bottom to top
-    for i := lBandList.Count-1 downto 0 do
-    begin
-      lRTBand := lBandList[i];
-      if lPageFooterYPos = -1 then
-        lPageFooterYPos := (FRTPage.RTLayout.Top + FRTPage.RTLayout.Height);
-      lPageFooterYPos := lPageFooterYPos - lRTBand.RTLayout.Height;
-      lRTBand.RTLayout.Top := lPageFooterYPos;
-    end;
   finally
-    lBandList.Free;
+    aBand.AfterPrintWithChilds;
   end;
 end;
 
@@ -9931,32 +9868,18 @@ procedure TFPReportLayouter.EndColumn;
 begin
   { handle footers }
   FPageFooterYPos := FRTPage.RTLayout.Top + FRTPage.RTLayout.Height;
-  if not RTIsLastColumn then
-    HandleFooterBands(False);
+  { page, column and group footers}
+  HandleFooterBands;
+  { bottom stacked group and column footers }
+  PrepareBottomStackedFooters;
 end;
 
 procedure TFPReportLayouter.EndPage;
-
-var
-  lFooter: TFPReportCustomGroupFooterBand;
-  i: Integer;
-
 begin
   if Assigned(FRTPage) then
     EndColumn;
-  { page footer }
-  HandleFooterBands(True);
-  { column footers }
-  HandleRTColumnFooterBands;
-  { bottom stacked footers }
-  // ToDo: make them work in Multicolumn mode
-  for i:=FBottomStackedFooterList.Count-1 downto 0 do
-  begin
-    lFooter := TFPReportCustomGroupFooterBand(FBottomStackedFooterList[i]);
-    FPageFooterYPos := FPageFooterYPos - lFooter.RTLayout.Height;
-    lFooter.RTLayout.Top := FPageFooterYPos;
-  end;
-  FBottomStackedFooterList.Clear;
+  { bottom stacked group and column footers }
+  HandleBottomStackedFooters;
 end;
 
 procedure TFPReportLayouter.StartNewColumn;
@@ -9975,6 +9898,7 @@ begin
     ShowBandWithChilds(FColumnHeader);
   FNewColumn := False;
   HandleRepeatedGroupHeaderBands(FRTCurrentColumn = 1);
+  FColumnDetailsPrinted := False;
 end;
 
 procedure TFPReportLayouter.HandleOverflowed;
@@ -9987,7 +9911,7 @@ var
 begin
   FOverflowed := False;
   lPrevRTPage := TFPReportCustomPage(RTObjects[RTCurPageIdx-1]);
-  if FRTIsMultiColumn and (FFooterList.Find(TFPReportCustomColumnFooterBand) <> nil) then
+  if FRTIsMultiColumn and (FGroupFooterList.Find(TFPReportCustomColumnFooterBand) <> nil) then
     lBandCount := lPrevRTPage.BandCount - 2  // skip over the ColumnFooter band
   else
     lBandCount := lPrevRTPage.BandCount - 1;
@@ -10040,6 +9964,25 @@ begin
   end;
 end;
 
+procedure TFPReportLayouter.PrepareBottomStackedFooters;
+var
+  lPageFooterYPos: TFPReportUnits;
+  i: Integer;
+  lRTBand: TFPReportCustomBand;
+begin
+  // handle fpStackAtBottom from bottom to top
+  lPageFooterYPos := (FRTPage.RTLayout.Top + FRTPage.RTLayout.Height);
+  for i := FRTBottomStackedFooterList.Count-1 downto 0 do
+  begin
+    lRTBand := FRTBottomStackedFooterList[i];
+    if lRTBand.RTLayout.Top = -1 then
+    begin
+      lPageFooterYPos := lPageFooterYPos - lRTBand.RTLayout.Height;
+      lRTBand.RTLayout.Top := lPageFooterYPos;
+    end;
+  end;
+end;
+
 function TFPReportLayouter.GetPerDesignerPageCount(Index : Cardinal): Cardinal;
 begin
   Result:=Report.FPerDesignerPageCount[Index];
@@ -10085,31 +10028,19 @@ var
   lValue: TFPReportUnits;
   lGrpFooter: TFPReportCustomGroupFooterBand;
 
-  function IntermediateFooter: Boolean;
-  begin
-    Result := (
-      RTIsLastColumn and
-      (rsPage in lGrpFooter.GroupHeader.IntermediateFooter)
-    ) or
-    (rsColumn in lGrpFooter.GroupHeader.IntermediateFooter);
-  end;
-
 begin
   //Write('FooterSpaceNeeded: ');
   Result := 0;
-  for i:=0 to FFooterList.Count-1 do
+  for i:=0 to FGroupFooterList.Count-1 do
   begin
     lValue := 0;
     try
-      lFooter:=FFooterList[i];
-      if lFooter is TFPReportCustomGroupFooterBand then begin
-        lGrpFooter := TFPReportCustomGroupFooterBand(lFooter);
-        if not IntermediateFooter or
-        not lGrpFooter.GroupHeader.FNeedsIntermediateFooter or
-        lGrpFooter.FDoNotConsiderInFooterSpaceNeeded then
-          Continue;
-        Report.FRTInIntermediateGroupFooter := True;
-      end;
+      lFooter:=FGroupFooterList[i];
+      lGrpFooter := TFPReportCustomGroupFooterBand(lFooter);
+      if not lGrpFooter.GroupHeader.NeedsIntermediateFooter(RTIsLastColumn) or
+      lGrpFooter.FDoNotConsiderInFooterSpaceNeeded then
+        Continue;
+      Report.FRTInIntermediateGroupFooter := True;
       while Assigned(lFooter) do
       begin
         lBand:=TFPReportCustomBand(lFooter.PrepareObject(FRTPage));
@@ -10130,6 +10061,27 @@ begin
       //write('GF:',FormatFloat('#,##0.0', lValue),' ');
     end;
   end;
+  if Assigned(FPageFooter) then
+  begin
+    lValue := 0;
+    lFooter := FPageFooter;
+    while Assigned(lFooter) do
+    begin
+      lBand:=TFPReportCustomBand(lFooter.PrepareObject(FRTPage));
+      try
+        lBand.BeforePrint;
+        if lBand.EvaluateVisibility then begin
+          lValue := lValue + lBand.RTLayout.Height;
+          Result := Result + lBand.RTLayout.Height;
+        end;
+        lFooter := lFooter.ChildBand;
+      finally
+        lBand.Page.RemoveChild(lBand);
+        lBand.Free;
+      end;
+    end;
+    //write('PF:',FormatFloat('#,##0.0', lValue),' ');
+  end;
   if Assigned(FColumnFooter) then
   begin
     lValue := 0;
@@ -10197,15 +10149,15 @@ var
   lGrp: TFPReportCustomGroupHeaderBand;
 
 begin
-  FFooterList.Clear;
-  FFooterList.Add(APage.FindBand(TFPReportCustomPageFooterBand));
+  FGroupFooterList.Clear;
+  FPageFooter := APage.FindBand(TFPReportCustomPageFooterBand);
   { add group footers that have to be printed on every page }
   for i:=FGroupHeaderList.Count-1 downto 0 do
   begin
     lGrp:=TFPReportCustomGroupHeaderBand(FGroupHeaderList[i]);
     if Assigned(lGrp.GroupFooter) and
     (lGrp.FIntermediateFooter <> []) then
-      FFooterList.Add(lGrp.GroupFooter);
+      FGroupFooterList.Add(lGrp.GroupFooter);
   end;
   if FRTIsMultiColumn then
     FColumnFooter := TFPReportColumnFooterBand(Pages[RTCurDsgnPageIdx].FindBand(TFPReportColumnFooterBand));
@@ -10254,14 +10206,13 @@ begin
   FColumnYStartPos := FLastYPos;
   StartNewColumn;
   FNewPage := False;
-  FPageDetailsPrinted := False;
 end;
 
 procedure TFPReportLayouter.ShowDataBand(const aBand: TFPReportCustomDataBand);
 begin
   FLastDsgnDataBand := aBand;
   if ShowBandWithChilds(aBand) then
-    FPageDetailsPrinted := True;
+    FColumnDetailsPrinted := True;
 end;
 
 procedure TFPReportLayouter.ShowDataHeaderBand(const aBand: TFPReportCustomDataHeaderBand);
@@ -10554,7 +10505,7 @@ procedure TFPReportLayouter.InitPass(aPassIdx: Integer);
 begin
   Report.FIsFirstPass := (aPassIdx = 1);
   Report.EmptyRTObjects;
-  FFooterList.Clear;
+  FGroupFooterList.Clear;
   FGroupHeaderList.Clear;
   ClearBandList;
   InitRTCurPageIdx;
@@ -10612,7 +10563,7 @@ procedure TFPReportLayouter.ShowGroupHeaderBand(
 begin
   if aCheckStartOnNewSection and
   aBand.GroupChanged and
-  FPageDetailsPrinted then
+  FColumnDetailsPrinted then
     if aBand.StartOnNewSection = rsPage then
       StartNewPage
     else if aBand.StartOnNewSection = rsColumn then
@@ -10631,10 +10582,10 @@ end;
 procedure TFPReportLayouter.ShowGroupFooterBand(aBand: TFPReportCustomGroupFooterBand);
 begin
   ShowBandWithChilds(aBand);
-  FPageDetailsPrinted := True;
+  FColumnDetailsPrinted := True;
 end;
 
-Procedure TFPReportLayouter.RemoveBandsFromPage(aList : TBandList);
+procedure TFPReportLayouter.RemoveBandsFromPage(aList: TBandList);
 
 Var
   i : Integer;
@@ -10786,7 +10737,7 @@ begin
         if NoSpaceRemaining then
           overFlowActions := HandleOverflowedBands(lHandledBands, aBand, lRTBand);
         if (overFlowActions=[]) then
-          aBand.AfterPrintBand(Self, lRTBand)
+          aBand.AfterPrintBand(FRTBottomStackedFooterList, lRTBand)
         else
           Report.FRTIsOverflowed := True;
         end;
@@ -10849,22 +10800,19 @@ end;
 
 procedure TFPReportLayouter.Execute(aReport: TFPCustomReport);
 begin
-  FFooterList := Nil;
+  FGroupFooterList := Nil;
   FGroupHeaderList := Nil;
-  FBottomStackedFooterList := nil;
-  FRTColumnFooterList := nil;
+  FRTBottomStackedFooterList := nil;
   FmyReport:=AReport;
   try
-    FFooterList := TBandList.Create;
+    FGroupFooterList := TBandList.Create;
     FGroupHeaderList := TBandList.Create;
-    FBottomStackedFooterList := TBandList.Create;
-    FRTColumnFooterList := TBandList.Create;
+    FRTBottomStackedFooterList := TBandList.Create;
     DoExecute;
   finally
     FreeAndNil(FGroupHeaderList);
-    FreeAndNil(FFooterList);
-    FreeAndNil(FBottomStackedFooterList);
-    FreeAndNil(FRTColumnFooterList);
+    FreeAndNil(FGroupFooterList);
+    FreeAndNil(FRTBottomStackedFooterList);
     FMyReport:=Nil; // Don't free :)
   end;
 end;