Browse Source

* Merging revisions r43889,r43891,r43895,r43899,r43900,r43930,r43954,r43955,r43973 from trunk:
------------------------------------------------------------------------
r43889 | michael | 2020-01-08 15:16:51 +0100 (Wed, 08 Jan 2020) | 1 line

* Reset parent font when needed. Patch by Pascal Riekenberg (bug ID 36531)
------------------------------------------------------------------------
r43891 | michael | 2020-01-08 20:37:48 +0100 (Wed, 08 Jan 2020) | 1 line

* Fix from Pascal Riekenberg to fix missing assign of columngap (bug ID 36537)
------------------------------------------------------------------------
r43895 | michael | 2020-01-09 10:52:25 +0100 (Thu, 09 Jan 2020) | 1 line

* Do not raise error in case of warning (bug ID 36541)
------------------------------------------------------------------------
r43899 | michael | 2020-01-10 14:35:01 +0100 (Fri, 10 Jan 2020) | 1 line

* Fix from Pascal Riekenberg to fix failing tests
------------------------------------------------------------------------
r43900 | michael | 2020-01-10 14:44:10 +0100 (Fri, 10 Jan 2020) | 1 line

* Patch from Pascal Riekenberg to allow saving rendered report to JSON (bug ID 36547)
------------------------------------------------------------------------
r43930 | michael | 2020-01-13 22:14:41 +0100 (Mon, 13 Jan 2020) | 1 line

* Fix from Pascal Riekenberg to fix nested groups (bug ID 36532)
------------------------------------------------------------------------
r43954 | michael | 2020-01-16 10:40:03 +0100 (Thu, 16 Jan 2020) | 1 line

* Patch from Pascal Riekenberg to fix running a report twice. (Bug ID 36592)
------------------------------------------------------------------------
r43955 | michael | 2020-01-16 11:27:16 +0100 (Thu, 16 Jan 2020) | 1 line

* Remove debug statement, patch by Pascal Riekenberg (Bug ID 36585)
------------------------------------------------------------------------
r43973 | michael | 2020-01-18 17:40:57 +0100 (Sat, 18 Jan 2020) | 1 line

* Fix bug #0036581, amended patch by Pascal Riekenberg
------------------------------------------------------------------------

git-svn-id: branches/fixes_3_2@43990 -

michael 5 years ago
parent
commit
611a60ad5b
31 changed files with 578 additions and 95 deletions
  1. 17 0
      .gitattributes
  2. 1 1
      packages/fcl-db/src/sqldb/oracle/oracleconnection.pp
  3. BIN
      packages/fcl-report/demos/fonts/DejaVuSans-Bold.ttf
  4. BIN
      packages/fcl-report/demos/fonts/DejaVuSans-BoldOblique.ttf
  5. BIN
      packages/fcl-report/demos/fonts/DejaVuSans-ExtraLight.ttf
  6. BIN
      packages/fcl-report/demos/fonts/DejaVuSans-Oblique.ttf
  7. BIN
      packages/fcl-report/demos/fonts/DejaVuSans.ttf
  8. BIN
      packages/fcl-report/demos/fonts/Ubuntu-B.ttf
  9. BIN
      packages/fcl-report/demos/fonts/Ubuntu-BI.ttf
  10. BIN
      packages/fcl-report/demos/fonts/Ubuntu-C.ttf
  11. BIN
      packages/fcl-report/demos/fonts/Ubuntu-L.ttf
  12. BIN
      packages/fcl-report/demos/fonts/Ubuntu-LI.ttf
  13. BIN
      packages/fcl-report/demos/fonts/Ubuntu-M.ttf
  14. BIN
      packages/fcl-report/demos/fonts/Ubuntu-MI.ttf
  15. BIN
      packages/fcl-report/demos/fonts/Ubuntu-R.ttf
  16. BIN
      packages/fcl-report/demos/fonts/Ubuntu-RI.ttf
  17. BIN
      packages/fcl-report/demos/fonts/Ubuntu-Th.ttf
  18. 4 0
      packages/fcl-report/demos/rptexpressions.pp
  19. 13 12
      packages/fcl-report/demos/rptgrouping.pp
  20. 32 31
      packages/fcl-report/demos/rptnestedgroups.pp
  21. 1 1
      packages/fcl-report/demos/rptttf.pp
  22. 13 6
      packages/fcl-report/demos/udapp.pp
  23. 50 0
      packages/fcl-report/src/fpjsonreport.pp
  24. 46 14
      packages/fcl-report/src/fpreport.pp
  25. 1 0
      packages/fcl-report/src/fpreportcontnr.pp
  26. 3 0
      packages/fcl-report/src/fpreportjson.pp
  27. 41 0
      packages/fcl-report/test/README.md
  28. 10 1
      packages/fcl-report/test/regtests.pp
  29. 32 28
      packages/fcl-report/test/tcbasereport.pp
  30. 313 0
      packages/fcl-report/test/tcreportgenerator.pas
  31. 1 1
      packages/fcl-report/test/testfpreport.lpi

+ 17 - 0
.gitattributes

@@ -2700,6 +2700,11 @@ packages/fcl-report/demos/countries2.inc svneol=native#text/plain
 packages/fcl-report/demos/demos.inc svneol=native#text/plain
 packages/fcl-report/demos/demos.inc svneol=native#text/plain
 packages/fcl-report/demos/fcldemo.lpi svneol=native#text/plain
 packages/fcl-report/demos/fcldemo.lpi svneol=native#text/plain
 packages/fcl-report/demos/fcldemo.pp svneol=native#text/plain
 packages/fcl-report/demos/fcldemo.pp svneol=native#text/plain
+packages/fcl-report/demos/fonts/DejaVuSans-Bold.ttf -text
+packages/fcl-report/demos/fonts/DejaVuSans-BoldOblique.ttf -text
+packages/fcl-report/demos/fonts/DejaVuSans-ExtraLight.ttf -text
+packages/fcl-report/demos/fonts/DejaVuSans-Oblique.ttf -text
+packages/fcl-report/demos/fonts/DejaVuSans.ttf -text
 packages/fcl-report/demos/fonts/LiberationSans-Bold.ttf -text
 packages/fcl-report/demos/fonts/LiberationSans-Bold.ttf -text
 packages/fcl-report/demos/fonts/LiberationSans-BoldItalic.ttf -text
 packages/fcl-report/demos/fonts/LiberationSans-BoldItalic.ttf -text
 packages/fcl-report/demos/fonts/LiberationSans-Italic.ttf -text
 packages/fcl-report/demos/fonts/LiberationSans-Italic.ttf -text
@@ -2708,6 +2713,16 @@ packages/fcl-report/demos/fonts/LiberationSerif-Bold.ttf -text
 packages/fcl-report/demos/fonts/LiberationSerif-BoldItalic.ttf -text
 packages/fcl-report/demos/fonts/LiberationSerif-BoldItalic.ttf -text
 packages/fcl-report/demos/fonts/LiberationSerif-Italic.ttf -text
 packages/fcl-report/demos/fonts/LiberationSerif-Italic.ttf -text
 packages/fcl-report/demos/fonts/LiberationSerif-Regular.ttf -text
 packages/fcl-report/demos/fonts/LiberationSerif-Regular.ttf -text
+packages/fcl-report/demos/fonts/Ubuntu-B.ttf -text
+packages/fcl-report/demos/fonts/Ubuntu-BI.ttf -text
+packages/fcl-report/demos/fonts/Ubuntu-C.ttf -text
+packages/fcl-report/demos/fonts/Ubuntu-L.ttf -text
+packages/fcl-report/demos/fonts/Ubuntu-LI.ttf -text
+packages/fcl-report/demos/fonts/Ubuntu-M.ttf -text
+packages/fcl-report/demos/fonts/Ubuntu-MI.ttf -text
+packages/fcl-report/demos/fonts/Ubuntu-R.ttf -text
+packages/fcl-report/demos/fonts/Ubuntu-RI.ttf -text
+packages/fcl-report/demos/fonts/Ubuntu-Th.ttf -text
 packages/fcl-report/demos/laz2fpreport.lpi svneol=native#text/plain
 packages/fcl-report/demos/laz2fpreport.lpi svneol=native#text/plain
 packages/fcl-report/demos/laz2fpreport.pp svneol=native#text/plain
 packages/fcl-report/demos/laz2fpreport.pp svneol=native#text/plain
 packages/fcl-report/demos/pictures/man01.png -text svneol=unset#image/png
 packages/fcl-report/demos/pictures/man01.png -text svneol=unset#image/png
@@ -2776,6 +2791,7 @@ packages/fcl-report/src/fpreportjson.pp svneol=native#text/plain
 packages/fcl-report/src/fpreportpdfexport.pp svneol=native#text/plain
 packages/fcl-report/src/fpreportpdfexport.pp svneol=native#text/plain
 packages/fcl-report/src/fpreportqrcode.pp svneol=native#text/plain
 packages/fcl-report/src/fpreportqrcode.pp svneol=native#text/plain
 packages/fcl-report/src/fpreportstreamer.pp svneol=native#text/plain
 packages/fcl-report/src/fpreportstreamer.pp svneol=native#text/plain
+packages/fcl-report/test/README.md svneol=native#text/plain
 packages/fcl-report/test/fonts/LiberationSerif-Regular.ttf -text
 packages/fcl-report/test/fonts/LiberationSerif-Regular.ttf -text
 packages/fcl-report/test/fonts/calibri.ttf -text
 packages/fcl-report/test/fonts/calibri.ttf -text
 packages/fcl-report/test/fonts/calibrib.ttf -text
 packages/fcl-report/test/fonts/calibrib.ttf -text
@@ -2787,6 +2803,7 @@ packages/fcl-report/test/regtests.pp svneol=native#text/plain
 packages/fcl-report/test/tcbasereport.pp svneol=native#text/plain
 packages/fcl-report/test/tcbasereport.pp svneol=native#text/plain
 packages/fcl-report/test/tchtmlparser.pas svneol=native#text/plain
 packages/fcl-report/test/tchtmlparser.pas svneol=native#text/plain
 packages/fcl-report/test/tcreportdom.pp svneol=native#text/plain
 packages/fcl-report/test/tcreportdom.pp svneol=native#text/plain
