How do I generate a multi-page word document using report generator
27 次查看(过去 30 天)
显示 更早的评论
I am trying to generate a multi page Word document using MS word templates, filling in holes etc.
I am able to generate a single page document as desired by adding holes in the MS word template. Is any way I can design my template and code so that I can loop and each iteration of the loop generates a new page in the word document, each time filling in the same holes with updated values?
2 个评论
Matías Écija Fernández
2022-5-17
编辑:Matías Écija Fernández
2022-5-17
Did you solve the problem? I have the same issue
回答(2 个)
Sanchari
2024-1-5
Hello Chris,
I understand that you want to generate a multi-page MS-Word document using the MATLAB Report Generator.
You can do so by filling in placeholders (holes) with updated values on each iteration of a loop. Assuming that you are using a Windows platform, you can use MATLAB's ‘actxserver’ to create an ActiveX server that interfaces with Microsoft Word, allowing you to control Word from MATLAB.
Here's a step-by-step approach to achieving this multi-page document using MATLAB Report Generator:
1. Design the Word Template:
- Create a Word document that will serve as your template.
- Insert placeholders where you want to insert the updated values.
- Placeholders can be simple text that you will search for and replace in your MATLAB code (e.g., <placeholder1>, <placeholder2>, etc.).
2. Write MATLAB Code:
- Start by creating a Word ActiveX server and opening your template document.
- Use a loop to iterate over the data you want to insert into the document.
- For each iteration, search for the placeholders and replace them with the new values.
- Insert a page break at the end of each iteration, except for the last one.
Please try this simplified example of MATLAB code that demonstrates the process:
% Define the path to your Word template
templatePath = 'C:\path\to\your\template.dotx';
outputPath = 'C:\path\to\your\output.docx';
% Start an ActiveX server for Word
wordApp = actxserver('Word.Application');
wordApp.Visible = true; % Set to true to see the Word application
% Open the template
document = wordApp.Documents.Open(templatePath);
% Assume 'data' is a struct array or cell array with the data for each page
for i = 1:length(data)
% If not the first page, create a new page by inserting a page break
if i ~= 1
wordApp.Selection.InsertBreak; % This inserts a page break
% Insert the template content again for a new page
wordApp.Selection.InsertFile(templatePath);
end
% Find and replace placeholders in the document
% Assuming 'placeholders' is a cell array of placeholder strings and
% 'data' contains the corresponding replacement values for each page
for j = 1:length(placeholders)
% Set the range for the Find operation
findRange = wordApp.ActiveDocument.Content;
findRange.Find.ClearFormatting;
findRange.Find.Text = placeholders{j};
findRange.Find.Replacement.ClearFormatting;
findRange.Find.Replacement.Text = data(i).(placeholders{j});
% Execute the Find and Replace operation
findRange.Find.Execute(Replace=wordApp.wdReplaceAll);
end
end
% Save the document
document.SaveAs2(outputPath);
% Close Word
document.Close;
wordApp.Quit;
% Release the COM object
delete(wordApp);
In this example, data is a cell array with the values to be inserted into the document. The placeholders’ <placeholder1>’, ‘<placeholder2>’, etc., are replaced with the actual values from the data array. The ‘InsertBreak(7)’ method is used to insert a page break. Please adjust the ‘findText.Text’ and ‘wordApp.Selection.TypeText’ lines to match your actual placeholders and data structure and update the file paths to your template and output document locations.
Hope this information is helpful to you!
1 个评论
dpb
2025-12-14,22:22
编辑:dpb
about 7 hours 前
@Sanchari - the above doesn't use the <MATLAB Report Generator> at all, but drops one into the bowels of MS Word directly.
dpb
about 6 hours 前
编辑:dpb
about 2 hours 前
@Chris, I know this is way too late to do you any good on the actual question, but figured it would be good content for somebody else to find searching for the same Question as did I.
I finally figured out how to do precisely the above, fill out the same template in multiple pages -- in the end, it's not difficult, only trying to ferret one's way through the documentation is a bear to figure out how as the examples are devoid of such real world issues and there's no finding the place where it is described, if it is, other than by essentially pure luck. I actually stumbled over it by using a google search AI response as a klew...
Anyways, that aside, the answer is to first make the template page a named "Quick Part" (in the Word "Insert" menu" tab) with a unique name identifying it so it can be identfied in the dom code. Then, the trick is you open the page as a new page in the document in the loop and Voila! the set of named holes starts over again for the new page. So, you do NOT first fill in the table in the document/report when open it; but open the page and fill it in and append to the report. Surprisingly, you do not need to deal with memory management or keeping copies in a cell array or something; that is handled transparently internally by ReportGenerator. When you're done, with each page, it is appended the document/report and when the report is done, it is closed.
A sample of mine doing just that follows--the "trick" is to not fill the report, but the page...
function endowmentReportBuilder(yr,reports)
% format Word document from report data structure
% reports is a cell array of individual report data structs
import mlreportgen.dom.*
workdir='c:\Users\...';
fnTemplate=fullfile(workdir,'\ReportTemplate.dotx');
fnReport=fullfile(workdir,'\Report.docx');
rpt=Document(fnReport,'docx',fnTemplate);
open(rpt);
for i=1:numel(reports)
report=reports{i}; % dereference cell array of reports
page=DocumentPart(rpt.Type,fnTemplate,"EndowmentReport");
open(page);
while(~strcmp(page.CurrentHoleId,"#end#"))
switch(page.CurrentHoleId) % obviously use your own template IDS here
case "ReportHeader"
donorfund=report.Name;
fund=report.Fund;
addReportHeaderTable(donorfund,fund,yr)
case "ActivityData"
donordata.contribs=report.Contributions;
donordata.expends=report.Purpose;
addReportActivityTable(donordata,yr)
case "SummaryData"
donordata.corpus=report.Corpus;
donordata.expends=report.Expend;
addReportSummaryTable(donordata,yr)
end
moveToNextHole(page);
end
append(rpt,page);
end
close(rpt);
rptview(rpt.OutputPath) % optional, or open from Word itself
return
function addReportHeaderTable(donorfund,fund,yr)
% build the header as a table instead of individual elements
dates=datetime(yr-1,7+[0 12],[1 0],'Format','MMMM d, uuuu');
reportdate=sprintf('%s through %s',dates);
reportfund=sprintf('Fund Number: %s',fund);
tableStyle = { Width("100%"), ...
Border("none"), ...
RowSep("none"), ...
ColSep("none") };
entriesStyle = {FontFamily("Calibri"), ...
FontSize("12pt"), ...
Color("#006747"), ...
Bold(true)};
bodyContent = {donorfund; ...
reportfund; ...
reportdate};
tableContent = bodyContent; % no header, footer here
t=Table(tableContent);
t.Style = tableStyle;
t.TableEntriesHAlign = "center";
t.TableEntriesStyle = [t.TableEntriesStyle, entriesStyle];
append(page,t);
end
function addReportActivityTable(donordata,yr)
% Annual activity table
contribs=donordata.contribs;
expends=donordata.expends;
headerStyle ={ Bold(true), ...
Border("single"), ...
};
tableStyle = { Width("80%"), ...
Border("single"), ...
RowSep("single"), ...
ColSep("none") ...
};
fontSize=10;
fontsizestr=string(fontSize)+"pt";
entriesStyle = {FontFamily("Calibri"), ...
FontSize(fontsizestr)};
headerContent=[{'Annual Activity'} compose('Fiscal Year %d',[yr-1 yr])];
bodyContent =[[{'Contributions'} cellstr(num2currency(contribs))];
[{'Expenditures to fund purpose'} cellstr(num2currency(expends))]];
%tableContent = [headerContent; bodyContent];
t= FormalTable(headerContent, bodyContent);
t.Style=tableStyle;
t.Header.Style=[t.Header.Style,headerStyle];
t.Header.RowSep="single";
t.Header.RowSepWidth="1pt";
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";
% adjust height
tHeight=round(1.25*fontSize,1);
tHeightStr=string(tHeight)+"pt";
r=t.Header.row(1);
r.Height=tHeightStr;
for i=1:t.Body.NRows
r=t.Body.row(i);
r.Height=tHeightStr;
end
append(page,t);
end
function addReportSummaryTable(donordata,yr)
% Annual summary table
corpus=donordata.corpus;
expendables=donordata.expends;
totals=corpus+expendables;
headerStyle ={ Bold(true), ...
Border("single"), ...
VAlign("bottom"), ...
};
tableStyle = { Width("80%"), ...
Border("single"), ...
RowSep("none"), ...
ColSep("none") ...
};
footerStyle = { Bold(true), ...
Border("single"), ...
ColSep("none") ...
};
fontSize=10;
fontsizestr=string(fontSize)+"pt";
entriesStyle = {FontFamily("Calibri"), ...
FontSize(fontsizestr)};
headerContent=[{'Fund Summary'} compose('Fiscal Year %d',[yr-1 yr])];
try
bodyContent =[[{'Endowment (principal)'} cellstr(num2currency(corpus))];
[{'Expendable (income and contributions'} cellstr(num2currency(expendables))]];
catch
disp(expendables)
end
footerContent = [{'Total fund balance'} cellstr(num2currency(totals))];
t= FormalTable(headerContent, bodyContent, footerContent);
t.Style=tableStyle;
t.Header.Style=[t.Header.Style,headerStyle];
t.Header.RowSep="single";
t.Header.RowSepWidth="1pt";
t.Footer.Style=[t.Footer.Style,footerStyle];
t.Footer.RowSep="single";
t.Footer.RowSepWidth="1pt";
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";
% adjust height
% the Word doc had extra blank line in the table so this just masks it
% I dunno if can manage to get it to go away by messing with Word
% paragraph format or not; haven't taken time to try. This approach
% just uses a hole to hold the table so all the formatting done
% programmatically instead of trying to fill in a formatted Word table.
tHeight=round(1.25*fontSize,1);
tHeightStr=string(tHeight)+"pt";
r=t.Header.row(1);
r.Height=tHeightStr;
for i=1:t.Body.NRows
r=t.Body.row(i);
r.Height=tHeightStr;
end
r=t.Footer.row(1);
r.Height=tHeightStr;
append(page,t);
end
end
My driver routine builds a cell array of struct that each contain the data for one page. The above is a little clunky in move that struct data to the "donor" struct; that's because I built the code first with fake data and haven't yet cleaned it up to do away with the internal struct and reference the input one...
0 个评论
另请参阅
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!