Report Generator Table Formatting

55 views (last 30 days)
dpb
dpb on 9 Dec 2025 at 16:59
Edited: dpb on 11 Dec 2025 at 17:00
I have the following function that works to add the table into a defined hole in an MS Word template; the content is all as expected excepting the formatting isn't quite as would like and have not been able to figure out how to get the last couple of details as desired.
Specifically, the
headerStyle ={ Bold(true), ...
};
...
t.Header.Style=headerStyle;
doesn't work to set the header to a bold font, nor does the similar code work for the footer.
Secondly, the entire table is outlined but the outline of the header and footer is missing...there are two rows in the table content section for which I do not want a row separator line; but do want the header and footer outlines. I don't see why that isn't what is instructed to do with
headerStyle ={ Bold(true), ...
Border("single"), ...
};
tableStyle = { Width("80%"), ...
Border("single"), ...
RowSep("none"), ...
ColSep("none") ...
};
footerStyle = { Bold(true), ...
Border("single"), ...
ColSep("none") ...
};
The full function follows...I didn't want to put the actual data out for public view so didn't attach the output document.
Hopefully, somebody understands this well enough to be able to know what magic incantations to take; found a formal table example in the doc from which this was copied, but none of the examples I could find ever don't show every row separator.
function addReportSummaryTable(donordata,yr)
% Annual summary table
corpus=donordata.corpus;
expendables=donordata.expends;
headerStyle ={ Bold(true), ...
};
tableStyle = { Width("80%"), ...
Border("single"), ...
RowSep("none"), ...
ColSep("none") ...
};
footerStyle = { Bold(true), ...
Border("single"), ...
ColSep("none") ...
};
entriesStyle = {FontFamily("Calibri"), ...
FontSize("11pt")};
headerContent=[{'Fund Summary'} compose('Fiscal Year %d',[yr-1 yr])];
bodyContent =[[{'Endowment (principal)'} cellstr(num2currency(corpus))];
[{'Expendable (income and contributions'} cellstr(num2currency(expendables))]];
totals=corpus+expendables;
footerContent = [{'Total fund balance'} cellstr(num2currency(totals))];
tableContent = [headerContent; bodyContent; footerContent];
t= FormalTable(tableContent);
t.Style=tableStyle;
t.Header.Style=headerStyle;
t.TableEntriesHAlign = "right";
t.TableEntriesStyle = [t.TableEntriesStyle, entriesStyle];
g=TableColSpecGroup();
s=TableColSpec();
s.Style={HAlign('left')};
g.ColSpecs=s;
t.ColSpecGroups=g;
t.HAlign = "center";
append(rpt, t);
end
  2 Comments
Kevin Holly
Kevin Holly on 9 Dec 2025 at 19:53
I just used some fake data. I will look into this. I need to take care of some things and then I will look at this soon.
donordata = table;
donordata.corpus = rand(2,1);
donordata.expends = rand(2,1);
yr = 2012;
addReportSummaryTable(donordata,yr)
function addReportSummaryTable(donordata,yr)
import mlreportgen.report.*
import mlreportgen.dom.*
rpt = Report('Annual Report', 'pdf');
open(rpt);
% Annual summary table
corpus=donordata.corpus;
expendables=donordata.expends;
headerStyle ={ Bold(true), ...
};
tableStyle = { Width("80%"), ...
Border("single"), ...
RowSep("none"), ...
ColSep("none") ...
};
footerStyle = { Bold(true), ...
Border("single"), ...
ColSep("none") ...
};
entriesStyle = {FontFamily("Calibri"), ...
FontSize("11pt")};
headerContent=[{'Fund Summary'} compose('Fiscal Year %d',[yr-1 yr])];
bodyContent =[[{'Endowment (principal)'} cellstr(num2currency(corpus))];
[{'Expendable (income and contributions'} cellstr(num2currency(expendables))]];
totals=corpus+expendables;
footerContent = [{'Total fund balance'} cellstr(num2currency(totals))];
tableContent = [headerContent; bodyContent; footerContent];
t= FormalTable(tableContent);
t.Style=tableStyle;
t.Header.Style=headerStyle;
t.TableEntriesHAlign = "right";
t.TableEntriesStyle = [t.TableEntriesStyle, entriesStyle];
g=TableColSpecGroup();
s=TableColSpec();
s.Style={HAlign('left')};
g.ColSpecs=s;
t.ColSpecGroups=g;
t.HAlign = "center";
append(rpt, t);
close(rpt);
end
function output = num2currency(number)
for ii = 1:length(number)
output{ii} = horzcat('$',num2str(number(ii)));
end
end
dpb
dpb on 9 Dec 2025 at 22:00
Edited: dpb on 9 Dec 2025 at 22:28
num2currency([-1234.34 50000 0]).'
ans = 3×1 string array
"($1,234.34)" "$50,000.00" "$ - "
function currency=num2currency(X,N)
% num2currency Convert numbers to character representation as USD currency
% C = num2currency(X) converts a the array(X) to its USD representation C
% with two decimal digits.
%
% C = num2currency(X,N) converts a the matrix(X) to its USD representation C
% rounded to N decimal digits of precision as numerical rounding by the
% runtime i/o library, not necessarily bankers rounding.
%
% At present, the currency symbol is fixed as USD and always shown.
nc = java.text.DecimalFormat.getCurrencyInstance; % java currency instance in local locale
if nargin>1
if nc.getMaximumFractionDigits~=N, nc.setMaximumFractionDigits(N); end
end
currency=arrayfun(@(v) string(nc.format(v)),X);
% fixup to return "$ -" for zero -- pad to max length of values in array
ix=find(X==0);
if isempty(ix), return, end
L=max(strlength(currency(X>0))); % max of nonnegative to align $
currency(ix)="$"+blanks(L-1);
for i=ix
currency{i}(end-2)='-';
end
end