+packages/fcl-report/test/tcreportgenerator.pas svneol=native#text/plain
 packages/fcl-report/test/tcreportstreamer.pp svneol=native#text/plain
 packages/fcl-report/test/tcreportstreamer.pp svneol=native#text/plain
 packages/fcl-report/test/testfpreport.lpi svneol=native#text/plain
 packages/fcl-report/test/testfpreport.lpi svneol=native#text/plain
 packages/fcl-report/test/testfpreport.lpr svneol=native#text/plain
 packages/fcl-report/test/testfpreport.lpr svneol=native#text/plain

+ 1 - 1
packages/fcl-db/src/sqldb/oracle/oracleconnection.pp

@@ -1051,7 +1051,7 @@ begin
     OCI_SUCCESS : Result := True;
     OCI_SUCCESS : Result := True;
     OCI_SUCCESS_WITH_INFO : Begin
     OCI_SUCCESS_WITH_INFO : Begin
                             Result := True;
                             Result := True;
-                            HandleError;
+                            // HandleError;
                             end;
                             end;
   end; {case}
   end; {case}
 end;
 end;

BIN
packages/fcl-report/demos/fonts/DejaVuSans-Bold.ttf


BIN
packages/fcl-report/demos/fonts/DejaVuSans-BoldOblique.ttf


BIN
packages/fcl-report/demos/fonts/DejaVuSans-ExtraLight.ttf


BIN
packages/fcl-report/demos/fonts/DejaVuSans-Oblique.ttf


BIN
packages/fcl-report/demos/fonts/DejaVuSans.ttf


BIN
packages/fcl-report/demos/fonts/Ubuntu-B.ttf


BIN
packages/fcl-report/demos/fonts/Ubuntu-BI.ttf


BIN
packages/fcl-report/demos/fonts/Ubuntu-C.ttf


BIN
packages/fcl-report/demos/fonts/Ubuntu-L.ttf


BIN
packages/fcl-report/demos/fonts/Ubuntu-LI.ttf


BIN
packages/fcl-report/demos/fonts/Ubuntu-M.ttf


BIN
packages/fcl-report/demos/fonts/Ubuntu-MI.ttf


BIN
packages/fcl-report/demos/fonts/Ubuntu-R.ttf


BIN
packages/fcl-report/demos/fonts/Ubuntu-RI.ttf


BIN
packages/fcl-report/demos/fonts/Ubuntu-Th.ttf


+ 4 - 0
packages/fcl-report/demos/rptexpressions.pp

@@ -135,7 +135,11 @@ begin
   Memo.Layout.Top := 20;
   Memo.Layout.Top := 20;
   Memo.Layout.Width := 50;
   Memo.Layout.Width := 50;
   Memo.Layout.Height := 8;
   Memo.Layout.Height := 8;
+  {$IFDEF fptestX}
+  Memo.Text := 'Report Date: 2020-01-15';
+  {$ELSE}
   Memo.Text := 'Report Date: [TODAY]';
   Memo.Text := 'Report Date: [TODAY]';
+  {$ENDIF}
 
 
   Memo := TFPReportMemo.Create(TitleBand);
   Memo := TFPReportMemo.Create(TitleBand);
   Memo.Layout.Left := 0;
   Memo.Layout.Left := 0;

+ 13 - 12
packages/fcl-report/demos/rptgrouping.pp

@@ -154,7 +154,7 @@ begin
   {*** group header ***}
   {*** group header ***}
   GroupHeader := TFPReportGroupHeaderBand.Create(p);
   GroupHeader := TFPReportGroupHeaderBand.Create(p);
   GroupHeader.Layout.Height := 15;
   GroupHeader.Layout.Height := 15;
-  GroupHeader.GroupCondition := 'copy(country,1,1)';
+  GroupHeader.GroupCondition := 'copy(data.country,1,1)';
   {$ifdef ColorBands}
   {$ifdef ColorBands}
   GroupHeader.Frame.Shape := fsRectangle;
   GroupHeader.Frame.Shape := fsRectangle;
   GroupHeader.Frame.BackgroundColor := clGroupHeaderFooter;
   GroupHeader.Frame.BackgroundColor := clGroupHeaderFooter;
@@ -166,7 +166,7 @@ begin
   Memo.Layout.Width := 10;
   Memo.Layout.Width := 10;
   Memo.Layout.Height := 8;
   Memo.Layout.Height := 8;
   Memo.UseParentFont := False;
   Memo.UseParentFont := False;
-  Memo.Text := '[copy(country,1,1)]';
+  Memo.Text := '[copy(data.country,1,1)]';
   Memo.Font.Size := 16;
   Memo.Font.Size := 16;
 
 
   Memo := TFPReportMemo.Create(GroupHeader);
   Memo := TFPReportMemo.Create(GroupHeader);
@@ -197,11 +197,11 @@ begin
 
 
 
 
   {*** variables ***}
   {*** variables ***}
-  rpt.Variables.AddExprVariable('population_in_M', 'sum(StrToFloat(population) / 1000000)', rtFloat, rtNone, '');
-  rpt.Variables.AddExprVariable('grp_sum_population', 'sum(StrToFloat(population))',rtFloat , GroupHeader);
-  rpt.Variables.AddExprVariable('grp_sum_population_in_M', 'sum(StrToFloat(population) / 1000000)', rtFloat, GroupHeader);
-  rpt.Variables.AddExprVariable('sum_population', 'sum(StrToFloat(population))', rtFloat, rtnone, '');
-  rpt.Variables.AddExprVariable('sum_population_in_M', 'sum(StrToFloat(population) / 1000000)', rtFloat,rtnone,'');
+  rpt.Variables.AddExprVariable('population_in_M', 'sum(StrToFloat(data.population) / 1000000)', rtFloat, rtNone, '');
+  rpt.Variables.AddExprVariable('grp_sum_population', 'sum(StrToFloat(data.population))',rtFloat , GroupHeader);
+  rpt.Variables.AddExprVariable('grp_sum_population_in_M', 'sum(StrToFloat(data.population) / 1000000)', rtFloat, GroupHeader);
+  rpt.Variables.AddExprVariable('sum_population', 'sum(StrToFloat(data.population))', rtFloat, rtnone, '');
+  rpt.Variables.AddExprVariable('sum_population_in_M', 'sum(StrToFloat(data.population) / 1000000)', rtFloat,rtnone,'');
 
 
 
 
   {*** detail ***}
   {*** detail ***}
@@ -218,7 +218,7 @@ begin
   Memo.Layout.Top := 2;
   Memo.Layout.Top := 2;
   Memo.Layout.Width := 45;
   Memo.Layout.Width := 45;
   Memo.Layout.Height := 5;
   Memo.Layout.Height := 5;
-  Memo.Text := '[country]';
+  Memo.Text := '[data.country]';
 
 
   Memo := TFPReportMemo.Create(DataBand);
   Memo := TFPReportMemo.Create(DataBand);
   Memo.Layout.Left := 55;
   Memo.Layout.Left := 55;
@@ -237,7 +237,7 @@ begin
   Memo.Text := '> Germany';
   Memo.Text := '> Germany';
   Memo.UseParentFont := false;
   Memo.UseParentFont := false;
   Memo.Font.Color := clGreen;
   Memo.Font.Color := clGreen;
-  Memo.VisibleExpr := 'StrToFloat(population) > 80890000';
+  Memo.VisibleExpr := 'StrToFloat(data.population) > 80890000';
 
 
   Memo := TFPReportMemo.Create(DataBand);
   Memo := TFPReportMemo.Create(DataBand);
   Memo.Layout.Left := 85;
   Memo.Layout.Left := 85;
@@ -247,7 +247,7 @@ begin
   Memo.Text := '< Germany';
   Memo.Text := '< Germany';
   Memo.UseParentFont := false;
   Memo.UseParentFont := false;
   Memo.Font.Color := clRed;
   Memo.Font.Color := clRed;
-  Memo.VisibleExpr := 'StrToFloat(population) < 80890000';
+  Memo.VisibleExpr := 'StrToFloat(data.population) < 80890000';
 
 
   Memo := TFPReportMemo.Create(DataBand);
   Memo := TFPReportMemo.Create(DataBand);
   Memo.Layout.Left := 110;
   Memo.Layout.Left := 110;
@@ -255,7 +255,7 @@ begin
   Memo.Layout.Width := 15;
   Memo.Layout.Width := 15;
   Memo.Layout.Height := 5;
   Memo.Layout.Height := 5;
   Memo.TextAlignment.Horizontal := taRightJustified;
   Memo.TextAlignment.Horizontal := taRightJustified;
-  Memo.Text := '[formatfloat(''#,##0.0'',StrToFloat(population)/grp_sum_population*100)] %';
+  Memo.Text := '[formatfloat(''#,##0.0'',StrToFloat(data.population)/grp_sum_population*100)] %';
 
 
   Memo := TFPReportMemo.Create(DataBand);
   Memo := TFPReportMemo.Create(DataBand);
   Memo.Layout.Left := 130;
   Memo.Layout.Left := 130;
@@ -263,7 +263,7 @@ begin
   Memo.Layout.Width := 15;
   Memo.Layout.Width := 15;
   Memo.Layout.Height := 5;
   Memo.Layout.Height := 5;
   Memo.TextAlignment.Horizontal := taRightJustified;
   Memo.TextAlignment.Horizontal := taRightJustified;
-  Memo.Text := '[formatfloat(''#,##0.0'',StrToFloat(population)/sum_population*100)] %';
+  Memo.Text := '[formatfloat(''#,##0.0'',StrToFloat(data.population)/sum_population*100)] %';
 
 
   {*** group footer ***}
   {*** group footer ***}
   GroupFooter := TFPReportGroupFooterBand.Create(p);
   GroupFooter := TFPReportGroupFooterBand.Create(p);
@@ -353,6 +353,7 @@ constructor TGroupingDemo.Create(AOwner: TComponent);
 begin
 begin
   inherited;
   inherited;
   lReportData := TFPReportUserData.Create(nil);
   lReportData := TFPReportUserData.Create(nil);
+  lReportData.Name := 'data';
   lReportData.OnGetValue := @GetReportDataValue;
   lReportData.OnGetValue := @GetReportDataValue;
   lReportData.OnGetEOF := @GetReportDataEOF;
   lReportData.OnGetEOF := @GetReportDataEOF;
   lReportData.OnFirst := @GetReportDataFirst;
   lReportData.OnFirst := @GetReportDataFirst;

+ 32 - 31
packages/fcl-report/demos/rptnestedgroups.pp

