From 8114a56fa4b8cfe1f7df74b82eb3f0bb3a9a4ec5 Mon Sep 17 00:00:00 2001
From: Houtan Bastani <houtan@dynare.org>
Date: Tue, 3 Sep 2019 16:11:16 +0200
Subject: [PATCH] template for making a report with one table

---
 macros/make_report_table.m           |  47 +++++++++++
 src/@report/write.m                  |   2 +-
 src/@report_data/hasLabel.m          |  33 ++++++++
 src/@report_data/nCols.m             |  33 ++++++++
 src/@report_data/report_data.m       |  28 +++++--
 src/@report_data/writeDataForTable.m | 109 +++++++++++---------------
 src/@report_table/report_table.m     |  41 ++++++----
 src/@report_table/writeTableFile.m   | 113 ++++++++++++++++-----------
 src/@section/write.m                 |  25 +++---
 test/createReport.m                  |  10 +--
 10 files changed, 287 insertions(+), 154 deletions(-)
 create mode 100644 macros/make_report_table.m
 create mode 100644 src/@report_data/hasLabel.m
 create mode 100644 src/@report_data/nCols.m

diff --git a/macros/make_report_table.m b/macros/make_report_table.m
new file mode 100644
index 0000000..b36d57e
--- /dev/null
+++ b/macros/make_report_table.m
@@ -0,0 +1,47 @@
+function make_report_table(M_, options_, title, table_dir, table_root_name, column_names, row_names, values, val_precis)
+%
+
+% Copyright (C) 2015-2019 Dynare Team
+%
+% This file is part of Dynare.
+%
+% Dynare is free software: you can redistribute it and/or modify
+% it under the terms of the GNU General Public License as published by
+% the Free Software Foundation, either version 3 of the License, or
+% (at your option) any later version.
+%
+% Dynare is distributed in the hope that it will be useful,
+% but WITHOUT ANY WARRANTY; without even the implied warranty of
+% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+% GNU General Public License for more details.
+%
+% You should have received a copy of the GNU General Public License
+% along with Dynare.  If not, see <http://www.gnu.org/licenses/>.
+
+if options_.noprint
+    return
+end
+
+nv = rows(values);
+if ~isempty(row_names)
+    assert(length(row_names) == nv);
+end
+
+rep = report('directory', CheckPath('Output', M_.dname));
+rep.addPage();
+rep.addSection();
+rep.addTable('title', title, ...
+    'column_names', column_names, ...
+    'precision', val_precis, ...
+    'tableDirName', table_dir, ...
+    'tableName', [M_.fname '_' table_root_name '.tex']);
+for i = 1:nv
+    if isempty(row_names)
+        rep.addData('label', '', 'data', values(i, :));
+    else
+        rep.addData('label', row_names{i}, 'data', values(i, :));
+    end
+end
+rep.write();
+rep.compile();
+end
diff --git a/src/@report/write.m b/src/@report/write.m
index 0707f93..fc7caa1 100644
--- a/src/@report/write.m
+++ b/src/@report/write.m
@@ -44,7 +44,7 @@ if strcmpi(o.orientation, 'landscape')
     fprintf(fid, ',landscape');
 end
 fprintf(fid, ']{geometry}\n');