Sign in to comment.

Accepted Answer

Kevin Holly
Kevin Holly on 9 Dec 2025 at 20:09
Edited: Kevin Holly on 9 Dec 2025 at 22:31
You need to define the Formaltable with 3 inputs. Header, Body, and Footer, respectfully
t= FormalTable(headerContent, bodyContent, footerContent);
donordata = table;
donordata.corpus = rand(2,1);
donordata.expends = rand(2,1);
yr = 2012;
addReportSummaryTable(donordata,yr)
function addReportSummaryTable(donordata,yr)
import mlreportgen.report.*
import mlreportgen.dom.*
rpt = Report('Annual Report', 'pdf');
open(rpt);
% Annual summary table
corpus=donordata.corpus;
expendables=donordata.expends;
headerStyle ={ Bold(true)};
tableStyle = { Width("80%"), ...
Border("single"), ...
RowSep("none"), ...
ColSep("none") ...
};
footerStyle = { Bold(true), ...
Border("single"), ...
ColSep("none") ...
};
entriesStyle = {FontFamily("Calibri"), ...
FontSize("11pt")};
headerContent=[{'Fund Summary'} compose('Fiscal Year %d',[yr-1 yr])];
bodyContent =[[{'Endowment (principal)'} cellstr(num2currency(corpus))];
[{'Expendable (income and contributions'} cellstr(num2currency(expendables))]];
totals=corpus+expendables;
footerContent = [{'Total fund balance'} cellstr(num2currency(totals))];
% tableContent = [headerContent; bodyContent; footerContent];
t= FormalTable(headerContent, bodyContent, footerContent);
t.Style=tableStyle;
t.Header.Style=[t.Header.Style,headerStyle];
t.Footer.Style=[t.Footer.Style,footerStyle];
t.TableEntriesHAlign = "right";
t.TableEntriesStyle = [t.TableEntriesStyle, entriesStyle];
g=TableColSpecGroup();
s=TableColSpec();
s.Style={HAlign('left')};
g.ColSpecs=s;
t.ColSpecGroups=g;
t.HAlign = "center";
append(rpt, t);
close(rpt);
end
function output = num2currency(number)
for ii = 1:length(number)
output{ii} = horzcat('$',num2str(number(ii)));
end
end

More Answers (1)

Taylor
Taylor on 9 Dec 2025 at 21:01
Your “header” and “footer” rows are actually part of the body. FormalTable(tableContent) treats the whole array as the body. The Header and Footer objects exist, but they’re empty—so styling t.Header.Style (or t.Footer.Style) doesn’t affect the first/last rows you assembled. Build the table with separate sections:t = FormalTable(headerContent, bodyContent, footerContent);
Bold needs to be applied to the entries, not just the section container. For a TableHeader/TableFooter, use TableEntriesStyle (e.g., {Bold(true)}) to style the text inside the cells; Header.Style/Footer.Style applies to the section, and formats that don’t apply there are ignored.
RowSep('none') at the table level suppresses separators everywhere—including the rules under the header and above the footer. Use section‑level RowSep so the body has no separator while the header/footer still draw theirs.
With ColSep('none') and no row separators, you won’t see a box around those sections unless you add borders to the header/footer entries themselves (entry‑level borders override table/section separators).
  11 Comments
Kevin Holly
Kevin Holly on 11 Dec 2025 at 14:24
@dpb So, I discover that the below code does work. It actually changes the vertical alignment - I verified in Word, although it did not look like it when I did it yesterday. The problem was that there was a space underneath the text due to line spacing being multiple instead of single. I was able to fix this by adding a OuterMargin Style
Previous:
for i = 1:t.Header.NRows
r = t.Header.row(i);
for j = 1:r.NEntries
e = r.Entries(j);
e.Style = [e.Style {VAlign("bottom")}]; % Ensure vertical alignment is set to bottom
end
end
Fixed linespace with OuterMargin:
for i = 1:t.Header.NRows
r = t.Header.row(i);
for j = 1:r.NEntries
e = r.Entries(j);
e.Style = [e.Style {VAlign("bottom"), OuterMargin("0pt", "0pt","0pt","0pt")}];
end
end
dpb
dpb about 2 hours ago
Edited: dpb 11 minutes ago
@Kevin Holly -- thanks a bunch! I hadn't realized about the line spacing; I'm not sure, but I may be able to fix that in the template with the paragraph style. My Word expertise beyond just typing into it and giving it to somebody else to clean up is near zero so I didn't think about there being something there causing the boundaries to be larger than the displayed text because of the line spacing.
Really appreciate you taking the time and effort to figure this out.

Sign in to comment.

Tags

Products


Release

R2024b

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!