@@ -192,7 +192,7 @@ begin
 
 
   GroupHeader1Region := TFPReportGroupHeaderBand.Create(Page);
   GroupHeader1Region := TFPReportGroupHeaderBand.Create(Page);
   GroupHeader1Region.Layout.Height := 15;
   GroupHeader1Region.Layout.Height := 15;
-  GroupHeader1Region.GroupCondition := 'region';
+  GroupHeader1Region.GroupCondition := 'data.region';
   GroupHeader1Region.Frame.Shape := fsRectangle;
   GroupHeader1Region.Frame.Shape := fsRectangle;
   GroupHeader1Region.Frame.BackgroundColor := clGroupHeaderFooter;
   GroupHeader1Region.Frame.BackgroundColor := clGroupHeaderFooter;
   //GroupHeader1Region.StartOnNewPage := True;
   //GroupHeader1Region.StartOnNewPage := True;
@@ -209,7 +209,7 @@ begin
     Memo.UseParentFont := False;
     Memo.UseParentFont := False;
     Memo.Font.Size := 16;
     Memo.Font.Size := 16;
     Memo.TextAlignment.Vertical := tlBottom;
     Memo.TextAlignment.Vertical := tlBottom;
-    Memo.Text := 'Region: [region] ([formatfloat(''#,##0.0'', grp1region_sum_population_in_M)] M)';
+    Memo.Text := 'Region: [data.region] ([formatfloat(''#,##0.0'', grp1region_sum_population_in_M)] M)';
 
 
     Memo := TFPReportMemo.Create(GroupHeader1Region);
     Memo := TFPReportMemo.Create(GroupHeader1Region);
     Memo.Layout.Left := 25;
     Memo.Layout.Left := 25;
@@ -235,7 +235,7 @@ begin
     Memo.Layout.Top := 1;
     Memo.Layout.Top := 1;
     Memo.Layout.Width := 170;
     Memo.Layout.Width := 170;
     Memo.Layout.Height := 4;
     Memo.Layout.Height := 4;
-    Memo.Text := 'Region: [region]';
+    Memo.Text := 'Region: [data.region]';
 
 
 
 
   {*** group header 2 subregion ***}
   {*** group header 2 subregion ***}
@@ -244,7 +244,7 @@ begin
 
 
   GroupHeader2Subregion := TFPReportGroupHeaderBand.Create(Page);
   GroupHeader2Subregion := TFPReportGroupHeaderBand.Create(Page);
   GroupHeader2Subregion.Layout.Height := 2;
   GroupHeader2Subregion.Layout.Height := 2;
-  GroupHeader2Subregion.GroupCondition := 'subregion';
+  GroupHeader2Subregion.GroupCondition := 'data.subregion';
   GroupHeader2Subregion.ParentGroupHeader := GroupHeader1Region;
   GroupHeader2Subregion.ParentGroupHeader := GroupHeader1Region;
   //GroupHeader2Subregion.StartOnNewPage := True;
   //GroupHeader2Subregion.StartOnNewPage := True;
   GroupHeader2Subregion.ReprintedHeader := [rsPage];
   GroupHeader2Subregion.ReprintedHeader := [rsPage];
@@ -295,7 +295,7 @@ begin
     Memo.UseParentFont := False;
     Memo.UseParentFont := False;
     Memo.Font.Size := 14;
     Memo.Font.Size := 14;
     Memo.TextAlignment.Vertical := tlBottom;
     Memo.TextAlignment.Vertical := tlBottom;
-    Memo.Text := 'Subregion: [subregion] ([formatfloat(''#,##0.0'', grp2subregion_sum_population_in_M)] M)';
+    Memo.Text := 'Subregion: [data.subregion] ([formatfloat(''#,##0.0'', grp2subregion_sum_population_in_M)] M)';
 
 
     Memo := TFPReportMemo.Create(ChildBand);
     Memo := TFPReportMemo.Create(ChildBand);
     Memo.Layout.Left := 25;
     Memo.Layout.Left := 25;
@@ -306,7 +306,7 @@ begin
     Memo.Font.Size := 10;
     Memo.Font.Size := 10;
     Memo.TextAlignment.Vertical := tlBottom;
     Memo.TextAlignment.Vertical := tlBottom;
     Memo.TextAlignment.Horizontal := taRightJustified;
     Memo.TextAlignment.Horizontal := taRightJustified;
-    Memo.Text := '[formatfloat(''#0.0'', grp2subregion_sum_population / grp1region_sum_population * 100)] % in [region] - [formatfloat(''#0.0'', grp2subregion_sum_population / total_sum_population * 100)] % in World';
+    Memo.Text := '[formatfloat(''#0.0'', grp2subregion_sum_population / grp1region_sum_population * 100)] % in [data.region] - [formatfloat(''#0.0'', grp2subregion_sum_population / total_sum_population * 100)] % in World';
 
 
   {--- group header 2 subregion - band 3 ---}
   {--- group header 2 subregion - band 3 ---}
 
 
@@ -340,7 +340,7 @@ begin
     Memo.Layout.Top := 1;
     Memo.Layout.Top := 1;
     Memo.Layout.Width := 170;
     Memo.Layout.Width := 170;
     Memo.Layout.Height := 4;
     Memo.Layout.Height := 4;
-    Memo.Text := 'Subregion: [subregion]';
+    Memo.Text := 'Subregion: [data.subregion]';
 
 
 
 
   {*** group header 3 initial ***}
   {*** group header 3 initial ***}
@@ -349,7 +349,7 @@ begin
 
 
   GroupHeader3Initial := TFPReportGroupHeaderBand.Create(Page);
   GroupHeader3Initial := TFPReportGroupHeaderBand.Create(Page);
   GroupHeader3Initial.Layout.Height := 2;
   GroupHeader3Initial.Layout.Height := 2;
-  GroupHeader3Initial.GroupCondition := 'copy(country,1,1)';
+  GroupHeader3Initial.GroupCondition := 'copy(data.country,1,1)';
   GroupHeader3Initial.ParentGroupHeader := GroupHeader2Subregion;
   GroupHeader3Initial.ParentGroupHeader := GroupHeader2Subregion;
   GroupHeader3Initial.ReprintedHeader := [rsPage];
   GroupHeader3Initial.ReprintedHeader := [rsPage];
   GroupHeader3Initial.IntermediateFooter := [rsPage];
   GroupHeader3Initial.IntermediateFooter := [rsPage];
@@ -426,7 +426,7 @@ begin
     Memo.UseParentFont := False;
     Memo.UseParentFont := False;
     Memo.Font.Size := 12;
     Memo.Font.Size := 12;
     Memo.TextAlignment.Vertical := tlBottom;
     Memo.TextAlignment.Vertical := tlBottom;
-    Memo.Text := '[copy(country,1,1)]  ([formatfloat(''#,##0.0'', grp3initial_sum_population_in_M)] M)';
+    Memo.Text := '[copy(data.country,1,1)]  ([formatfloat(''#,##0.0'', grp3initial_sum_population_in_M)] M)';
 
 
     Memo := TFPReportMemo.Create(ChildBand);
     Memo := TFPReportMemo.Create(ChildBand);
     Memo.Layout.Left := 25;
     Memo.Layout.Left := 25;
@@ -437,7 +437,7 @@ begin
     Memo.Font.Size := 10;
     Memo.Font.Size := 10;
     Memo.TextAlignment.Vertical := tlBottom;
     Memo.TextAlignment.Vertical := tlBottom;
     Memo.TextAlignment.Horizontal := taRightJustified;
     Memo.TextAlignment.Horizontal := taRightJustified;
-    Memo.Text := '[formatfloat(''#0.0'', grp3initial_sum_population / grp2subregion_sum_population * 100)] % in [subregion] - [formatfloat(''#0.0'', grp3initial_sum_population / grp1region_sum_population * 100)] % in [region] - [formatfloat(''#0.0'', grp3initial_sum_population / total_sum_population * 100)] % in World';
+    Memo.Text := '[formatfloat(''#0.0'', grp3initial_sum_population / grp2subregion_sum_population * 100)] % in [data.subregion] - [formatfloat(''#0.0'', grp3initial_sum_population / grp1region_sum_population * 100)] % in [data.region] - [formatfloat(''#0.0'', grp3initial_sum_population / total_sum_population * 100)] % in World';
 
 
     Memo := TFPReportMemo.Create(ChildBand);
     Memo := TFPReportMemo.Create(ChildBand);
     Memo.Layout.Left := 90;
     Memo.Layout.Left := 90;
@@ -521,7 +521,7 @@ begin
     Memo.Layout.Top := 1;
     Memo.Layout.Top := 1;
     Memo.Layout.Width := 170;
     Memo.Layout.Width := 170;
     Memo.Layout.Height := 4;
     Memo.Layout.Height := 4;
-    Memo.Text := '[copy(country,1,1)]';
+    Memo.Text := '[copy(data.country,1,1)]';
 
 
 
 
   {--- group header 3 initial - band 4 ---}
   {--- group header 3 initial - band 4 ---}
@@ -559,15 +559,15 @@ begin
 
 
   {*** variables ***}
   {*** variables ***}
 
 
-  rpt.Variables.AddExprVariable('population_in_M', 'StrToFloat(population) / 1000000', rtFloat, rtNone, '');
-  rpt.Variables.AddExprVariable('grp1region_sum_population_in_M', 'sum(StrToFloat(population) / 1000000)', rtFloat, GroupHeader1Region);
-  rpt.Variables.AddExprVariable('grp1region_sum_population', 'sum(StrToFloat(population))', rtFloat, GroupHeader1Region);
-  rpt.Variables.AddExprVariable('grp2subregion_sum_population_in_M', 'sum(StrToFloat(population) / 1000000)', rtFloat, GroupHeader2Subregion);
-  rpt.Variables.AddExprVariable('grp2subregion_sum_population', 'sum(StrToFloat(population))', rtFloat, GroupHeader2Subregion);
-  rpt.Variables.AddExprVariable('grp3initial_sum_population_in_M', 'sum(StrToFloat(population) / 1000000)', rtFloat, GroupHeader3Initial);
-  rpt.Variables.AddExprVariable('grp3initial_sum_population', 'sum(StrToFloat(population))', rtFloat, GroupHeader3Initial);
-  rpt.Variables.AddExprVariable('total_sum_population_in_M', 'sum(StrToFloat(population) / 1000000)', rtFloat);
-  rpt.Variables.AddExprVariable('total_sum_population', 'sum(StrToFloat(population))', rtFloat);
+  rpt.Variables.AddExprVariable('population_in_M', 'StrToFloat(data.population) / 1000000', rtFloat, rtNone, '');
+  rpt.Variables.AddExprVariable('grp1region_sum_population_in_M', 'sum(StrToFloat(data.population) / 1000000)', rtFloat, GroupHeader1Region);
+  rpt.Variables.AddExprVariable('grp1region_sum_population', 'sum(StrToFloat(data.population))', rtFloat, GroupHeader1Region);
+  rpt.Variables.AddExprVariable('grp2subregion_sum_population_in_M', 'sum(StrToFloat(data.population) / 1000000)', rtFloat, GroupHeader2Subregion);
+  rpt.Variables.AddExprVariable('grp2subregion_sum_population', 'sum(StrToFloat(data.population))', rtFloat, GroupHeader2Subregion);
+  rpt.Variables.AddExprVariable('grp3initial_sum_population_in_M', 'sum(StrToFloat(data.population) / 1000000)', rtFloat, GroupHeader3Initial);
+  rpt.Variables.AddExprVariable('grp3initial_sum_population', 'sum(StrToFloat(data.population))', rtFloat, GroupHeader3Initial);
+  rpt.Variables.AddExprVariable('total_sum_population_in_M', 'sum(StrToFloat(data.population) / 1000000)', rtFloat);
+  rpt.Variables.AddExprVariable('total_sum_population', 'sum(StrToFloat(data.population))', rtFloat);
 
 
 
 
   {****************}
   {****************}