-fprintf(fid, '\\usepackage{pdflscape, booktabs, pgfplots, colortbl, adjustbox, multicol}\n');
+fprintf(fid, '\\usepackage{pdflscape, booktabs, pgfplots, colortbl, adjustbox, multicol, caption}\n');
 fprintf(fid, '\\pgfplotsset{compat=1.5.1}');
 fprintf(fid, ['\\makeatletter\n' ...
               '\\def\\blfootnote{\\gdef\\@thefnmark{}\\@footnotetext}\n' ...
diff --git a/src/@report_data/hasLabel.m b/src/@report_data/hasLabel.m
new file mode 100644
index 0000000..7db1a09
--- /dev/null
+++ b/src/@report_data/hasLabel.m
@@ -0,0 +1,33 @@
+function tf = hasLabel(o)
+%function tf = hasLabel(o)
+% Returns true if the label is nonempty, false otherwise
+%
+% INPUTS
+%   o            [report_data]      report_data object
+%
+%
+% OUTPUTS
+%   tf           [boolean]          label empty status
+%
+% SPECIAL REQUIREMENTS
+%   none
+
+% Copyright (C) 2019 Dynare Team
+%
+% This file is part of Dynare.
+%
+% Dynare is free software: you can redistribute it and/or modify
+% it under the terms of the GNU General Public License as published by
+% the Free Software Foundation, either version 3 of the License, or
+% (at your option) any later version.
+%
+% Dynare is distributed in the hope that it will be useful,
+% but WITHOUT ANY WARRANTY; without even the implied warranty of
+% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+% GNU General Public License for more details.
+%
+% You should have received a copy of the GNU General Public License
+% along with Dynare.  If not, see <http://www.gnu.org/licenses/>.
+
+tf = ~isempty(o.label);
+end
diff --git a/src/@report_data/nCols.m b/src/@report_data/nCols.m
new file mode 100644
index 0000000..a7b753b
--- /dev/null
+++ b/src/@report_data/nCols.m
@@ -0,0 +1,33 @@
+function n = nCols(o)
+%function n = nCols(o)
+% Returns the number of columns of data
+%
+% INPUTS
+%   o            [report_data]      report_data object
+%
+%
+% OUTPUTS
+%   n            [integer]          number of columns of data
+%
+% SPECIAL REQUIREMENTS
+%   none
+
+% Copyright (C) 2019 Dynare Team
+%
+% This file is part of Dynare.
+%
+% Dynare is free software: you can redistribute it and/or modify
+% it under the terms of the GNU General Public License as published by
+% the Free Software Foundation, either version 3 of the License, or
+% (at your option) any later version.
+%
+% Dynare is distributed in the hope that it will be useful,
+% but WITHOUT ANY WARRANTY; without even the implied warranty of
+% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+% GNU General Public License for more details.
+%
+% You should have received a copy of the GNU General Public License
+% along with Dynare.  If not, see <http://www.gnu.org/licenses/>.
+
+n = length(o.data);
+end
diff --git a/src/@report_data/report_data.m b/src/@report_data/report_data.m
index af0e3d8..9ef5f22 100644
--- a/src/@report_data/report_data.m
+++ b/src/@report_data/report_data.m
@@ -18,14 +18,15 @@ classdef report_data < handle
     % You should have received a copy of the GNU General Public License
     % along with Dynare.  If not, see <http://www.gnu.org/licenses/>.
     properties (SetAccess = private)
-        data = ''
-        tableSubSectionHeader = ''
-        tableAlignRight = false
-        tableRowColor = 'white'
-        tableRowIndent = 0
-        tableNaNSymb = 'NaN'
-        tablePrecision = ''
-        zeroTol = 1e-6
+        data = ''                  % The data for the table. Default: none.
+        label = ''                 % Row label for data. Default: none.
+        tableSubSectionHeader = '' % A header for a subsection of the table. No data will be associated with it. It is equivalent to adding an empty series with a name. Default: ''
+        tableAlignRight = false    % Whether or not to align the series name to the right of the cell. Default: false.
+        tableRowColor = 'white'    % The color that you want the row to be. Predefined values include LightCyan and Gray. Default: white.
+        tableRowIndent = 0         % The number of times to indent the name of the series in the table. Used to create subgroups of series. Default: 0.
+        tableNaNSymb = 'NaN'       % Replace NaN values with the text in this option. Default: NaN.
+        tablePrecision = ''        % The number of decimal places to report in the table data. Default: the value set by precision.
+        zeroTol = 1e-6             % The zero tolerance. Anything smaller than zeroTol and larger than -zeroTol will be set to zero before being graphed or written to the table. Default: 1e-6.
     end
     methods
         function o = report_data(varargin)
@@ -69,9 +70,20 @@ classdef report_data < handle
                     error('@report_data.report_data: %s is not a recognized option.', pair{1});
                 end
             end
+            
+            assert(~isempty(o.tableSubSectionHeader) || (isnumeric(o.data) && isvector(o.data)), '@report_data.report_data: data must be a numeric vector');
+            assert(ischar(o.label), '@report_data.report_data: label must be a string');
+            assert(ischar(o.tableSubSectionHeader), '@report_data.report_data: tableSubSectionHeader must be a string');
+            assert(ischar(o.tableRowColor), '@report_data.report_data: tableRowColor must be a string');
+            assert(isint(o.tableRowIndent) && o.tableRowIndent >= 0, '@report_data.report_data: tableRowIndent must be an integer >= 0');
+            assert(islogical(o.tableAlignRight), '@report_data.report_data: tableAlignRight must be true or false');
+            assert(ischar(o.tableNaNSymb), '@report_data.report_data: tableNaNSymb must be a string');
+            assert(isempty(o.tablePrecision) || (isint(o.tablePrecision) && o.tablePrecision >= 0), '@report_data.report_data: tablePrecision must be a non-negative integer');
         end
     end
     methods (Access = ?report_table, Hidden = true)
+        n = nCols(o)
+        tf = hasLabel(o)
         writeDataForTable(o, fid, precision)
     end
 end
diff --git a/src/@report_data/writeDataForTable.m b/src/@report_data/writeDataForTable.m
index 1df33dc..93f2fd6 100644
--- a/src/@report_data/writeDataForTable.m
+++ b/src/@report_data/writeDataForTable.m
@@ -32,83 +32,62 @@ function writeDataForTable(o, fid, precision)
 % along with Dynare.  If not, see <http://www.gnu.org/licenses/>.
 
 %% Validate options provided by user
-assert(ischar(o.tableSubSectionHeader), '@report_data.writeDataForTable: tableSubSectionHeader must be a string');
-if isempty(o.tableSubSectionHeader)
-    assert(~isempty(o.data) && iscell(o.data), ...
-           '@report_data.writeDataForTable: must provide data as a cell');
-end
-
-assert(ischar(o.tableRowColor), '@report_data.writeDataForTable: tableRowColor must be a string');
-assert(isint(o.tableRowIndent) && o.tableRowIndent >= 0, ...
-       '@report_data.writeDataForTable: tableRowIndent must be an integer >= 0');
-assert(islogical(o.tableAlignRight), '@report_data.writeDataForTable: tableAlignRight must be true or false');
-assert(ischar(o.tableNaNSymb), '@report_data.writeDataForTable: tableNaNSymb must be a string');
-
 if ~isempty(o.tablePrecision)
-    assert(isint(o.tablePrecision) && o.tablePrecision >= 0, ...
-           '@report_data.writeDataForTable: tablePrecision must be a non-negative integer');
     precision = o.tablePrecision;
 end
 rounding = 10^precision;
 
 %% Write Output
 fprintf(fid, '%% Table Data (report_data)\n');
-nrows = length(o.data{1});
+
 ncols = length(o.data);
-for i = 1:nrows
-    if ~isempty(o.tableRowColor) && ~strcmpi(o.tableRowColor, 'white')
-        fprintf(fid, '\\rowcolor{%s}', o.tableRowColor);
-    else
-        fprintf(fid, '\\rowcolor{%s}', o.tableRowColor);
-    end
-    if ~isempty(o.tableSubSectionHeader)
-        fprintf(fid, '\\textbf{%s}', o.tableSubSectionHeader);
-        fprintf(fid, '%s', repmat(' &', 1, ncols-1));
-        fprintf(fid, '\\\\%%\n');
-        return
+
+if ~isempty(o.tableRowColor) && ~strcmpi(o.tableRowColor, 'white')
+    fprintf(fid, '\\rowcolor{%s}', o.tableRowColor);
+else
+    fprintf(fid, '\\rowcolor{%s}', o.tableRowColor);
+end
+if ~isempty(o.tableSubSectionHeader)
+    fprintf(fid, '\\textbf{%s}', o.tableSubSectionHeader);
+    fprintf(fid, '%s', repmat(' &', 1, ncols-1));
+    fprintf(fid, '\\\\%%\n');
+    return
+end
+if o.tableAlignRight
+    fprintf(fid, '\\multicolumn{1}{r}{');
+end
+if o.tableRowIndent == 0
+    fprintf(fid, '\\noindent ');
+else
+    for j=1:o.tableRowIndent
+        fprintf(fid,'\\indent ');
     end
-    if o.tableAlignRight
-        fprintf(fid, '\\multicolumn{1}{r}{');
+end
+if o.tableAlignRight
+    fprintf(fid, '}');
+end
+if ~isempty(o.label)
+    fprintf(fid, '$%s$ & ', o.label);
+end
+for j = 1:ncols
+    val = o.data(j);
+    if val < o.zeroTol && val > -o.zeroTol
+        val = 0;
+    elseif ~isnan(val)
+        % Use round half away from zero rounding
+        val = round(val*rounding)/rounding;
     end
-    if o.tableRowIndent == 0
-        fprintf(fid, '\\noindent ');
+    if isnan(val)
+        val = o.tableNaNSymb;
+        dataString = '%s';
     else
-        for j=1:o.tableRowIndent
-            fprintf(fid,'\\indent ');
-        end
-    end
-    if o.tableAlignRight
-        fprintf(fid, '}');
+        dataString = sprintf('%%.%df', precision);
     end
-    for j = 1:ncols
-        val = o.data{j}(i);
-        if iscell(val)
-            val = val{:};
-        end
-        if isnan(val)
-            val = o.tableNaNSymb;
-            dataString = '%s';
-        elseif isnumeric(val)
-            dataString = sprintf('%%.%df', precision);
-            if val < o.zeroTol && val > -o.zeroTol
-                val = 0;
-            end
-            % Use round half away from zero rounding
-            val = round(val*rounding)/rounding;
-            if isnan(val)
-                val = o.tableNaNSymb;
-                dataString = '%s';
-            end
-        else
-            dataString = '%s';
-            val = regexprep(val, '_', '\\_');
-        end
-        fprintf(fid, dataString, val);
-        if j ~= ncols
-            fprintf(fid, ' & ');
-        else
-            fprintf(fid, '\\\\%%\n');
-        end
+    fprintf(fid, dataString, val);
+    if j ~= ncols
+        fprintf(fid, ' & ');
+    else
+        fprintf(fid, '\\\\%%\n');
     end
 end
 end
diff --git a/src/@report_table/report_table.m b/src/@report_table/report_table.m
index 4b8fa17..90e9053 100644
--- a/src/@report_table/report_table.m
+++ b/src/@report_table/report_table.m
@@ -22,11 +22,7 @@ classdef report_table < handle
     end
     properties (Access = private)
         series = {}
-        table_data = {}
-        % Not documented
-        preamble = {''}
-        afterward = {''}
-        column_names = ''
+        table_data = {}               % The cell that provides the data for the table. Defalut: none
     end
     properties (SetAccess = private)
         tableDirName = 'tmpRepDir'    % The name of the folder in which to store this table. Default: tmpRepDir.
@@ -34,15 +30,18 @@ classdef report_table < handle
         title = {''}                  % Table Title. Default: none.
         titleFormat = ''              % A string representing the valid LATEX markup to use on title. The number of cell array entries must be equal to that of the title option if you do not want to use the default value for the title (and subtitles). Default: \large\bfseries.
         showHlines = false            % Whether or not to show horizontal lines separating the rows. Default: false.
-        showVlines = false            %
+        showVlines = false            % Whether or not to show vertical lines separating the columns. Default: false.
         vlineAfter = ''               % Show a vertical line after the specified date (or dates if a cell array of dates is passed). Default: empty.
         vlineAfterEndOfPeriod = false % Show a vertical line after the end of every period (i.e. after every year, after the fourth quarter, etc.). Default: false.
-        data = ''                     % The dseries that provides the data for the graph. Default: none.
+        data = ''                     % The dseries that provides the time series data for the table. Default: none.
         seriesToUse = ''              % The names of the series contained in the dseries provided to the data option. If empty, use all series provided to data option. Default: empty.
         range = {}                    % The date range of the data to be displayed. Default: all.
         precision = 1                 % The number of decimal places to report in the table data (rounding done via the round half away from zero method). Default: 1.
         writeCSV = false              % Whether or not to write a CSV file containing the data displayed in the table. The file will be saved in the directory specified by tableDirName with the same base name as specified by tableName with the ending .csv. Default: false.
         highlightRows = {''}          % A cell array containing the colors to use for row highlighting. See shadeColor for how to use colors with reports. Highlighting for a specific row can be overridden by using the tableRowColor option to addSeries. Default: empty.
+        column_names = ''             % The column names when using table_data. Default: none
+        preamble = {''}               % Text before table. Default: none
+        afterward = {''}              % Text after table. Default: none
     end
     methods (Access = ?section)
         function o = report_table(varargin)
@@ -114,8 +113,8 @@ classdef report_table < handle
             assert(isempty(o.range) || length(o.range) <=2 && allCellsAreDatesRange(o.range), ...
                 ['@report_table.report_table: range is specified as a dates range, e.g. ' ...
                 '''dates(''1999q1''):dates(''1999q3'')''.']);
-            assert(isempty(o.data) || isdseries(o.data), ...
-                '@report_table.report_table: data must be a dseries');
+            assert(isempty(o.data) || isdseries(o.data) || iscell(o.data), ...
+                '@report_table.report_table: data must be a dseries or a cell');
             assert(isempty(o.seriesToUse) || iscellstr(o.seriesToUse), ...
                 '@report_table.report_table: seriesToUse must be a cell array of string(s)');
             assert(isempty(o.vlineAfter) || allCellsAreDates(o.vlineAfter), ...
@@ -134,15 +133,27 @@ classdef report_table < handle
             assert(islogical(o.writeCSV), '@report_table.report_table: writeCSV must be either true or false');
             assert(iscellstr(o.highlightRows), '@report_table.report_table: highlightRowsmust be a cell string');
 
-            % using o.seriesToUse, create series objects and put them in o.series
+            % create individual series/table_data objects given the `data` option
             if ~isempty(o.data)
-                if isempty(o.seriesToUse)
-                    for i=1:o.data.vobs
-                        o.series{end+1} = report_series('data', o.data{o.data.name{i}});
+                if isdseries(o.data)
+                    if isempty(o.seriesToUse)
+                        for i=1:o.data.vobs
+                            o.series{end+1} = report_series('data', o.data{o.data.name{i}});
+                        end
+                    else
+                        for i=1:length(o.seriesToUse)
+                            o.series{end+1} = report_series('data', o.data{o.seriesToUse{i}});
+                        end
                     end
                 else
-                    for i=1:length(o.seriesToUse)
-                        o.series{end+1} = report_series('data', o.data{o.seriesToUse{i}});
+                    if isnumeric(o.data(1,1))
+                        labels = cell(rows(o.data), 1);
+                    else
+                        labels = o.data(:, 1);
+                        o.data = cell2mat(o.data(:, 2:end));
+                    end
+                    for i = 1:rows(o.data)
+                        o.table_data{end+1} = report_data('data', o.data(i, :), 'label', labels{i});
                     end
                 end
             end
diff --git a/src/@report_table/writeTableFile.m b/src/@report_table/writeTableFile.m
index bfc2a9b..4130c44 100644
--- a/src/@report_table/writeTableFile.m
+++ b/src/@report_table/writeTableFile.m
@@ -33,9 +33,7 @@ function tableName = writeTableFile(o, pg, sec, row, col, rep_dir)
 % You should have received a copy of the GNU General Public License
 % along with Dynare.  If not, see <http://www.gnu.org/licenses/>.
 
-ne = length(o.series);
-is_data_table = ~isempty(o.table_data);
-if ne == 0 && ~is_data_table
+if isempty(o.series) && isempty(o.table_data)
     warning('@report_table.write: no series to plot, returning');
     return
 end
@@ -55,9 +53,23 @@ if fid == -1
 end
 
 fprintf(fid, '%% Report_Table Object written %s\n', datestr(now));
-fprintf(fid, '\\begin{tabular}[t]{l}\n');
+fprintf(fid, '\\noindent\\begin{minipage}[tc]{\\textwidth}\n\\begin{center}');
+if ~isempty(o.title)
+    fprintf(fid, '\\captionof{table}{');
+    for i=1:length(o.title)
+        if ~isempty(o.title{i})
+            fprintf(fid, '%s %s', o.titleFormat{i}, o.title{i});
+            if i ~= length(o.title)
+                fprintf(fid, '\\\\\n');
+            end
+        end
+    end
+    fprintf(fid, '}\n');
+end
+fprintf(fid, '\\begin{tabular}{c}\n');
 fprintf(fid, '\\setlength{\\parindent}{6pt}\n');
 fprintf(fid, '\\setlength{\\tabcolsep}{4pt}\n');
+fprintf(fid, '\\maxsizebox{\\textwidth}{!}{%%\n');
 
 %number of left-hand columns, 1 until we allow the user to group data,
 % e.g.: GDP Europe
@@ -65,7 +77,7 @@ fprintf(fid, '\\setlength{\\tabcolsep}{4pt}\n');
 %         GDP Germany
 % this example would be two lh columns, with GDP Europe spanning both
 nlhc = 1;
-if ~is_data_table
+if isempty(o.table_data)
     fprintf(fid, '\\begin{tabular}{@{}l');
     if isempty(o.range)
         dates = getMaxRange(o.series);
@@ -105,15 +117,8 @@ if ~is_data_table
             fprintf(fid, '|');
         end
     end
-    nrhc = length(rhscols);
-    ncols = ndates+nlhc+nrhc;
+    ncols = ndates+nlhc+length(rhscols);
     fprintf(fid, '@{}}%%\n');
-    for i=1:length(o.title)
-        if ~isempty(o.title{i})
-            fprintf(fid, '\\multicolumn{%d}{c}{%s %s}\\\\\n', ...
-                ncols, o.titleFormat{i}, o.title{i});
-        end
-    end
     fprintf(fid, '\\toprule%%\n');
 
     % Column Headers
@@ -172,33 +177,15 @@ if ~is_data_table
             end
         end
     end
-else
-    fprintf(fid, '\\begin{tabular}{');
-    if o.showVlines
-        fprintf(fid, '|');
-    end
-    for i = 1:length(o.column_names)
-        if isempty(o.column_names{i})
-            fprintf(fid, 'l');
-        else
-            fprintf(fid, 'r');
-        end
-        if o.showVlines
-            fprintf(fid, '|');
-        end
-    end
-    fprintf(fid,'}');
-end
 
-% Write Report_Table Data
-if ~is_data_table
+    % Write Report_Table Data
     fprintf(fid, '\\\\[-2pt]%%\n');
     fprintf(fid, '\\hline%%\n');
     fprintf(fid, '%%\n');
     if o.writeCSV
         csvseries = dseries();
     end
-    for i=1:ne
+    for i=1:length(o.series)
         o.series{i}.writeSeriesForTable(fid, o.range, o.precision, ncols, o.highlightRows{mod(i,length(o.highlightRows))+1});
         if o.writeCSV
             if isempty(o.series{i}.tableSubSectionHeader)
@@ -216,25 +203,63 @@ if ~is_data_table
         csvseries.save(strrep(o.tableName, '.tex', ''), 'csv');
     end
 else
+    % Write Data Table
+    fprintf(fid, '\\begin{tabular}{');
+    ncols = o.table_data{1}.nCols();
+    if o.table_data{1}.hasLabel()
+        ncols = ncols + 1;
+    end
+    if o.showVlines
+        fprintf(fid, '|');
+    end
+    if o.table_data{1}.hasLabel()
+        fprintf(fid, 'l');
+    else
+        fprintf(fid, 'r');
+    end
+    if isempty(o.column_names)
+        fprintf(fid, repmat('r', 1, ncols-1));
+    else
+        for i = 2:ncols
+            keyboard
+            if isempty(o.column_names{i})
+                fprintf(fid, 'l');
+            else
+                fprintf(fid, 'r');
+            end
+            if o.showVlines
+                fprintf(fid, '|');
+            end
+        end
+    end
+    fprintf(fid,'}');
+    fprintf(fid, '\\toprule%%\n');
+
+    % Write Report_Table Data
     fprintf(fid, '%%\n');
     fprintf(fid, '\\hline%%\n');
     fprintf(fid, '%%\n');
-    for i = 1:length(o.column_names)
-        if ~isempty(o.column_names{i})
-            fprintf(fid, '%s', o.column_names{i});
-        end
-        if i ~= length(o.column_names)
-            fprintf(fid, ' & ');
+    if ~isempty(o.column_names)
+        for i = 1:ncols
+            if ~isempty(o.column_names{i})
+                fprintf(fid, '%s', o.column_names{i});
+            end
+            if i ~= ncols
+                fprintf(fid, ' & ');
+            end
         end
+        fprintf(fid, '\\\\[-2pt]%%\n');
+        fprintf(fid, '\\hline%%\n');
+    end
+    for i=1:length(o.table_data)
+        o.table_data{i}.writeDataForTable(fid, o.precision);
     end
-    fprintf(fid, '\\\\[-2pt]%%\n');
-    fprintf(fid, '\\hline%%\n');
-    o.table_data{1}.writeDataForTable(fid, o.precision);
 end
 fprintf(fid, '\\bottomrule\n');
-fprintf(fid, '\\end{tabular}\n');
-fprintf(fid, '\\end{tabular}\n');
+fprintf(fid, '\\end{tabular}}\n');
 fprintf(fid, '\\setlength{\\parindent}{0pt}\n \\par \\medskip\n\n');
+fprintf(fid, '\\end{tabular}\n');
+fprintf(fid, '\\end{center}\\end{minipage}\n');
 fprintf(fid, '%% End Report_Table Object\n');
 if fclose(fid) == -1
     error('@report_table.writeTableFile: closing %s\n', o.filename);
diff --git a/src/@section/write.m b/src/@section/write.m
index 4e68ac7..c93a75c 100644
--- a/src/@section/write.m
+++ b/src/@section/write.m
@@ -32,23 +32,18 @@ function write(o, fid, pg, sec, rep_dir)
 % You should have received a copy of the GNU General Public License
 % along with Dynare.  If not, see <http://www.gnu.org/licenses/>.
 
-fprintf(fid, '%% Section Object written %s\n', datestr(now));
 if isempty(o.elements)
-    warning(['reporting:section.write(): trying to print a section ' ...
-             'with no elements. Perhaps you forgot to overwrite the report object.']);
+    warning('@report:section.write(): trying to print a section with no elements.');
     return
 end
 
+fprintf(fid, '%% Section Object written %s\n', datestr(now));
+fprintf(fid, '\\noindent\\begin{minipage}[tc]{\\textwidth}\n');
 if ~isempty(o.height)
-    fprintf(fid, '\\setlength\\sectionheight{%s}%%\n', o.height);
-end
-fprintf(fid, '\\maxsizebox{\\linewidth}{');
-if isempty(o.height)
-    fprintf(fid, '!');
+    fprintf(fid, '\\maxsizebox{\\linewidth}{%s}{%%\n', o.height);
 else
-    fprintf(fid, '\\sectionheight');
+    fprintf(fid, '\\maxsizebox{\\linewidth}{!}{%%\n');
 end
-fprintf(fid, '}{%%\n');
 fprintf(fid, '\\begin{tabular}[t]{');
 for i=1:o.cols
     if ~isa(o.elements{1}, 'paragraph')
@@ -61,17 +56,14 @@ for i=1:o.cols
 end
 fprintf(fid, '}\n');
 
-ne = numElements(o);
 row = 1;
 col = 1;
-for i=1:ne
+for i=1:numElements(o)
     if isa(o.elements{i}, 'vspace')
         o.elements{i}.write(fid);
         fprintf(fid, '\\\\\n');
         if col ~= o.cols
-            fprintf(fid, '\\end{tabular}}\\\\\n');
-            fprintf(fid, '%% End Section Object\n\n');
-            return
+            break
         end
     else
         if isa(o.elements{i}, 'paragraph')
@@ -89,6 +81,7 @@ for i=1:ne
         end
     end
 end
-fprintf(fid, '\\end{tabular}}\\\\\n');
+fprintf(fid, '\\end{tabular}\n');
+fprintf(fid, '}\\end{minipage}\n');
 fprintf(fid, '%% End Section Object\n\n');
 end
diff --git a/test/createReport.m b/test/createReport.m
index c789f2a..bf05747 100644
--- a/test/createReport.m
+++ b/test/createReport.m
@@ -297,11 +297,11 @@ rep.addSeries('data', d, ...
                     'graphFanShadeColor', 'red', 'graphFanShadeOpacity', 20);
 
 % Page 26
-% rep.addPage('title', {'report\_data test'}, ...
-%     'titleFormat', {'\large\bfseries', '\large'});
-% rep.addSection('cols', 1);
-% rep.addTable('title', {'Table Data', 'subtitle 2'});
-% rep.addData('data', [repmat({'a'},10,1) num2cell(rand(10,6))]);
+rep.addPage('title', {'report\_data test'}, ...
+    'titleFormat', {'\large\bfseries', '\large'});
+rep.addSection();
+rep.addTable('title', {'Table Data', 'subtitle 2'}, ...
+    'data', [repmat({'a'},10,1) num2cell(rand(10,6))]);
 
 
 %% Write & Compile Report
-- 
GitLab