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 个评论
Chris
Chris 2022-5-24
I solved it but it wasn't an elegant solution. I used a template to write out many word documents whose filename corresponded to the page order. I then used an external program that combines PDFs from the command line to package it into a single PDF. There may be a better way to do it, this is what I came up with.

请先登录,再进行评论。

回答(2 个)

Sanchari
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!

dpb
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...

产品


版本

R2020a

Community Treasure Hunt

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

Start Hunting!

Translated by