@@ -639,7 +639,7 @@ begin
     Memo.Layout.Top := 2;
     Memo.Layout.Top := 2;
     Memo.Layout.Width := 45;
     Memo.Layout.Width := 45;
     Memo.Layout.Height := 5;
     Memo.Layout.Height := 5;
-    Memo.Text := '[country]';
+    Memo.Text := '[data.country]';
     Memo.Options := memo.Options + [moDisableWordWrap];
     Memo.Options := memo.Options + [moDisableWordWrap];
 
 
     Memo := TFPReportMemo.Create(DataBand);
     Memo := TFPReportMemo.Create(DataBand);
@@ -648,7 +648,7 @@ begin
     Memo.Layout.Width := 25;
     Memo.Layout.Width := 25;
     Memo.Layout.Height := 5;
     Memo.Layout.Height := 5;
     Memo.TextAlignment.Horizontal := taRightJustified;
     Memo.TextAlignment.Horizontal := taRightJustified;
-    Memo.Text := '[formatfloat(''#,##0'', StrToFloat(population))]';
+    Memo.Text := '[formatfloat(''#,##0'', StrToFloat(data.population))]';
 
 
     Memo := TFPReportMemo.Create(DataBand);
     Memo := TFPReportMemo.Create(DataBand);
     Memo.Layout.Left := 80;
     Memo.Layout.Left := 80;
@@ -658,7 +658,7 @@ begin
     Memo.Text := '> DEU';
     Memo.Text := '> DEU';
     Memo.UseParentFont := false;
     Memo.UseParentFont := false;
     Memo.Font.Color := clGreen;
     Memo.Font.Color := clGreen;
-    Memo.VisibleExpr := 'StrToFloat(population) > 82667685';
+    Memo.VisibleExpr := 'StrToFloat(data.population) > 82667685';
 
 
     Memo := TFPReportMemo.Create(DataBand);
     Memo := TFPReportMemo.Create(DataBand);
     Memo.Layout.Left := 80;
     Memo.Layout.Left := 80;
@@ -668,7 +668,7 @@ begin
     Memo.Text := '< DEU';
     Memo.Text := '< DEU';
     Memo.UseParentFont := false;
     Memo.UseParentFont := false;
     Memo.Font.Color := clRed;
     Memo.Font.Color := clRed;
-    Memo.VisibleExpr := 'StrToFloat(population) < 82667685';
+    Memo.VisibleExpr := 'StrToFloat(data.population) < 82667685';
 
 
     Memo := TFPReportMemo.Create(DataBand);
     Memo := TFPReportMemo.Create(DataBand);
     Memo.Layout.Left := 95;
     Memo.Layout.Left := 95;
@@ -676,7 +676,7 @@ begin
     Memo.Layout.Width := 15;
     Memo.Layout.Width := 15;
     Memo.Layout.Height := 5;
     Memo.Layout.Height := 5;
     Memo.TextAlignment.Horizontal := taRightJustified;
     Memo.TextAlignment.Horizontal := taRightJustified;
-    Memo.Text := '[formatfloat(''#,##0.0'',StrToFloat(population)/grp3initial_sum_population*100)] %';
+    Memo.Text := '[formatfloat(''#,##0.0'',StrToFloat(data.population)/grp3initial_sum_population*100)] %';
 
 
     Memo := TFPReportMemo.Create(DataBand);
     Memo := TFPReportMemo.Create(DataBand);
     Memo.Layout.Left := 115;
     Memo.Layout.Left := 115;
@@ -684,7 +684,7 @@ begin
     Memo.Layout.Width := 15;
     Memo.Layout.Width := 15;
     Memo.Layout.Height := 5;
     Memo.Layout.Height := 5;
     Memo.TextAlignment.Horizontal := taRightJustified;
     Memo.TextAlignment.Horizontal := taRightJustified;
-    Memo.Text := '[formatfloat(''#,##0.0'',StrToFloat(population)/grp2subregion_sum_population*100)] %';
+    Memo.Text := '[formatfloat(''#,##0.0'',StrToFloat(data.population)/grp2subregion_sum_population*100)] %';
 
 
     Memo := TFPReportMemo.Create(DataBand);
     Memo := TFPReportMemo.Create(DataBand);
     Memo.Layout.Left := 135;
     Memo.Layout.Left := 135;
@@ -692,7 +692,7 @@ begin
     Memo.Layout.Width := 15;
     Memo.Layout.Width := 15;
     Memo.Layout.Height := 5;
     Memo.Layout.Height := 5;
     Memo.TextAlignment.Horizontal := taRightJustified;
     Memo.TextAlignment.Horizontal := taRightJustified;
-    Memo.Text := '[formatfloat(''#,##0.0'',StrToFloat(population)/grp1region_sum_population*100)] %';
+    Memo.Text := '[formatfloat(''#,##0.0'',StrToFloat(data.population)/grp1region_sum_population*100)] %';
 
 
     Memo := TFPReportMemo.Create(DataBand);
     Memo := TFPReportMemo.Create(DataBand);
     Memo.Layout.Left := 155;
     Memo.Layout.Left := 155;
@@ -700,7 +700,7 @@ begin
     Memo.Layout.Width := 15;
     Memo.Layout.Width := 15;
     Memo.Layout.Height := 5;
     Memo.Layout.Height := 5;
     Memo.TextAlignment.Horizontal := taRightJustified;
     Memo.TextAlignment.Horizontal := taRightJustified;
-    Memo.Text := '[formatfloat(''#,##0.0'',StrToFloat(population)/total_sum_population*100)] %';
+    Memo.Text := '[formatfloat(''#,##0.0'',StrToFloat(data.population)/total_sum_population*100)] %';
 
 
 
 
   {**********************}
   {**********************}
@@ -821,7 +821,7 @@ begin
     Memo.UseParentFont := False;
     Memo.UseParentFont := False;
     Memo.Font.Size := 12;
     Memo.Font.Size := 12;
     Memo.TextAlignment.Vertical := tlBottom;
     Memo.TextAlignment.Vertical := tlBottom;
-    Memo.Text := 'Population [copy(country,1,1)]: [formatfloat(''#,##0'', grp3initial_sum_population)]';
+    Memo.Text := 'Population [copy(data.country,1,1)]: [formatfloat(''#,##0'', grp3initial_sum_population)]';
 
 
   {--- group footer 3 initial - band 4 ---}
   {--- group footer 3 initial - band 4 ---}
 
 
@@ -947,7 +947,7 @@ begin
     Memo.UseParentFont := False;
     Memo.UseParentFont := False;
     Memo.Font.Size := 14;
     Memo.Font.Size := 14;
     Memo.TextAlignment.Vertical := tlBottom;
     Memo.TextAlignment.Vertical := tlBottom;
-    Memo.Text := 'Population [subregion]: [formatfloat(''#,##0'', grp2subregion_sum_population)]';
+    Memo.Text := 'Population [data.subregion]: [formatfloat(''#,##0'', grp2subregion_sum_population)]';
 
 
   {--- group footer 2 subregion - band 4 ---}
   {--- group footer 2 subregion - band 4 ---}
 
 
@@ -1038,7 +1038,7 @@ begin
     Memo.UseParentFont := False;
     Memo.UseParentFont := False;
     Memo.Font.Size := 16;
     Memo.Font.Size := 16;
     Memo.TextAlignment.Vertical := tlBottom;
     Memo.TextAlignment.Vertical := tlBottom;
-    Memo.Text := 'Population [region]: [formatfloat(''#,##0'', grp1region_sum_population)]';
+    Memo.Text := 'Population [data.region]: [formatfloat(''#,##0'', grp1region_sum_population)]';
 
 
   {--- group footer 1 region - band 3 ---}
   {--- group footer 1 region - band 3 ---}
 
 
@@ -1182,6 +1182,7 @@ constructor TNestedGroupsDemo.Create(AOWner: TComponent);
 begin
 begin
   inherited;
   inherited;
   FReportData := TFPReportUserData.Create(nil);
   FReportData := TFPReportUserData.Create(nil);
+  FReportData.Name := 'Data';
   FReportData.OnGetValue := @GetReportDataValue;
   FReportData.OnGetValue := @GetReportDataValue;
   FReportData.OnGetEOF := @GetReportDataEOF;
   FReportData.OnGetEOF := @GetReportDataEOF;
   FReportData.OnFirst := @GetReportDataFirst;
   FReportData.OnFirst := @GetReportDataFirst;

+ 1 - 1
packages/fcl-report/demos/rptttf.pp

@@ -153,7 +153,7 @@ begin
   GroupHeader := TFPReportGroupHeaderBand.Create(p);
   GroupHeader := TFPReportGroupHeaderBand.Create(p);
   GroupHeader.Layout.Height := 15;
   GroupHeader.Layout.Height := 15;
   GroupHeader.Data := lReportData;
   GroupHeader.Data := lReportData;
-  GroupHeader.GroupCondition := '[copy(country,1,1)]';
+  GroupHeader.GroupCondition := 'copy(country,1,1)';
   GroupHeader.Frame.BackgroundColor := clYellow;    // this has no affect on rendered PDF because here Shape = fsNone
   GroupHeader.Frame.BackgroundColor := clYellow;    // this has no affect on rendered PDF because here Shape = fsNone
   GroupHeader.Frame.Color :=   TFPReportColor($01579B);
   GroupHeader.Frame.Color :=   TFPReportColor($01579B);
   GroupHeader.Frame.Lines := [flBottom];
   GroupHeader.Frame.Lines := [flBottom];

+ 13 - 6
packages/fcl-report/demos/udapp.pp

@@ -7,7 +7,7 @@ unit udapp;
 interface
 interface
 
 
 uses
 uses
-  Classes, SysUtils, fpttf, fpreport,
+  Classes, SysUtils, fpttf, fpreport, fpjsonreport,
 
 
   {$IFDEF ExportPDF}
   {$IFDEF ExportPDF}
   fpreportpdfexport,
   fpreportpdfexport,
@@ -48,14 +48,15 @@ Type
 
 
   TReportDemoApp = class(TComponent)
   TReportDemoApp = class(TComponent)
   private
   private
-    Frpt: TFPReport;
+    Frpt: TFPJSONReport;
   protected
   protected
     procedure InitialiseData; virtual;
     procedure InitialiseData; virtual;
     procedure CreateReportDesign; virtual;
     procedure CreateReportDesign; virtual;
   public
   public
+    procedure TestInit;
     Class Function Description : string; virtual;
     Class Function Description : string; virtual;
 //    procedure DoCreateJSON(const AFileName: String; RunTime: Boolean=False);
 //    procedure DoCreateJSON(const AFileName: String; RunTime: Boolean=False);
-    Property rpt : TFPReport read Frpt Write FRpt;
+    Property rpt : TFPJSONReport read Frpt Write FRpt;
   end;
   end;
   TReportDemoAppClass = Class of TReportDemoApp;
   TReportDemoAppClass = Class of TReportDemoApp;
 
 
@@ -144,14 +145,20 @@ begin
     PaperManager.RegisterStandardSizes;
     PaperManager.RegisterStandardSizes;
 end;
 end;
 
 
+procedure TReportDemoApp.TestInit;
+begin
+  Frpt := TFPJSONReport.Create(Self);
+  InitialiseData;
+  CreateReportDesign;
+end;
+
 class function TReportDemoApp.Description: string;
 class function TReportDemoApp.Description: string;
 begin
 begin
   Result:='';
   Result:='';
 end;
 end;
 
 
 
 
-class function TReportDemoApplication.GetRenderClass(F: TRenderFormat
-  ): TFPReportExporterClass;
+class function TReportDemoApplication.GetRenderClass(F: TRenderFormat): TFPReportExporterClass;
 
 
 begin
 begin
   Case F of
   Case F of
@@ -465,7 +472,7 @@ begin
   if (F<>'') and (CompareText(F,'default')<>0) and (Fmt=rfDefault) then
   if (F<>'') and (CompareText(F,'default')<>0) and (Fmt=rfDefault) then
     Usage(Format('Unknown output format: %s',[F]));
     Usage(Format('Unknown output format: %s',[F]));
   FRunner.ReportApp:=GetReportClass(D).Create(Self);
   FRunner.ReportApp:=GetReportClass(D).Create(Self);
-  FRunner.ReportApp.rpt:=TFPReport.Create(FRunner.ReportApp);
+  FRunner.ReportApp.rpt:=TFPJSONReport.Create(FRunner.ReportApp);
   FRunner.Format:=Fmt;
   FRunner.Format:=Fmt;
   FRunner.DesignFileName:=J;
   FRunner.DesignFileName:=J;
   FRunner.Execute;
   FRunner.Execute;

+ 50 - 0
packages/fcl-report/src/fpjsonreport.pp

@@ -52,10 +52,13 @@ Type
     destructor Destroy; override;
     destructor Destroy; override;
     procedure LoadFromStream(const aStream: TStream);
     procedure LoadFromStream(const aStream: TStream);
     procedure SaveToStream(const aStream: TStream);
     procedure SaveToStream(const aStream: TStream);
+    procedure SaveRenderToStream(const aStream: TStream);
     Procedure LoadFromJSON(aJSON : TJSONObject); virtual;
     Procedure LoadFromJSON(aJSON : TJSONObject); virtual;
     Procedure SavetoJSON(aJSON : TJSONObject); virtual;
     Procedure SavetoJSON(aJSON : TJSONObject); virtual;
+    Procedure SaveRenderToJSON(aJSON : TJSONObject); virtual;
     Procedure LoadFromFile(const aFileName : String);
     Procedure LoadFromFile(const aFileName : String);
     Procedure SaveToFile(const aFileName : String);
     Procedure SaveToFile(const aFileName : String);
+    procedure SaveRenderToFile(const aFileName: String);
     Property LoadErrors : TStrings Read FLoadErrors;
     Property LoadErrors : TStrings Read FLoadErrors;
     Property DataManager : TFPCustomReportDataManager Read FDataManager Write SetDataManager;
     Property DataManager : TFPCustomReportDataManager Read FDataManager Write SetDataManager;
     Property DesignDataName : String Read GetDesignDataName Write SetDesignDataName Stored StoreDesignDataName;
     Property DesignDataName : String Read GetDesignDataName Write SetDesignDataName Stored StoreDesignDataName;
@@ -242,6 +245,23 @@ begin
   end;
   end;
 end;
 end;
 
 
+procedure TFPJSONReport.SaveRenderToJSON(aJSON: TJSONObject);
+
+Var
+  R : TFPReportJSONStreamer;
+
+begin
+  DoWriteJSON(aJSON);
+  R:=TFPReportJSONStreamer.Create(Nil);
+  try
+    R.OwnsJSON:=False;
+    R.JSON:=aJSON;
+    WriteRTElement(R);
+  finally
+    R.Free;
+  end;
+end;
+
 procedure TFPJSONReport.LoadFromStream(const aStream : TStream);
 procedure TFPJSONReport.LoadFromStream(const aStream : TStream);
 
 
 Var
 Var
@@ -275,6 +295,23 @@ begin
   end;
   end;
 end;
 end;
 
 
+procedure TFPJSONReport.SaveRenderToStream(const aStream: TStream);
+
+Var
+  O : TJSONObject;
+  S : TJSONStringType;
+
+begin
+  O:=TJSONObject.Create;
+  try
+    SaveRendertoJSON(O);
+    S:=O.AsJSON;
+    aStream.WriteBuffer(S[1],Length(S));
+  finally
+    O.Free;
+  end;
+end;
+
 procedure TFPJSONReport.LoadFromFile(const aFileName: String);
 procedure TFPJSONReport.LoadFromFile(const aFileName: String);
 
 
 Var
 Var
@@ -302,5 +339,18 @@ begin
   end;
   end;
 end;
 end;
 
 
+procedure TFPJSONReport.SaveRenderToFile(const aFileName: String);
+Var
+  F : TFileStream;
+
+begin
+  F:=TFileStream.Create(aFileName,fmCreate);
+  try
+    SaveRenderToStream(F);
+  finally
+    F.Free;
+  end;
+end;
+
 end.
 end.
 
 

+ 46 - 14
packages/fcl-report/src/fpreport.pp

@@ -22,7 +22,7 @@ unit fpreport;
 // Global debugging
 // Global debugging
 { $define gdebug}
 { $define gdebug}
 // Separate for aggregate variables
 // Separate for aggregate variables
-{$define gdebuga}
+{ $define gdebuga}
 
 
 interface
 interface
 
 
@@ -1676,6 +1676,7 @@ type
     Procedure SaveDataToNames;
     Procedure SaveDataToNames;
     Procedure RestoreDataFromNames;
     Procedure RestoreDataFromNames;
     procedure WriteElement(AWriter: TFPReportStreamer; AOriginal: TFPReportElement = nil); override;
     procedure WriteElement(AWriter: TFPReportStreamer; AOriginal: TFPReportElement = nil); override;
+    procedure WriteRTElement(AWriter: TFPReportStreamer; AOriginal: TFPReportElement = nil);
     procedure ReadElement(AReader: TFPReportStreamer); override;
     procedure ReadElement(AReader: TFPReportStreamer); override;
     procedure AddPage(APage: TFPReportCustomPage);
     procedure AddPage(APage: TFPReportCustomPage);
     procedure RemovePage(APage: TFPReportCustomPage);
     procedure RemovePage(APage: TFPReportCustomPage);
@@ -3305,9 +3306,9 @@ begin
   if (aNode is TFPExprVariable) then
   if (aNode is TFPExprVariable) then
     begin
     begin
     DS:=ExtractWord(1,TFPExprVariable(ANode).Identifier.Name,['.']);
     DS:=ExtractWord(1,TFPExprVariable(ANode).Identifier.Name,['.']);
-    If AData.FindReportData(DS)<>Nil then
+      If AData.FindReportData(DS)<>Nil then
       FDataName:=DS;
       FDataName:=DS;
-    end
+      end
   else if (ANode is TFPExprFunction) then
   else if (ANode is TFPExprFunction) then
     begin
     begin
     I:=0;
     I:=0;
@@ -3512,11 +3513,7 @@ begin
   if Not SameText(aData.Name,FDataName) then
   if Not SameText(aData.Name,FDataName) then
     exit;
     exit;
   If not IsFirstPass then
   If not IsFirstPass then
-    begin
-    FLastValue.ResultType:=rtFloat;
-    FLastValue.ResFloat:=0;
     exit;
     exit;
-    end;
   if (FResetValue=#0) then
   if (FResetValue=#0) then
     begin
     begin
     FResetValue:=#255;
     FResetValue:=#255;
@@ -3623,8 +3620,8 @@ begin
         inc(FAggregateValuesIndex);
         inc(FAggregateValuesIndex);
         end;
         end;
       FResetValue:=lResetValue;
       FResetValue:=lResetValue;
-      FAggregateValue:=PFPExpressionResult(FAggregateValues[FAggregateValuesIndex])^;
       FLastValue:=FAggregateValue;
       FLastValue:=FAggregateValue;
+      FAggregateValue:=PFPExpressionResult(FAggregateValues[FAggregateValuesIndex])^;
       end
       end
     else
     else
       begin
       begin
@@ -3643,7 +3640,7 @@ begin
     begin
     begin
     WriteString('Name',Self.Name);
     WriteString('Name',Self.Name);
     WriteString('DataType',ResultTypeName(DataType));
     WriteString('DataType',ResultTypeName(DataType));
-    WriteString('Value',Value);
+//    WriteString('Value',Value);
     WriteString('Expression',Expression);
     WriteString('Expression',Expression);
     WriteString('ResetValueExpression',ResetValueExpression);
     WriteString('ResetValueExpression',ResetValueExpression);
     WriteString('ResetType',GetEnumName(TypeInfo(TFPReportResetType),Ord(ResetType)));
     WriteString('ResetType',GetEnumName(TypeInfo(TFPReportResetType),Ord(ResetType)));
@@ -3668,7 +3665,7 @@ begin
       DataType:=rtString
       DataType:=rtString
     else
     else
       DataType:=TResultType(I);
       DataType:=TResultType(I);
-    Value:=ReadString('Value','');
+//    Value:=ReadString('Value','');
     Expression:=ReadString('Expression','');
     Expression:=ReadString('Expression','');
     ResetValueExpression:=ReadString('ResetValueExpression','');
     ResetValueExpression:=ReadString('ResetValueExpression','');
     S:=ReadString('ResetType','');
     S:=ReadString('ResetType','');
@@ -5137,6 +5134,7 @@ begin
   FUseParentFont := True;
   FUseParentFont := True;
   FFont := TFPReportFont.Create;
   FFont := TFPReportFont.Create;
   FFont.OnChanged:=@HandleFontChange;
   FFont.OnChanged:=@HandleFontChange;
+  ReassignParentFont;
   FCullThreshold := 75;
   FCullThreshold := 75;
 end;
 end;
 
 
@@ -7733,6 +7731,7 @@ begin
     Report := E.Report;
     Report := E.Report;
     Font.Assign(E.Font);
     Font.Assign(E.Font);
     ColumnCount := E.ColumnCount;
     ColumnCount := E.ColumnCount;
+    ColumnGap := E.ColumnGap;
   end;
   end;
   inherited Assign(Source);
   inherited Assign(Source);
 end;
 end;
@@ -8724,6 +8723,39 @@ begin
   // TODO: Implement writing OnRenderReport, OnBeginReport, OnEndReport
   // TODO: Implement writing OnRenderReport, OnBeginReport, OnEndReport
 end;
 end;
 
 
+procedure TFPCustomReport.WriteRTElement(AWriter: TFPReportStreamer; AOriginal: TFPReportElement);
+var
+  i: integer;
+begin
+  // ignore AOriginal here as we don't support whole report diffs, only element diffs
+  AWriter.PushElement('Report');
+  try
+    inherited WriteElement(AWriter, AOriginal);
+    // local properties
+    AWriter.WriteString('Title', Title);
+    AWriter.WriteString('Author', Author);
+    AWriter.WriteBoolean('TwoPass',TwoPass);
+    AWriter.WriteDateTime('DateCreated', DateCreated);
+    // now the pages
+    AWriter.PushElement('Pages');
+    try
+      for i := 0 to RTObjects.Count - 1 do
+      begin
+        AWriter.PushElement(IntToStr(i)); // use page index as identifier
+        try
+          TFPReportComponent(RTObjects[i]).WriteElement(AWriter);
+        finally
+          AWriter.PopElement;
+        end;
+      end;
+    finally
+      AWriter.PopElement;
+    end;
+  finally
+    AWriter.PopElement;
+  end;
+end;
+
 procedure TFPCustomReport.ReadElement(AReader: TFPReportStreamer);
 procedure TFPCustomReport.ReadElement(AReader: TFPReportStreamer);
 var
 var
   E: TObject;
   E: TObject;
@@ -8927,6 +8959,7 @@ end;
 procedure TFPCustomReport.RunReport;
 procedure TFPCustomReport.RunReport;
 begin
 begin
   DoBeginReport;
   DoBeginReport;
+  ClearPreparedReport;
   StartLayout;
   StartLayout;
   CollectReportData;
   CollectReportData;
   Validate;
   Validate;
@@ -9439,6 +9472,7 @@ begin
   FBandPosition := bpNormal;
   FBandPosition := bpNormal;
   FFont:=TFPReportFont.Create;
   FFont:=TFPReportFont.Create;
   FFont.OnChanged:=@HandleFontChange;
   FFont.OnChanged:=@HandleFontChange;
+  ReassignParentFont;
 end;
 end;
 
 
 destructor TFPReportCustomBand.Destroy;
 destructor TFPReportCustomBand.Destroy;
@@ -11895,12 +11929,11 @@ begin
         {$endif}
         {$endif}
         // DumpData(lData);
         // DumpData(lData);
         PrepareRecord(lData);
         PrepareRecord(lData);
+        Report.UpdateAggregates(lPage,lData);
         if FNewPage then
         if FNewPage then
           StartNewPage;
           StartNewPage;
         ShowDataHeaderBand;
         ShowDataHeaderBand;
         HandleGroupBands;
         HandleGroupBands;
-        // This must be done after the groups were handled.
-        Report.UpdateAggregates(lPage,lData);
         ShowDataBand;
         ShowDataBand;
         lData.Next;
         lData.Next;
         end;  { while not lData.EOF }
         end;  { while not lData.EOF }
@@ -12096,12 +12129,11 @@ begin
         {$endif}
         {$endif}
         // DumpData(aPageData);
         // DumpData(aPageData);
         PrepareRecord(aData);
         PrepareRecord(aData);
+        Report.UpdateAggregates(aPage,aData);
         if FNewPage then
         if FNewPage then
           StartNewPage;
           StartNewPage;
         ShowDataHeaderBand;
         ShowDataHeaderBand;
         HandleGroupBands;
         HandleGroupBands;
-        // This must be done after the groups were handled.
-        Report.UpdateAggregates(aPage,aData);
         ShowDataBand;
         ShowDataBand;
         aData.Next;
         aData.Next;
         end;
         end;

+ 1 - 0
packages/fcl-report/src/fpreportcontnr.pp

@@ -260,6 +260,7 @@ procedure TFPReportObjectData.DoClose;
 begin
 begin
   FIndex:=-1;
   FIndex:=-1;
   inherited DoClose;
   inherited DoClose;
+  DataFields.Clear;
 end;
 end;
 
 
 function TFPReportObjectData.DoEOF: boolean;
 function TFPReportObjectData.DoEOF: boolean;

+ 3 - 0
packages/fcl-report/src/fpreportjson.pp

@@ -190,7 +190,9 @@ begin
   else
   else
     AValue:=D.AsJSON;
     AValue:=D.AsJSON;
   end;
   end;
+  {$IFDEF gdebug}
   Writeln(FIndex,' : ',AFieldName,' -> ',AValue);
   Writeln(FIndex,' : ',AFieldName,' -> ',AValue);
+  {$ENDIF}
 end;
 end;
 
 
 procedure TFPReportJSONData.DoInitDataFields;
 procedure TFPReportJSONData.DoInitDataFields;
@@ -249,6 +251,7 @@ end;
 procedure TFPReportJSONData.DoClose;
 procedure TFPReportJSONData.DoClose;
 begin
 begin
   inherited DoClose;
   inherited DoClose;
+  DataFields.Clear;
   FIndex:=-1;
   FIndex:=-1;
 end;
 end;
 
 

+ 41 - 0
packages/fcl-report/test/README.md

@@ -0,0 +1,41 @@
+# Testsuite
+
+## Demos
+
+The testsuite can optionally run all demos: define USEDEMOS and fpTestX on the
+command-line or in the lazarus defines.
+
+In that case the ../demo and ../demo/polygon directories must be added to
+the unit path of the compiler.
+
+You can then run these tests using the following command-line
+./testfpreport --suite=TTestDemos
+
+The demo reports will be rendered and saved to a directory "rendered".
+
+The first time you run the demo test, the file will be called demo.set.json.
+The second time you run the demo test, if the result differs, the result
+will be saved to a file called demo.actual.json.
+
+So, to test changes, first delete all json files in  the rendered directory.
+Then do a first run, this will create the initial files, and set a baseline. 
+Make your changes, and then run the reports again. You will be notified of
+differences.
+
+The reason these files are not stored in SVN is that they are dependent on
+the platform:
+a) The reports contain newlines. 
+   Depending on the platform they will be saved as \r \r\n or \n. 
+b) There can and will be localization issues. 
+
+## Fonts 
+
+The demos need some extra fonts. 
+The needed fonts (Ubuntu and DejaVu Sans) can be downloaded from:
+
+* https://assets.ubuntu.com/v1/0cef8205-ubuntu-font-family-0.83.zip
+* https://www.downloadfonts.io/calibri-font-family-free/
+* https://www.fontsquirrel.com/fonts/download/dejavu-sans
+* https://www.fontsquirrel.com/fonts/download/liberation-sans
+
+These fonts should be saved to the ./fonts or ../demo/fonts directory.

+ 10 - 1
packages/fcl-report/test/regtests.pp

@@ -4,10 +4,19 @@ unit regtests;
   Add all test units to the uses clause here.
   Add all test units to the uses clause here.
   Avoids messing with the uses clause of the main program(s).
   Avoids messing with the uses clause of the main program(s).
 }
 }
+
+// Define USEDEMOS if you want to test & compare rendering of the demos.
+
+{$DEFINE USEDEMOS}
+
 interface
 interface
 
 
 uses
 uses
-  tcbasereport, tcreportstreamer, tchtmlparser;
+  tcbasereport, tcreportstreamer, tchtmlparser
+{$IFDEF USEDEMOS}
+  , tcreportgenerator
+{$ENDIF}
+  ;
 
 
 implementation
 implementation
   
   

+ 32 - 28
packages/fcl-report/test/tcbasereport.pp

@@ -239,7 +239,7 @@ type
 
 
   TTestReportChildren = class(TTestCase)
   TTestReportChildren = class(TTestCase)
   private
   private
-    FC: TMyFPReportElementWithChildren;
+    FC, FC2: TMyFPReportElementWithChildren;
     FChild: TFPReportElement;
     FChild: TFPReportElement;
   protected
   protected
     procedure SetUp; override;
     procedure SetUp; override;
@@ -743,11 +743,11 @@ begin
   Variable.DataType:=rtFloat;
   Variable.DataType:=rtFloat;
   AssertEquals('Float type remains',rtFloat,Variable.DataType);
   AssertEquals('Float type remains',rtFloat,Variable.DataType);
   AssertEquals('Float default value',0.0,Variable.AsFloat);
   AssertEquals('Float default value',0.0,Variable.AsFloat);
-  AssertEquals('Float as string',0.0,StrToFloat(Variable.Value));
+  AssertEquals('Float as string',' 0.0000000000000000E+000',Variable.Value);
   Variable.DataType:=rtBoolean;
   Variable.DataType:=rtBoolean;
   Variable.AsFloat:=1.23;
   Variable.AsFloat:=1.23;
   AssertEquals('Float type remains',rtFloat,Variable.DataType);
   AssertEquals('Float type remains',rtFloat,Variable.DataType);
-  AssertEquals('Float as string',1.23,StrToFloat(Variable.Value));
+  AssertEquals('Float as string',' 1.2300000000000000E+000',Variable.Value);
   AssertEquals('Float value',1.23,Variable.AsFloat);
   AssertEquals('Float value',1.23,Variable.AsFloat);
   R:=Variable.AsExpressionResult;
   R:=Variable.AsExpressionResult;
   AssertEquals('Correct result',rtFloat,r.resulttype);
   AssertEquals('Correct result',rtFloat,r.resulttype);
@@ -1520,13 +1520,15 @@ end;
 procedure TTestReportChildren.SetUp;
 procedure TTestReportChildren.SetUp;
 begin
 begin
   FC := TMyFPReportElementWithChildren.Create(nil);
   FC := TMyFPReportElementWithChildren.Create(nil);
+  FC2 := TMyFPReportElementWithChildren.Create(nil);
   FChild := TFPReportElement.Create(nil);
   FChild := TFPReportElement.Create(nil);
 end;
 end;
 
 
 procedure TTestReportChildren.TearDown;
 procedure TTestReportChildren.TearDown;
 begin
 begin
-  FreeAndNil(FC);
   FreeAndNil(FChild);
   FreeAndNil(FChild);
+  FreeAndNil(FC);
+  FreeAndNil(FC2);
 end;
 end;
 
 
 procedure TTestReportChildren.WrongParent;
 procedure TTestReportChildren.WrongParent;
@@ -1562,17 +1564,12 @@ begin
   AssertEquals('Parent childcount is 1', 1, FC.ChildCount);
   AssertEquals('Parent childcount is 1', 1, FC.ChildCount);
   AssertSame('Parent first child is OK', FChild, FC.Child[0]);
   AssertSame('Parent first child is OK', FChild, FC.Child[0]);
   FC.ResetChanged;
   FC.ResetChanged;
-  E := TFPReportElementWithChildren.Create(nil);
-  try
-    FChild.Parent := E;
-    AssertSame('Parent was saved', E, FChild.parent);
-    AssertEquals('Changed was called', 1, FC.ChangedCalled);
-    AssertEquals('Old Parent childcount is 0', 0, FC.ChildCount);
-    AssertEquals('Parent childcount is 1', 1, E.ChildCount);
-    AssertSame('Parent first child is OK', FChild, E.Child[0]);
-  finally
-    E.Free;
-  end;
+  FChild.Parent := FC2;
+  AssertSame('Parent was saved', FC2, FChild.parent);
+  AssertEquals('Changed was called', 1, FC.ChangedCalled);
+  AssertEquals('Old Parent childcount is 0', 0, FC.ChildCount);
+  AssertEquals('Parent childcount is 1', 1, FC2.ChildCount);
+  AssertSame('Parent first child is OK', FChild, FC2.Child[0]);
 end;
 end;
 
 
 procedure TTestReportChildren.TestSetParent4;
 procedure TTestReportChildren.TestSetParent4;
@@ -1582,7 +1579,9 @@ begin
   AssertEquals('Parent childcount is 1', 1, FC.ChildCount);
   AssertEquals('Parent childcount is 1', 1, FC.ChildCount);
   AssertSame('Parent first child is OK', FChild, FC.Child[0]);
   AssertSame('Parent first child is OK', FChild, FC.Child[0]);
   FreeAndNil(FC);
   FreeAndNil(FC);
-  AssertNull('Child parent was removed when parent is freed', FChild.Parent);
+  //FChild is freed due to free of parent
+  //AssertNull('Child parent was removed when parent is freed', FChild.Parent);
+  FChild := Nil;
 end;
 end;
 
 
 procedure TTestReportChildren.TestSetParent6;
 procedure TTestReportChildren.TestSetParent6;
@@ -2188,21 +2187,20 @@ var
   B: TFPReportCustomBand;
   B: TFPReportCustomBand;
   P: TMyFPReportPage;
   P: TMyFPReportPage;
 begin
 begin
-  B := TFPReportCustomBand.Create(nil);
+  P := TMyFPReportPage.Create(nil);
   try
   try
-    P := TMyFPReportPage.Create(nil);
+    B := TFPReportCustomBand.Create(nil);
     try
     try
       B.Parent := P;
       B.Parent := P;
       AssertSame('Parent stored correctly', P, B.Page);
       AssertSame('Parent stored correctly', P, B.Page);
       AssertEquals('Bandcount correct', 1, P.BandCount);
       AssertEquals('Bandcount correct', 1, P.BandCount);
       AssertSame('Bands[0] correct', B, P.Bands[0]);
       AssertSame('Bands[0] correct', B, P.Bands[0]);
     finally
     finally
-      P.Free;
+      B.Free;
     end;
     end;
-    AssertNull('Band notified that page is gone', B.Parent);
-    AssertNull('Band notified that page is gone', B.Page);
+    AssertEquals('Page notified that Band is gone', 0, P.BandCount);
   finally
   finally
-    B.Free;
+    P.Free;
   end;
   end;
 end;
 end;
 
 
@@ -2775,6 +2773,7 @@ end;
 
 
 procedure TTestCustomReport.TestBeginReportEvent;
 procedure TTestCustomReport.TestBeginReportEvent;
 begin
 begin
+  TMyFPReportPage.Create(Report); // add at least one page
   Report.OnBeginReport := @HandleOnBeginReport;
   Report.OnBeginReport := @HandleOnBeginReport;
   AssertEquals('Failed on 1', 0, FBeginReportCount);
   AssertEquals('Failed on 1', 0, FBeginReportCount);
   Report.RunReport;
   Report.RunReport;
@@ -2784,6 +2783,7 @@ end;
 
 
 procedure TTestCustomReport.TestEndReportEvent;
 procedure TTestCustomReport.TestEndReportEvent;
 begin
 begin
+  TMyFPReportPage.Create(Report); // add at least one page
   Report.OnEndReport := @HandleOnEndReport;
   Report.OnEndReport := @HandleOnEndReport;
   AssertEquals('Failed on 1', 0, FEndReportCount);
   AssertEquals('Failed on 1', 0, FEndReportCount);
   Report.RunReport;
   Report.RunReport;
@@ -2814,9 +2814,10 @@ begin
   AssertEquals('Failed on 3', 0, TMyFPReportPage(Report.Pages[2]).FPrepareObjectsCalled);
   AssertEquals('Failed on 3', 0, TMyFPReportPage(Report.Pages[2]).FPrepareObjectsCalled);
 
 
   Report.RunReport;
   Report.RunReport;
-  AssertEquals('Failed on 4', 1, TMyFPReportPage(Report.Pages[0]).FPrepareObjectsCalled);
-  AssertEquals('Failed on 5', 1, TMyFPReportPage(Report.Pages[1]).FPrepareObjectsCalled);
-  AssertEquals('Failed on 6', 1, TMyFPReportPage(Report.Pages[2]).FPrepareObjectsCalled);
+  // due to Re-interpret of Page.Data, page is prepared per record (r38906)
+  AssertEquals('Failed on 4', 2, TMyFPReportPage(Report.Pages[0]).FPrepareObjectsCalled);
+  AssertEquals('Failed on 5', 2, TMyFPReportPage(Report.Pages[1]).FPrepareObjectsCalled);
+  AssertEquals('Failed on 6', 2, TMyFPReportPage(Report.Pages[2]).FPrepareObjectsCalled);
 end;
 end;
 
 
 procedure TTestCustomReport.TestBandPrepareObjects;
 procedure TTestCustomReport.TestBandPrepareObjects;
@@ -2870,7 +2871,8 @@ begin
   AssertEquals('Failed on 1', 0, Report.RTObjects.Count);
   AssertEquals('Failed on 1', 0, Report.RTObjects.Count);
 
 
   Report.RunReport;
   Report.RunReport;
-  AssertEquals('Failed on 2', 3, Report.RTObjects.Count);
+  // due to Re-interpret of Page.Data, page is prepared per record (r38906)
+  AssertEquals('Failed on 2', 6, Report.RTObjects.Count);
 end;
 end;
 
 
 procedure TTestCustomReport.TestRTObjects2;
 procedure TTestCustomReport.TestRTObjects2;
@@ -2895,7 +2897,8 @@ begin
 
 
   AssertEquals('Failed on 1', 0, Report.RTObjects.Count);
   AssertEquals('Failed on 1', 0, Report.RTObjects.Count);
   Report.RunReport;
   Report.RunReport;
-  AssertEquals('Failed on 2', 1, Report.RTObjects.Count); // runtime objects adhere to same hierarchy as design time
+  // due to Re-interpret of Page.Data, page is prepared per record (r38906)
+  AssertEquals('Failed on 2', 2, Report.RTObjects.Count); // runtime objects adhere to same hierarchy as design time
   AssertEquals('Failed on 3', 'TFPReportCustomPage', TObject(Report.RTObjects[0]).ClassName);
   AssertEquals('Failed on 3', 'TFPReportCustomPage', TObject(Report.RTObjects[0]).ClassName);
   rtPage := TFPReportCustomPage(Report.RTObjects[0]);
   rtPage := TFPReportCustomPage(Report.RTObjects[0]);
   AssertEquals('Failed on 4', 1, rtPage.ChildCount);
   AssertEquals('Failed on 4', 1, rtPage.ChildCount);
@@ -3104,7 +3107,7 @@ begin
   Memo := TFPReportMemo.Create(DataBand);
   Memo := TFPReportMemo.Create(DataBand);
   Memo.Layout.Top := 5;
   Memo.Layout.Top := 5;
   Memo.Layout.Left := 10;
   Memo.Layout.Left := 10;
-  Memo.Text := '[recno]';
+  Memo.Text := '[recno('''')]';
 
 
   AssertEquals('Failed on 1', 0, Report.RTObjects.Count);
   AssertEquals('Failed on 1', 0, Report.RTObjects.Count);
   Report.RunReport;
   Report.RunReport;
@@ -3404,6 +3407,7 @@ begin
   FMemo.UseParentFont := False;
   FMemo.UseParentFont := False;
   FMemo.Font.Name := 'Calibri';
   FMemo.Font.Name := 'Calibri';
   FMemo.StretchMode := smActualHeight;
   FMemo.StretchMode := smActualHeight;
+  FMemo.WordOverflow := woOverflow;
   TMemoFriend(FMemo).CreateRTLayout;
   TMemoFriend(FMemo).CreateRTLayout;
   TMemoFriend(FMemo).RecalcLayout;
   TMemoFriend(FMemo).RecalcLayout;
   AssertEquals('Failed on 2', 2, FMemo.TextLines.Count);
   AssertEquals('Failed on 2', 2, FMemo.TextLines.Count);

+ 313 - 0
packages/fcl-report/test/tcreportgenerator.pas

@@ -0,0 +1,313 @@
+unit tcreportgenerator;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+  Classes,
+  SysUtils,
+  fpcunit,
+  testregistry,
+  fpreport,
+  udapp,
+  fpTTF,
+  fpjson,
+  {demos}
+  rptsimplelist,
+  rptexpressions,
+  rptgrouping,
+  rptgrouping2,
+  rptframes,
+  rptimages,
+  rptttf,
+  rptshapes,
+  rptdataset,
+  rptcolumns,
+  rptmasterdetail,
+  rptjson,
+  rptcontnr,
+  rptnestedgroups,
+  rptBarcode,
+  rptQRcode;
+
+type
+
+  { TTestDemos }
+
+  TTestDemos = class(TTestCase)
+  private
+    FFilePath: String;
+    procedure SaveJSON(pFileName: String; pJSON: TJSONData);
+  protected
+    procedure SetUp; override;
+    procedure TearDown; override;
+    procedure TestDemo(pName: String; pDemoAppClass: TReportDemoAppClass);
+  published
+    procedure SimpleList;
+    procedure ExpressionDemo;
+    procedure GroupingDemo;
+    procedure Grouping2Demo;
+    procedure FramesDemo;
+    procedure ImagesDemo;
+    procedure TTFDemo;
+    procedure ShapesDemo;
+    procedure DatasetDemo;
+    procedure ColumnsDemo;
+    procedure MasterDetailDemo;
+    procedure JSONDemo;
+    procedure CollectionDemo;
+    procedure ObjectListDemo;
+    procedure TestNestedGroupDemo;
+    procedure BarcodeDemo;
+    procedure QRCodeDemo;
+  end;
+
+
+implementation
+
+uses
+  fpjsonreport,
+  jsonscanner,
+  jsonparser;
+
+{ TTestDemos }
+
+procedure TTestDemos.SaveJSON(pFileName: String; pJSON: TJSONData);
+var
+  S: TFileStream;
+  J: TJSONStringType;
+begin
+  S:=TFileStream.Create(pFileName,fmCreate);
+  try
+    J:=pJSON.FormatJSON;
+    S.WriteBuffer(J[1],Length(J));
+  finally
+    S.Free;
+  end;
+end;
+
+procedure TTestDemos.SetUp;
+begin
+  inherited SetUp;
+  FFilePath:=ExtractFilePath(ParamStr(0));
+  if not ForceDirectories(FFilePath+'rendered') then
+     Fail('Could not create directory for rendered JSON');
+  gTTFontCache.Clear;
+  gTTFontCache.SearchPath.Add(FFilePath+'fonts/');
+  gTTFontCache.SearchPath.Add(FFilePath+'../demos/fonts/');
+{$IFDEF UNIX}
+  gTTFontCache.SearchPath.Add(GetUserDir + '.fonts/');
+  gTTFontCache.SearchPath.Add('/usr/share/fonts/truetype/ubuntu-font-family/');
+  gTTFontCache.SearchPath.Add('/usr/share/fonts/truetype/ubuntu/');
+  gTTFontCache.SearchPath.Add('/usr/share/fonts/truetype/dejavu/');
+{$ENDIF}
+  // ask to generate the font cache
+  gTTFontCache.BuildFontCache;
+end;
+
+procedure TTestDemos.TearDown;
+begin
+  inherited TearDown;
+end;
+
+procedure TTestDemos.TestDemo(pName: String; pDemoAppClass: TReportDemoAppClass);
+var
+  lApp: TReportDemoApp;
+  lSetJSON: TJSONData;
+  lActualJSON: TJSONObject;
+  S: TFileStream;
+  P: TJSONParser;
+  J: TJSONStringType;
+  lEqual: Boolean;
+  lSetFile, lActualFile: String;
+
+begin
+  lSetFile:=FFilePath+'rendered'+PathDelim+pName+'.set.json';
+  lActualFile:=FFilePath+'rendered'+PathDelim+pName+'.actual.json';
+
+
+  lApp:=pDemoAppClass.Create(Nil);
+  lActualJSON := TJSONObject.Create;
+  try
+    // delete old actual
+    DeleteFile(lActualFile);
+
+    // create Report
+    lApp.TestInit;
+    // run first time
+    lApp.rpt.RunReport;
+    lApp.rpt.SaveRenderToJSON(lActualJSON);
+
+    // delete DateCreated
+    lActualJSON.GetPath('Report.DateCreated').AsString := '';
+
+    //SaveJSON(lSetFile, lActualJSON); // uncomment for regeneration after changes
+    if Not FileExists(lSetFile) then
+       begin
+       SaveJSON(lSetFile, lActualJSON);
+       Ignore('No previous test result available, saved result for reference');
+       end;
+    // load set report
+    S:=TFileStream.Create(lSetFile,fmOpenRead);
+    try
+      P:=TJSONParser.Create(S, []);
+      try
+        lSetJSON:=TJSONObject(P.Parse);
+
+        // compare reports
+        lEqual := lSetJSON.AsJSON = lActualJSON.AsJSON;
+        if not lEqual then
+          SaveJSON(lActualFile, lActualJSON);
+        AssertTrue('equal renders', lEqual);
+
+        // run a second time
+        lApp.rpt.RunReport;
+        lActualJSON.Clear;
+        lApp.rpt.SaveRenderToJSON(lActualJSON);
+
+        // delete DateCreated
+        lActualJSON.GetPath('Report.DateCreated').AsString := '';
+
+        // compare reports
+        lEqual := lSetJSON.AsJSON = lActualJSON.AsJSON;
+        if not lEqual then
+          SaveJSON(lActualFile, lActualJSON);
+        AssertTrue('equal second renders', lEqual);
+      finally
+        lSetJSON.Free;
+        P.Free;
+      end;
+    finally
+      S.Free;
+    end;
+  finally
+    lActualJSON.Free;
+    lApp.Free;
+  end;
+end;
+
+procedure TTestDemos.SimpleList;
+begin
+  TestDemo('simplelist', TSimpleListDemo);
+end;
+
+procedure TTestDemos.ExpressionDemo;
+begin
+  TestDemo('expression', TExpressionsDemo);
+end;
+
+procedure TTestDemos.GroupingDemo;
+begin
+  TestDemo('grouping', TGroupingDemo);
+end;
+
+procedure TTestDemos.Grouping2Demo;
+begin
+  TestDemo('grouping2', TGrouping2Demo);
+end;
+
+procedure TTestDemos.FramesDemo;
+begin
+  TestDemo('frames', TFramesDemo);
+end;
+
+procedure TTestDemos.ImagesDemo;
+var
+  cd: String;
+begin
+  cd := GetCurrentDir;
+  SetCurrentDir(cd+PathDelim+'..'+PathDelim+'demos');
+  try
+    TestDemo('images', TImagesDemo);
+  finally
+    SetCurrentDir(cd);
+  end;
+end;
+
+procedure TTestDemos.TTFDemo;
+var
+  cd: String;
+begin
+  cd := GetCurrentDir;
+  SetCurrentDir(cd+PathDelim+'..'+PathDelim+'demos');
+  try
+    TestDemo('ttf', TTTFDemo);
+  finally
+    SetCurrentDir(cd);
+  end;
+end;
+
+procedure TTestDemos.ShapesDemo;
+begin
+  TestDemo('shapes', TShapesDemo);
+end;
+
+procedure TTestDemos.DatasetDemo;
+var
+  cd: String;
+begin
+  cd := GetCurrentDir;
+  SetCurrentDir(cd+PathDelim+'..'+PathDelim+'demos');
+  try
+    TestDemo('dataset', TDatasetDemo);
+  finally
+    SetCurrentDir(cd);
+  end;
+end;
+
+procedure TTestDemos.ColumnsDemo;
+begin
+  TestDemo('columns', TColumnsDemo)
+end;
+
+procedure TTestDemos.MasterDetailDemo;
+begin
+  TestDemo('masterdetail', TMasterDetailDemo);
+end;
+
+procedure TTestDemos.JSONDemo;
+var
+  cd: String;
+begin
+  cd := GetCurrentDir;
+  SetCurrentDir(cd+PathDelim+'..'+PathDelim+'demos');
+  try
+    TestDemo('json', TJSONDemo);
+  finally
+    SetCurrentDir(cd);
+  end;
+end;
+
+procedure TTestDemos.CollectionDemo;
+begin
+  TestDemo('collection', TCollectionDemo);
+end;
+
+procedure TTestDemos.ObjectListDemo;
+begin
+  TestDemo('objectlist', TObjectListDemo);
+end;
+
+procedure TTestDemos.BarcodeDemo;
+begin
+  TestDemo('barcode', TBarcodeDemo);
+end;
+
+procedure TTestDemos.QRCodeDemo;
+begin
+  TestDemo('qrcode', TQRCodeDemo);
+end;
+
+procedure TTestDemos.TestNestedGroupDemo;
+begin
+  TestDemo('nestedgroups', TNestedGroupsDemo);
+end;
+
+initialization
+  RegisterTests(
+    [TTestDemos
+    ]);
+
+end.
+

+ 1 - 1
packages/fcl-report/test/testfpreport.lpi

@@ -75,7 +75,7 @@
       <Filename Value="testfpreport"/>
       <Filename Value="testfpreport"/>
     </Target>
     </Target>
     <SearchPaths>
     <SearchPaths>
-      <OtherUnitFiles Value="../src"/>
+      <OtherUnitFiles Value="../src;../demos;../demos/polygon"/>
       <UnitOutputDirectory Value="units"/>
       <UnitOutputDirectory Value="units"/>
     </SearchPaths>
     </SearchPaths>
     <Parsing>
     <Parsing>