Matlab Generate Report
MathWorksOfficialCreated by MathWorks
- Skill
- Tool integration
- Science and research
- BSD-2-Clause
- No issues found
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
The MATLAB Report Generation skill provides a unified framework for creating structured, professional documentation directly from MATLAB and Simulink. It leverages the Report API for high-level document structure and the DOM API for granular control over formatting, tables, and images. The system supports template-based styling, custom reporter classes for branded content, and specialized tools for documenting Simulink models, including block diagrams and execution order.
What it does
- Creates PDF, Word, and HTML documents from MATLAB data.
- Supports auto-numbered figures, tables, and chapters.
- Generates technical documentation for Simulink models.
- Enables custom branded templates with headers, footers, and page borders.
- Provides a scaffolded approach for building custom reporter classes.
- Supports multipage HTML output with navigation.
When to use it
- When you need to automate the creation of technical reports from MATLAB analysis.
- When documenting Simulink models, including system I/O and execution order.
- When you require consistent, branded document styling across multiple reports.
- When you need to integrate MATLAB plots and data into professional PDF or Word documents.
Advantages
- Maintains document structure and numbering automatically.
- Separates content logic from visual styling via templates.
- Provides specialized support for complex Simulink model documentation.
- Allows for highly customized, reusable report components.
Usage examples
- “Create a basic PDF report with a title page and a chapter containing a plot.”
- The agent provides a script using mlreportgen.report.Report, TitlePage, and Figure classes to generate a PDF with auto-numbered content.
- “How do I document a Simulink model's system I/O?”
- The agent guides you to use slreportgen.report.Report and the SystemIO reporter to extract and format model I/O data.
- “How can I create a custom report template?”
- The agent instructs you to use the customizeReporter method to scaffold a custom reporter class and its associated template files.
Install
Download the skill as a ZIP:
https://mp-staging.autogineer.com/agent-skills/matlab/matlab-generate-report/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip matlab-generate-report.zip -d .agents/skills
Documentation by MathWorks
MATLAB Report Generation
Generate structured, professional reports (PDF, Word, HTML) from MATLAB data and Simulink models.
When to Use
Creating PDF, Word, or HTML reports from MATLAB data
Building reports with chapters, title pages, tables of contents
Adding auto-numbered figures and tables to documents
Creating multipage HTML reports (separate HTML files with navigation)
Documenting Simulink models with block diagrams, I/O tables, execution order
Customizing report templates (styles, colors, fonts, headers, footers)
Creating custom reporter classes with template holes and dynamic content
Building multi-reporter packages with shared company styles
Creating table variant reporters with different header colors
Including HTML content in reports
When NOT to Use
Simple file export (CSV, Excel) — use
writetable,writematrixLive Scripts as reports — use MATLAB Live Editor export
Interactive report building — use Report Explorer GUI
Web views of Simulink models — use
slreportgen.webview.*directly
API Selection
Always use the Report API (mlreportgen.report.*) as the foundation. It provides the Report container and reporters (Figure, BaseTable, FormalImage, Chapter, TitlePage, etc.) that handle auto-numbering, captions, and document structure.
Use both APIs together. Use the Report API to create the report and add content for which reporters exist. Use the DOM API (mlreportgen.dom.*) for styled paragraphs, custom tables, inline formatting, HTML inclusion, and images without captions. DOM objects append directly to Report, Chapter, or Section alongside reporters.
Critical rule: Report API reporters (Figure, BaseTable, FormalImage) can ONLY be appended to Report, Chapter, or Section objects — never to DOM objects like TableEntry. Use DOM objects inside TableEntry.
Simulink reports: Use slreportgen.report.Report (not mlreportgen.report.Report) — it extends the base with model compilation support required by SystemIO, ExecutionOrder, and other Simulink reporters.
Multipage HTML: Use mlreportgen.dom.Document with "html-multipage" type + HTMLPage objects. This is the only case where you use the DOM Document instead of the Report API Report.
Core Workflow: Standard Report
TitlePage, TableOfContents, ListOfFigures, and ListOfTables are optional — include only when the task requires them. The minimal report is just Report + content + close.
import mlreportgen.report.*
import mlreportgen.dom.*
rpt = Report("MyReport","pdf");
append(rpt,TitlePage(Title="Report Title",Author="Team")); % optional
append(rpt,TableOfContents()); % optional
append(rpt,ListOfFigures()); % optional
append(rpt,ListOfTables()); % optional
ch = Chapter("Results");
append(ch,Paragraph("Analysis complete."));
% Auto-numbered table
data = FormalTable(["ID","Value"],[1 10; 2 20]);
bt = BaseTable(data);
bt.Title = "Measured values";
append(ch,bt);
% Auto-numbered figure
fig = figure("Visible","off");
plot([1 2],[10 20]);
rptFig = Figure(fig);
rptFig.Snapshot.Caption = "Value trend";
append(ch,rptFig);
close(fig);
append(rpt,ch);
close(rpt);
rptview(rpt);Core Workflow: Simulink Report
load_system(modelName);
% MUST use slreportgen.report.Report for Simulink content
rpt = slreportgen.report.Report(outputPath,"pdf");
append(rpt,mlreportgen.report.TitlePage("Title","Model Report"));
append(rpt,mlreportgen.report.TableOfContents());
ch = mlreportgen.report.Chapter("Diagrams");
% Root diagram — direct reporter
rootDiag = slreportgen.report.Diagram(modelName);
rootDiag.Snapshot.ScaleToFit = true;
append(ch,rootDiag);
% Subsystems — finder pattern (iterator for memory efficiency)
finder = slreportgen.finder.SystemDiagramFinder(modelName);
finder.IncludeRoot = false;
finder.SortType = "alphabetical";
while finder.hasNext()
result = finder.next();
sec = mlreportgen.report.Section("Title",result.Name);
append(sec,result);
append(ch,sec);
end
append(rpt,ch);
close(rpt);
rptview(rpt);Choosing Built-In vs Custom Reporters
Before writing code, analyze requirements against built-in reporter capabilities:
List all content/format requirements — title page fields, page borders, headers/footers, branded styling
Check built-in reporters. Built-in
TitlePagehas: Title, Subtitle, Author, Publisher, PubDate, Image. Built-inChapter/Sectionprovide headings, auto-numbering, and a Content hole.If built-in reporters meet all requirements, use them directly
If any requirement is unmet, create custom reporters:
Single custom reporter — see references/custom-reporters.md
Multiple reporters sharing a visual identity — see references/report-package.md
Common triggers for custom reporters:
Title page needs fields beyond the standard set (project name, facility, contract number)
Page borders, watermarks, or custom page layout
Page headers/footers with dynamic content (per-chapter images, tickers)
Branded table variants with different header colors
MATLAB-generated logos (geometric shapes drawn with plot/patch + exportgraphics)
Do NOT force-fit requirements into built-in properties (e.g., concatenating fields into Subtitle). Create a custom reporter instead.
Do NOT build custom title pages with raw DOM elements (Paragraphs, Images appended to a Chapter). Always use customizeReporter for template-based layout and format portability.
Decision Tree: What Reference Do I Need?
| I need to... | Reference |
|---|---|
| Build a standard report with chapters, figures, tables, verification | references/report-api-patterns.md |
| Work with DOM tables, HTML inclusion, multipage HTML, Number/NumberFormat | references/dom-api-patterns.md |
| Understand template file types, locate/inspect templates | references/template-system.md |
| Look up built-in template holes, styles, page layout (pre-computed) | references/template-reference.md |
| Create a custom reporter class (customizeReporter, holes, headers) | references/custom-reporters.md |
| Build a multi-reporter package with shared styles and buildTemplates | references/report-package.md |
| Generate Simulink model reports (finders, orientation, centering) | references/simulink-report.md |
| Look up all Simulink finders and reporters | references/finders-and-reporters.md |
| Iterate struct arrays into chapters with custom reporters | references/data-driven-report-pattern.md |
| Edit template styles (colors, fonts, headers, footers, TOC) | references/template-system.md |
Key Functions
| Class | Purpose | Package |
|---|---|---|
Report | Top-level report container | mlreportgen.report |
TitlePage | Title page with title, subtitle, author | mlreportgen.report |
TableOfContents | Auto-generated TOC | mlreportgen.report |
Chapter / Section | Document structure | mlreportgen.report |
Figure | Auto-numbered figure from MATLAB figure | mlreportgen.report |
FormalImage | Auto-numbered image from file with caption | mlreportgen.report |
BaseTable | Auto-numbered table wrapper (pass FormalTable) | mlreportgen.report |
FormalTable | Table with header/body/footer from cell arrays | mlreportgen.dom |
Template (3-arg) | Edit templates: Template(out,fmt,src) | mlreportgen.dom |
HTMLFile / HTML | Include HTML content in any output format | mlreportgen.dom |
HTMLPage | Page in multipage HTML document (R2024a+) | mlreportgen.dom |
Number | Numeric DOM object (use with NumberFormat) | mlreportgen.dom |
TemplateHole | Placeholder filled at report time | mlreportgen.dom |
Page / NumPages | Page number / total pages | mlreportgen.dom |
*.customizeReporter | Scaffold custom reporter + templates | mlreportgen.report |
getStyle / replaceStyle | Modify template styles | mlreportgen.dom |
slreportgen.report.Report | Report container for Simulink content | slreportgen.report |
slreportgen.report.Diagram | Diagram snapshot of a system | slreportgen.report |
slreportgen.finder.SystemDiagramFinder | Find system/subsystem diagrams | slreportgen.finder |
slreportgen.report.SystemIO | Report inputs/outputs (requires compilation) | slreportgen.report |
slreportgen.report.ExecutionOrder | Report block execution order | slreportgen.report |
slreportgen.report.LookupTable | Report lookup table data + plot | slreportgen.report |
rptview | Display report (updates DOCX fields) | function |
docview | Process Word documents (update fields, convert) | function |
Common Mistakes
| Mistake | Correct Approach |
|---|---|
Using mlreportgen.dom.Document as report container | Use mlreportgen.report.Report. Exception: multipage HTML requires Document with "html-multipage" |
Using mlreportgen.report.Report for Simulink content | Use slreportgen.report.Report — needed for model compilation |
Using Report with "html" for multipage output | Use mlreportgen.dom.Document with "html-multipage" + HTMLPage objects |
| Appending reporters to DOM objects (Paragraph, TableEntry) | Reporters can ONLY go in Report, Chapter, or Section. Use DOM objects inside TableEntry |
Putting raw doubles in FormalTable cells | Use Number with NumberFormat in Style: n = Number(42); n.Style = {NumberFormat("%.0f")}. Put n directly in cell arrays. See references/dom-api-patterns.md § Number and NumberFormat |
NumberFormat("$%.0f") or NumberFormat("%,.0f") | Only standard sprintf specifiers work (no $, no %,, no %d). For currency/units, wrap Number in Paragraph and prepend/append Text |
| Customizing only "default"/"even" page headers | Customize ALL page types: "first", "default", "even" — but differentiate content (see below) |
| Putting StyleRef (chapter title) in the first-page header | First page already displays the chapter heading — omit StyleRef from "first" header. Use it only in "default"/"even" for wayfinding |
Opening DOCX with winopen/open/start | Use rptview(rpt) or docview(file,"updatefields") — triggers field update |
Using ColSpec/ColSpecGroup for widths | Use TableColSpecGroup + TableColSpec with Width in Style |
Setting widths without ResizeToFitContents(false) | Add ResizeToFitContents(false) to table Style; set absolute ft.Width (not "100%") |
TemplateParagraphStyle.Formats = {Bold(true)} | Use array: Formats = [Bold(true),FontSize("12pt")] |
Template(path,"pdf") to edit existing template | 2-arg creates blank. Use 3-arg: Template(out,"pdf",src) |
| Same path for source and output in Template constructor | OPC cannot read/write simultaneously. Use different paths, then movefile |
Setting PDFPageLayout.PageSize.Orientation without rotate() | Call rotate(pl) — swaps Width/Height AND sets Orientation |
Using DOCXPageLayout without explicit Height and Width | DOCX does not auto-swap — set both explicitly |
Page numbers resetting after PDFPageLayout | Set pl.FirstPageNumber = '' (empty string) to continue numbering |
| Wrapping reporter in Paragraph to center it | Customize the reporter's template style via Template API |
Using RawText(html) for HTML in PDF | Use HTMLFile(path) or HTML(string) — works for any format |
Using Group in a Table cell array | Use nested cell arrays: Table({{img,caption},desc}) |
Passing MATLAB table() to BaseTable | Pass FormalTable(headers,body) — avoids MATLABTable wrapper |
e.Style = {BackgroundColor("yellow")} (overwrites existing styles) | Append: e.Style = [e.Style, {BackgroundColor("yellow")}] — preserves inherited styles |
Force-fitting fields into TitlePage.Subtitle | Create a custom title page reporter via customizeReporter |
| Writing custom reporter class from scratch | Use customizeReporter to scaffold — creates required binary templates |
Writing classdef before running customizeReporter | Scaffolding overwrites classdef files. Scaffold first, then edit |
Unqualified TitlePage.getClassFolder() in package | Use fully-qualified: mypkg.TitlePage.getClassFolder() |
getStyle(ss,name,type) for PDF/HTML | Type arg returns []. Use getStyle(ss,name) without type |
OuterMargin("12pt","6pt","0","0") for vertical spacing | Args are (left,right,top,bottom) — NOT CSS order. Use OuterMargin("0","0","12pt","6pt") for top/bottom spacing |
Using TemplateCharacterStyle | Does not exist. Use TemplateTextStyle for character/inline styles in PDF/HTML |
Unzipping .pdftx/.htmtx/.dotx to edit with fileread/regexprep | ALWAYS use DOM Template API — Template(out,fmt,src) + open + modify objects + close. Raw edits bypass framework validation |
find_system + DiagramFinder for discovery | Use SystemDiagramFinder with filter properties |
Diagram(result.Path) on ChartDiagramFinder results | Use getReporter(result) — chart paths are invalid for Diagram constructor |
find() + for loop on finders | Always use iterator: while finder.hasNext() / result = finder.next() |
BlockFinder with SearchDepth=inf expecting referenced models | BlockFinder does NOT traverse Model References. Use SystemDiagramFinder (IncludeReferencedModels=true) + per-system BlockFinder (SearchDepth=1) |
ExecutionOrder on virtual subsystem | Check get_param(obj,'IsSubsystemVirtual') first |
ExecutionOrder/SystemIO on Simulink Functions | Guard with slreportgen.utils.isSimulinkFunction(result.Object) |
Conventions
Always
close(rpt)after appending all content — generates the output fileAlways
rptviewordocviewto open DOCX — neverwinopenoropenUse
figure("Visible","off")for plots destined for reportsUse
exportgraphics(notprint) when saving figures to filesOne Chapter = one numbering scope — figures/tables numbered Chapter-Sequence (e.g., "Figure 2-1")
BaseTablewrapsFormalTable— passFormalTable(headers,body), not a MATLABtable()Cell-level styling: Use
entry(t,row,col)after construction, not duringTemplate styles use
[]arrays; inline.Styleproperties use{}Reporters in
@ClassNamedirectories — each needs its ownresources/templates/Scaffold ALL reporters before editing ANY —
customizeReportercreates binary templates + overwrites classdefFix unqualified class names after scaffolding — use package-qualified names
Customize all output formats — PDF/Word/HTML templates are independent
Simulink: Always
slreportgen.report.Report; alwaysDiagram.Snapshot.ScaleToFit = trueSimulink finders: Use
.Objecthandle (not path string) forget_param; useresult.Namefor titlesJSON is single source of truth for multi-reporter packages — never hardcode style values
Viewing and Processing Reports
rptview(rpt); % Open in system viewer (updates DOCX fields)
rptview("MyReport.docx","pdf"); % Convert DOCX to PDF via Word
docview("Report.docx","updatefields","closedoc","closeapp"); % Update without displayVerification: Running without error does NOT confirm correctness. After close(rpt):
Check
isfile(rpt.OutputPath)anddir(rpt.OutputPath).bytes > 1000For PDF: call
open(rpt), then setrpt.Document.RetainFO = truebefore appending content, search*_FO/*.fofilesFor DOCX: unzip and search
word/document.xmlAssert expected titles, headings, captions appear in output
See references/report-api-patterns.md for full verification patterns.
Required Toolbox
MATLAB Report Generator (verify with mcp__matlab__detect_matlab_toolboxes). Simulink Report Generator additionally required for Simulink model reports.
Copyright 2026 The MathWorks, Inc.
Files
- ATTRIBUTION.mdAdded by AutoGineer1.1 KiB
- LICENSELicence file added by AutoGineer1.4 KiB
- SKILL.md17.1 KiB
- manifest.yaml598 B
- references/custom-reporters.md39.1 KiB
- references/data-driven-report-pattern.md13.1 KiB
- references/dom-api-patterns.md31.0 KiB
- references/finders-and-reporters.md13.1 KiB
- references/report-api-patterns.md7.2 KiB
- references/report-package.md15.8 KiB
- references/simulink-report.md19.9 KiB
- references/template-reference.md38.1 KiB
- references/template-system.md30.9 KiB
references/custom-reporters.md
Custom Reporters
Create custom reporters that extend built-in Report API reporters with custom templates, holes, and page layout across PDF, Word, and HTML output formats.
Three Approaches
Template-based reporter — uses
customizeReporterscaffold with holes filled by properties orget<HoleId>()methodsDerived reporter — extends an existing reporter, overrides
getImplto add contentDOM-only reporter — overrides
getImplto return DOM objects directly (no template)
Choosing an Approach
| Scenario | Approach |
|---|---|
| Content layout should be restyled by end users | Template-based (1) |
| Need to fill placeholders with dynamic data | Template with holes (1) |
| Extending built-in reporter with extra features | Derived reporter (2) |
| Fully programmatic content, no restyling needed | DOM-only reporter (3) |
| Simple one-off content addition | DOM-only reporter (3) |
Template-Only vs Custom Reporter
If you only need to change appearance (styles, colors, fonts, header/footer layout) without adding new properties or holes, copy the reporter's default template, modify it, and set TemplateSrc on a standard reporter instance (ch.TemplateSrc = "path/to/custom.pdftx"). No custom class needed.
Create a custom reporter class when: you need new properties that fill template holes with dynamic content (e.g., a thumbnail image that varies per chapter).
MANDATORY GATE — Scaffold First, No Exceptions
STOP. Before writing ANY classdef file, ANY .m script, or ANY code that defines a custom reporter, you MUST execute customizeReporter in a live MATLAB session via mcp__matlab__evaluate_matlab_code.
Self-check before proceeding:
[ ] Have I called
mcp__matlab__evaluate_matlab_codeto runcustomizeReporter?[ ] Did I see MATLAB output confirming the files were created?
[ ] Do the
resources/templates/directories now exist on disk?
If ANY answer is "no" — STOP. Do not write classdef files. Do not create "setup scripts." Go back and execute the scaffolding via MCP.
Why: customizeReporter creates binary OPC template archives that cannot be replicated by writing text files. It also overwrites any classdef file at the target path — writing code first means losing it.
Common evasion patterns (all WRONG):
Writing a
.msetup script that containscustomizeReportercalls → WRONG. Execute live via MCP.Writing classdef files "to plan the structure" → WRONG. Scaffolding overwrites your code.
Skipping scaffolding because "I'll add templates manually" → WRONG. Templates are binary archives.
Deferring scaffolding to "after I finish the code" → WRONG. Edit from the scaffolded output.
Step 1: Scaffold the Custom Reporter
Action: Call mcp__matlab__evaluate_matlab_code with the scaffolding commands. Do this for ALL reporters before editing ANY.
% Run in MATLAB via MCP tool — scaffold ALL reporters before editing ANY
mlreportgen.report.Chapter.customizeReporter("+myrpt/@Chapter/Chapter.m");
mlreportgen.report.TitlePage.customizeReporter("+myrpt/@TitlePage/TitlePage.m");
mlreportgen.report.TableOfContents.customizeReporter("+myrpt/@TableOfContents/TableOfContents.m");
mlreportgen.report.Section.customizeReporter("+myrpt/@Section/Section.m");This creates:
+myrpt/@Reporter/Reporter.m— class file extending the parent reporter+myrpt/@Reporter/resources/templates/pdf/default.pdftx— PDF template+myrpt/@Reporter/resources/templates/docx/default.dotx— Word template+myrpt/@Reporter/resources/templates/html/default.htmt— HTML single-file template+myrpt/@Reporter/resources/templates/html/default.htmtx— HTML multi-file template
Always use @ClassName class directories. Each reporter must have its own resources/templates/ directory.
Fix unqualified class names after scaffolding. customizeReporter generates unqualified references like TitlePage.getClassFolder(). When the subclass has the same simple name as the parent, MATLAB resolves the unqualified name to the parent class and the custom template is never found:
% WRONG (generated by customizeReporter):
path = TitlePage.getClassFolder();
% CORRECT (manually fix to):
path = myrpt.TitlePage.getClassFolder();Fix in getDefaultTemplatePath, getClassFolder calls within createTemplate, and the second argument to customizeClass in the customizeReporter method.
Step 2: Determine the Template Contents
For Chapter/Section reporters: Read the README files in each format directory.
For all other reporters: Introspect the templates directly using the DOM Template API:
import mlreportgen.dom.*
templateDir = fullfile(classFolder,"resources","templates");
tpl = Template(fullfile(tempdir,"inspect.pdftx"),"pdf", ...
fullfile(templateDir,"pdf","default.pdftx"));
open(tpl);
parts = tpl.TemplateDocumentParts;
for i = 1:numel(parts)
fprintf("Part %d: %s\n",i,parts(i).Name);
for j = 1:numel(parts(i).Children)
child = parts(i).Children(j);
fprintf(" Child %d: %s\n",j,class(child));
end
end
close(tpl);Do NOT unzip .pdftx, .htmtx, or .dotx to read or edit template files — raw HTML/XML doesn't match the DOM object model, and edits made this way bypass framework validation. Always use Template(out,fmt,src) → open → inspect/modify DOM objects → close. Exception: unzipping .dotx to read word/styles.xml for format discovery is valid.
Step 3: Fill Template Holes
Two ways to fill a hole:
Approach A — Property (simple assignment): Add a property whose name matches the hole ID. The base getImpl fills the hole directly. Property type can be string, char, mlreportgen.dom.Image, a cell array of appendable objects, etc.
Approach B — get<HoleId>() method (processing logic): Define a method get<HoleId> (e.g., getCompanyLogo for a CompanyLogo hole). The framework calls this to produce content.
Optional Image Properties
When an Image property may be empty (e.g., not every chapter has a logo), do NOT use a typed mlreportgen.dom.Image declaration — an empty 0x0 Image array causes framework errors. Instead, use an untyped property with a custom validator and a get<HoleId>() accessor:
properties
HeaderLogo {mustBeLogoValue} = [] % Accepts [], Image, or file path string
end
methods
function img = getHeaderLogo(obj)
%getHeaderLogo Return Image for the HeaderLogo hole.
if isempty(obj.HeaderLogo)
img = [];
elseif isa(obj.HeaderLogo,"mlreportgen.dom.Image")
img = obj.HeaderLogo;
else
img = mlreportgen.dom.Image(string(obj.HeaderLogo));
img.Width = "0.4in";
img.Height = "0.4in";
end
end
end
end
function mustBeLogoValue(v)
if isempty(v), return; end
if isa(v,"mlreportgen.dom.Image"), return; end
if isstring(v) || ischar(v), return; end
error("Value must be [], an Image object, or a file path string.");
endThe get<HoleId>() method is called by the framework to fill the template hole — it handles all three input types gracefully.
Approach A Example — Property for Header Thumbnail
When the property is always required (never empty), a typed declaration is fine:
classdef Chapter < mlreportgen.report.Chapter
% File: +myrpt/@Chapter/Chapter.m
properties
HeaderThumbnail mlreportgen.dom.Image
end
methods
function obj = Chapter(varargin)
obj = [email protected](varargin{:});
end
end
% REQUIRED: template infrastructure (generated by customizeReporter)
methods (Hidden)
function templatePath = getDefaultTemplatePath(~,rpt)
path = myrpt.Chapter.getClassFolder();
templatePath = mlreportgen.report.ReportForm.getFormTemplatePath(path,rpt.Type);
end
end
methods (Static)
function path = getClassFolder()
[path] = fileparts(mfilename("fullpath"));
end
function createTemplate(templatePath,type)
path = myrpt.Chapter.getClassFolder();
mlreportgen.report.ReportForm.createFormTemplate(templatePath,type,path);
end
function customizeReporter(toClasspath)
mlreportgen.report.ReportForm.customizeClass(toClasspath,"myrpt.Chapter");
end
end
endCritical: The template infrastructure methods are required. Without getDefaultTemplatePath, your custom templates are never found.
Approach B Example — get<HoleId>() Method
classdef TitlePage < mlreportgen.report.TitlePage
% File: +myrpt/@TitlePage/TitlePage.m
properties
LogoPath string = "company_logo.png"
end
methods
function obj = TitlePage(varargin)
obj = [email protected](varargin{:});
end
function content = getCompanyLogo(obj,~)
% Called by framework to fill the CompanyLogo hole.
import mlreportgen.dom.*
content = Image(obj.LogoPath);
content.Width = "2in";
content.Height = "1in";
end
end
% REQUIRED: template infrastructure (same pattern)
methods (Hidden)
function templatePath = getDefaultTemplatePath(~,rpt)
path = myrpt.TitlePage.getClassFolder();
templatePath = mlreportgen.report.ReportForm.getFormTemplatePath(path,rpt.Type);
end
end
methods (Static)
function path = getClassFolder()
[path] = fileparts(mfilename("fullpath"));
end
function createTemplate(templatePath,type)
path = myrpt.TitlePage.getClassFolder();
mlreportgen.report.ReportForm.createFormTemplate(templatePath,type,path);
end
function customizeReporter(toClasspath)
mlreportgen.report.ReportForm.customizeClass(toClasspath,"myrpt.TitlePage");
end
end
endStep 4: Customize Templates for Each Format
You must customize the template for every output format you support.
| Format | Type Argument | Headers/Footers |
|---|---|---|
PDF (.pdftx) | 'pdf' | PDFPageHeader/PDFPageFooter on PDFPageLayout |
Word (.dotx) | 'docx' | DOCXPageHeader/DOCXPageFooter on DOCXSection |
HTML multi-file (.htmtx) | 'html' | N/A (no page concept) |
HTML single-file (.htmt) | 'html-file' | N/A |
HTML template types: Match the type argument to the file extension: use 'html' for .htmtx, use 'html-file' for .htmt.
Children is read-only on TemplateDocumentPart, PDFPageHeader, etc. To insert/reorder, create a new object and append children (use clone() when copying).
DOCX templates require TemplateText spacers between holes. When adding new holes to a DOCX template, clone an existing spacer from the original.
Common Procedure
import mlreportgen.dom.*
templateDir = fullfile(classFolder,"resources","templates");
srcPath = fullfile(templateDir,"pdf","default.pdftx");
tmpPath = fullfile(templateDir,"pdf","default_new.pdftx");
% 1. Open (MUST use different output path from source)
tpl = Template(tmpPath,"pdf",srcPath);
open(tpl);
% 2. Access template document parts
parts = tpl.TemplateDocumentParts;
section1 = parts(1); % "Section1" part
% 3. For PDF: modify PDFPageLayout headers/footers
layout = section1.Children(1); % PDFPageLayout
defaultHeader = layout.PageHeaders(2); % PageType='default'
% 4. Save changes back
tpl.TemplateDocumentParts(1) = section1;
close(tpl);
% 5. Replace original
movefile(tmpPath,srcPath);Adding Holes to Page Headers
Key rules:
ALWAYS use the DOM Template API (
Template(out,fmt,src)→open→ read/modify objects →close) to edit templates. NEVER unzip.pdftx/.htmtxand edit raw HTML/XML withfileread/regexprep/fopen. The DOM API ensures the agent interprets template structure the same way the Report Generator framework does.Use a
Tablefor side-by-side layout — without one, elements stack verticallyDo not use
Table({...})shorthand withTemplateHole— build viaTableEntry+append(entry,hole)Holes inside Paragraphs are inline holes — can only accept inline content (Text, Image)
PDF column widths: Use
TableColSpecGroup+TableColSpecwithResizeToFitContents(false)on the tableWord: Build new
DOCXPageHeaderobjects from scratch — do NOT modifyTemplateTextModify ALL page headers — "first", "default", and "even". The "first" page header renders on the chapter's opening page. Skipping it means headers only appear on page 2+.
Omit StyleRef from the first-page header. The chapter title is already displayed as the heading on page 1 — repeating it in the header is redundant. Use StyleRef only in "default" and "even" headers (where the reader needs wayfinding). The first-page header can still contain logos, tickers, or a rule.
PDF Header Example — 3-Column Layout
function customizePdfChapterTemplate(classFolder)
import mlreportgen.dom.*
templateDir = fullfile(classFolder,"resources","templates","pdf");
srcPath = fullfile(templateDir,"default.pdftx");
tmpPath = fullfile(templateDir,"default_new.pdftx");
tpl = Template(tmpPath,"pdf",srcPath);
open(tpl);
parts = tpl.TemplateDocumentParts;
for i = 1:numel(parts)
if strcmp(parts(i).Name,"Section1")
section1 = parts(i);
sectionIdx = i;
break
end
end
layout = section1.Children(1);
for k = 1:numel(layout.PageHeaders)
pageType = layout.PageHeaders(k).PageType;
newHeader = PDFPageHeader(pageType);
imgPara = Paragraph("");
append(imgPara,TemplateHole("HeaderThumbnail"));
% Center cell: StyleRef on default/even only (not first page)
% First page already displays the chapter title as a heading —
% repeating it in the header is redundant (standard book design).
titlePara = Paragraph("");
titlePara.HAlign = "center";
titlePara.Style = [titlePara.Style, {FontSize("10pt")}];
if ~strcmp(pageType,"first")
append(titlePara,StyleRef("SectionTitle1"));
end
spacerPara = Paragraph(" ");
e1 = TableEntry(imgPara);
e1.Style = {Width("1in")};
e2 = TableEntry(titlePara);
e2.Style = {Width("5in")};
e3 = TableEntry(spacerPara);
e3.Style = {Width("1in")};
row = TableRow();
append(row,e1);
append(row,e2);
append(row,e3);
headerTable = Table();
append(headerTable,row);
headerTable.Width = "100%";
headerTable.Style = {Border("none"),ResizeToFitContents(false)};
headerTable.TableEntriesVAlign = "bottom";
append(newHeader,headerTable);
hrPara = Paragraph("");
hrPara.Style = {HAlign("center"),OuterMargin("0","0","3pt","0"),...
Border("solid","#000000","0.5pt")};
append(newHeader,hrPara);
layout.PageHeaders(k) = newHeader;
end
tpl.TemplateDocumentParts(sectionIdx) = section1;
close(tpl);
movefile(tmpPath,srcPath);
endAdding a Body Hole (Between Title and Content)
function addBodyHoleToPdfTemplate(classFolder,holeName)
import mlreportgen.dom.*
templateDir = fullfile(classFolder,"resources","templates","pdf");
srcPath = fullfile(templateDir,"default.pdftx");
tmpPath = fullfile(templateDir,"default_new.pdftx");
tpl = Template(tmpPath,"pdf",srcPath);
open(tpl);
parts = tpl.TemplateDocumentParts;
for i = 1:numel(parts)
if strcmp(parts(i).Name,"Section1")
oldSection = parts(i);
sectionIdx = i;
break
end
end
newSection = TemplateDocumentPart("Section1");
for k = 1:numel(oldSection.Children)
child = oldSection.Children(k);
append(newSection,clone(child));
if isa(child,"mlreportgen.dom.TemplateHole") && ...
strcmp(child.HoleId,"Title")
newHole = TemplateHole(holeName);
newHole.Description = upper(holeName);
append(newSection,newHole);
end
end
tpl.TemplateDocumentParts(sectionIdx) = newSection;
close(tpl);
movefile(tmpPath,srcPath);
endStep 5: Test the Custom Reporter
for fmt = ["pdf","docx","html"]
rpt = Report("Test_" + fmt,fmt);
ch = myrpt.Chapter("Test");
img = Image("thumbnail.png");
img.Width = "0.5in";
ch.HeaderThumbnail = img;
append(rpt,ch);
close(rpt);
rptview(rpt);
endStep 6: Recovering from Template Corruption
If template edits produce unexpected results, re-scaffold:
mlreportgen.report.Chapter.customizeReporter("+myrpt/@Chapter/Chapter.m");Then re-apply classdef edits and re-run buildTemplates.
When to suspect corruption:
TOC shows "No table of contents entries found"
Template holes stop rendering content
Styles look different from what
buildTemplatesspecifiesError messages reference template structure that shouldn't be affected
Customizing Chapter/Section Numbering (Without Template Edits)
The Chapter reporter's getTitleReporter() returns a SectionTitle with NumberPrefix and NumberSuffix:
Inline approach:
ch = Chapter("Title","Introduction");
titleRptr = getTitleReporter(ch);
titleRptr.NumberPrefix = ""; % Removes "Chapter" prefix
titleRptr.NumberSuffix = ". "; % "1. Introduction"
ch.Title = titleRptr;Subclass approach:
classdef Chapter < mlreportgen.report.Chapter
methods
function rptr = getTitleReporter(ch)
rptr = [email protected](ch);
rptr.NumberPrefix = "Section"; % "Section 1. Introduction"
end
end
endDOM-Only Reporter (No Template)
Override getImpl to return DOM objects directly. Use when content is entirely programmatic:
classdef TestResultCard < mlreportgen.report.Reporter
% File: +myrpt/@TestResultCard/TestResultCard.m
properties
TestName (1,1) string = ""
Result (1,1) string {mustBeMember(Result,["Pass","Fail","Skip"])} = "Pass"
Duration (1,1) double = 0
end
methods
function obj = TestResultCard(name,result,duration)
obj = [email protected]();
if nargin > 0,obj.TestName = name; end
if nargin > 1,obj.Result = result; end
if nargin > 2,obj.Duration = duration; end
end
function content = getImpl(obj,rpt) %#ok<INUSD>
import mlreportgen.dom.*
grp = Group();
namePara = Paragraph(obj.TestName);
namePara.Style = {Bold(true),FontSize("12pt")};
append(grp,namePara);
switch obj.Result
case "Pass", clr = "green";
case "Fail", clr = "red";
case "Skip", clr = "orange";
end
statusPara = Paragraph(upper(obj.Result));
statusPara.Style = {Bold(true),Color(clr)};
append(grp,statusPara);
durPara = Paragraph(sprintf("Duration: %.1f s",obj.Duration));
durPara.Style = {FontSize("9pt"),Color("gray")};
append(grp,durPara);
append(grp,HorizontalRule());
content = grp;
end
end
endKey rules for DOM-only reporters:
Call superclass constructor:
obj = [email protected]()getImplmust be public with signature(obj, rpt)Return any DOM object appendable to a Document
Do NOT use template infrastructure methods (no
getDefaultTemplatePathetc.)
Customizing Built-In TitlePage
Template Architecture
The TitlePage template has the same structure across all formats:
| Format | First Child (Layout) | Holes |
|---|---|---|
PDFPageLayout | Title, Subtitle, Image, Author, Publisher, PubDate | |
| DOCX | DOCXSection + TemplateText spacers | Title, Subtitle, Image, Author, Publisher, PubDate |
| HTML | TemplateText (structural HTML) | Title, Subtitle, Image, Author, Publisher, PubDate |
Sub-document part templates (one per hole): TitlePageTitle, TitlePageSubtitle, TitlePageImage, TitlePageAuthor, TitlePagePublisher, TitlePagePubDate. Each contains a styled Paragraph (with StyleName matching the part name) wrapping an inline Content hole:
TitlePageTitle (sub-doc part):
Paragraph (StyleName = "TitlePageTitle") ← 24pt bold centered
└─ TemplateHole("Content") ← filled with title textThe paragraph style controls all formatting — both positioning (centering, margins) and character appearance (font-size, bold). This is the mechanism that styles built-in TitlePage fields without the reporter needing inline style objects.
How the Reporter Fills Its Template
Base
getImplcreates aDocumentPartfrom theTitlePagetemplateIterates over all holes in the template
For each hole, checks if the reporter has a
get<HoleId>method (e.g.,getTitle)If method exists → calls it. Built-in methods use
InlineContentreporters backed by sub-doc part templates for stylingIf no method but a property with matching name exists → fills the hole directly with the property value
The hole's
DefaultHoleStyleName(if set and persisted) applies character formatting to the filled content
Adding Custom Properties and Holes
Add custom string fields (e.g., WindFarmName, CustomerName) with template-driven styling — no get<HoleId>() methods needed:
classdef TitlePage < mlreportgen.report.TitlePage
% File: +myrpt/@TitlePage/TitlePage.m
properties
WindFarmName string = ""
CustomerName string = ""
end
methods
function obj = TitlePage(varargin)
obj = [email protected](varargin{:});
end
end
% REQUIRED: template infrastructure
methods (Hidden)
function templatePath = getDefaultTemplatePath(~,rpt)
path = myrpt.TitlePage.getClassFolder();
templatePath = mlreportgen.report.ReportForm.getFormTemplatePath(path,rpt.Type);
end
end
methods (Static)
function path = getClassFolder()
[path] = fileparts(mfilename("fullpath"));
end
function createTemplate(templatePath,type)
path = myrpt.TitlePage.getClassFolder();
mlreportgen.report.ReportForm.createFormTemplate(templatePath,type,path);
end
function customizeReporter(toClasspath)
mlreportgen.report.ReportForm.customizeClass(toClasspath,"myrpt.TitlePage");
end
end
endNo getters required. The base getImpl iterates over template holes. When it finds a hole whose ID matches a property name, it fills the hole with the property value. Formatting is controlled entirely by template styles.
When ARE get<HoleId>() methods needed? Only when you need processing logic — e.g., accepting multiple input types (string, Image, DOM object), conditional content, or wrapping content in a sub-reporter. For simple string properties, the template-only approach is sufficient and preferred.
Inline Hole Pattern for Styled Custom Fields
To add a formatted string field to the TitlePage template, insert a Paragraph (with a paragraph style for positioning) containing an inline TemplateHole (with DefaultHoleStyleName for character formatting):
Template structure:
Paragraph (StyleName = "TitlePageWindFarmName") ← centering, margins
└─ TemplateHole("WindFarmName") ← filled by property value
DefaultHoleStyleName = "WindFarmNameChar" ← bold, color, font-sizeThis mirrors how the built-in TitlePage holes work — the sub-document part templates (TitlePageTitle, etc.) each contain a styled Paragraph with a Content hole. For custom fields, you embed the styled paragraph directly in the main doc part instead of creating sub-doc parts.
Editing the TitlePage Template (PDF/HTML)
function customizeTitlePageTemplate(classFolder)
import mlreportgen.dom.*
templateDir = fullfile(classFolder,"resources","templates","pdf");
srcPath = fullfile(templateDir,"default.pdftx");
tmpPath = fullfile(templateDir,"default_new.pdftx");
tpl = Template(tmpPath,"pdf",srcPath);
open(tpl);
% Modify the existing doc part in place (preserves DefaultHoleStyleName)
mainPart = tpl.TemplateDocumentParts(1);
% Add page border to existing PDFPageLayout
mainPart.Children(1).PageBorder = PageBorder("solid","#1B5E20","3pt");
mainPart.Children(1).PageBorder.MeasureFrom = "pageboundary";
% Append styled paragraphs with inline holes
wfPara = Paragraph();
wfPara.StyleName = "TitlePageWindFarmName";
wfHole = TemplateHole("WindFarmName");
wfHole.DefaultHoleStyleName = "WindFarmNameChar";
append(wfPara,wfHole);
append(mainPart,wfPara);
cnPara = Paragraph();
cnPara.StyleName = "TitlePageCustomerName";
cnHole = TemplateHole("CustomerName");
cnHole.DefaultHoleStyleName = "CustomerNameChar";
append(cnPara,cnHole);
append(mainPart,cnPara);
tpl.TemplateDocumentParts(1) = mainPart;
% Add paragraph styles (positioning)
ss = tpl.Stylesheet;
wfPS = TemplateParagraphStyle("TitlePageWindFarmName");
wfPS.Formats = [HAlign("center"),OuterMargin("0","0","12pt","6pt")];
addStyle(ss,wfPS);
cnPS = TemplateParagraphStyle("TitlePageCustomerName");
cnPS.Formats = [HAlign("center"),OuterMargin("0","0","6pt","6pt")];
addStyle(ss,cnPS);
% Add character styles (text formatting)
wfCS = TemplateTextStyle("WindFarmNameChar");
wfCS.Formats = [FontSize("16pt"),Bold(true),Color("#1B5E20")];
addStyle(ss,wfCS);
cnCS = TemplateTextStyle("CustomerNameChar");
cnCS.Formats = [FontSize("12pt"),Color("#333333")];
addStyle(ss,cnCS);
close(tpl);
movefile(tmpPath,srcPath);
endKey points:
Append to the existing doc part (
tpl.TemplateDocumentParts(1)) — do NOT rebuild withTemplateDocumentPart("TitlePage"). Rebuilding losesDefaultHoleStyleNamein DOCX.TemplateParagraphStylecontrols paragraph layout (centering, margins)TemplateTextStylecontrols character formatting (font, bold, color)OuterMargin(left,right,top,bottom)— NOT CSS order. UseOuterMargin("0","0","12pt","6pt")for vertical spacing between title page elementsUse
addStyleto add new styles to the stylesheet
Editing the TitlePage Template (DOCX)
DOCX templates do not persist DefaultHoleStyleName on inline holes. Use TemplateLinkedStyle instead — it combines paragraph AND character formatting in one style:
function customizeTitlePageDocxTemplate(classFolder)
import mlreportgen.dom.*
templateDir = fullfile(classFolder,"resources","templates","docx");
srcPath = fullfile(templateDir,"default.dotx");
tmpPath = fullfile(templateDir,"default_new.dotx");
tpl = Template(tmpPath,"docx",srcPath);
open(tpl);
mainPart = tpl.TemplateDocumentParts(1);
% Add page border
mainPart.Children(1).PageBorder = PageBorder("solid","#1B5E20","3pt");
mainPart.Children(1).PageBorder.MeasureFrom = "pageboundary";
% Append styled paragraphs with inline holes
wfPara = Paragraph();
wfPara.StyleName = "TitlePageWindFarmName";
append(wfPara,TemplateHole("WindFarmName"));
append(mainPart,wfPara);
cnPara = Paragraph();
cnPara.StyleName = "TitlePageCustomerName";
append(cnPara,TemplateHole("CustomerName"));
append(mainPart,cnPara);
tpl.TemplateDocumentParts(1) = mainPart;
% Use TemplateLinkedStyle — includes both paragraph and run formatting
ss = tpl.Stylesheet;
wfStyle = TemplateLinkedStyle("TitlePageWindFarmName");
wfStyle.Formats = [HAlign("center"),OuterMargin("0","0","12pt","6pt"),...
FontSize("16pt"),Bold(true),Color("#1B5E20")];
addStyle(ss,wfStyle);
cnStyle = TemplateLinkedStyle("TitlePageCustomerName");
cnStyle.Formats = [HAlign("center"),OuterMargin("0","0","6pt","6pt"),...
FontSize("12pt"),Color("#333333")];
addStyle(ss,cnStyle);
close(tpl);
movefile(tmpPath,srcPath);
endWhy TemplateLinkedStyle for DOCX: A linked style creates both a paragraph style (with w:pPr for alignment/margins) and a linked character style (with w:rPr for font/color/bold). Word applies run properties from the linked style to all runs in the paragraph — achieving the same result as separate paragraph + character styles in PDF/HTML.
Repeat for ALL formats. You MUST also customize:
HTML
.htmtx:Template(tmp,"html",src)— same pattern as PDF (DefaultHoleStyleName persists)HTML
.htmt:Template(tmp,"html-file",src)— same pattern as PDF
If you skip HTML templates, content that relies on custom holes will be silently missing in HTML output.
Page Borders
Always embed page borders in the template during customization:
% PDF:
pdfLayout.PageBorder = PageBorder("solid","#1B5E20","3pt");
pdfLayout.PageBorder.MeasureFrom = "pageboundary";
% DOCX:
docxSection.PageBorder = PageBorder("solid","#1B5E20","3pt");
docxSection.PageBorder.MeasureFrom = "pageboundary";Key facts:
PageBorderapplies only to the reporter's pages (not subsequent chapters)MeasureFrom = "pageboundary"positions relative to page edgeHTML has no page concept —
PageBorderis ignored
Chapter Template Anatomy
PDF Template Structure
┌─────────────────────────────────────────────────────┐
│ Section1 (top-level chapter) │
│ ├── <layout> — page size, margins, header/footer │
│ │ ├── <pheader type="first" .../> │
│ │ ├── <pfooter type="first" .../> │
│ │ ├── <pheader type="default" .../> │
│ │ ├── <pfooter type="default" .../> │
│ │ ├── <pheader type="even" .../> │
│ │ └── <pfooter type="even" .../> │
│ ├── <hole id="Title"> │
│ └── <hole id="Content"> │
├─────────────────────────────────────────────────────┤
│ Section2–Section6 (subsections) │
│ ├── <hole id="Title"> │
│ └── <hole id="Content"> │
├─────────────────────────────────────────────────────┤
│ SectionNumberedTitle1–6 (numbered title format) │
│ ├── <hole id="NumberPrefix"> │
│ ├── <autonumber stream-name="sect1"/> │
│ ├── <hole id="NumberSuffix"> │
│ └── <hole id="Content"> │
├─────────────────────────────────────────────────────┤
│ SectionTitle1–6 (unnumbered title format) │
│ └── <hole id="Content"> │
├─────────────────────────────────────────────────────┤
│ SectionFirstPageHeader / SectionDefaultPageHeader │
│ SectionEvenPageHeader / Section*PageFooter │
└─────────────────────────────────────────────────────┘How Headers Connect to the Page Layout (PDF)
<layout style="page-margin: 0.5in 1in 0.5in 1in 0.5in 0.5in 0in; page-size: 8.5in 11in portrait">
<pheader type="first" template-name="SectionFirstPageHeader"/>
<pfooter type="first" template-name="SectionFirstPageFooter"/>
<pheader type="default" template-name="SectionDefaultPageHeader"/>
<pfooter type="default" template-name="SectionDefaultPageFooter"/>
<pheader type="even" template-name="SectionEvenPageHeader"/>
<pfooter type="even" template-name="SectionEvenPageFooter"/>
<pnumber format="1"/>
</layout>Word Template Structure
TemplateDocumentParts (18 parts):
├── Section1 ← Top-level chapter
│ Children:
│ [1] DOCXSection ← margins, headers, footers, page numbers
│ [2-3] TemplateText ← whitespace
│ [4] TemplateHole ← "Title" hole
│ [5] TemplateText ← whitespace
│ [6] TemplateHole ← "Content" hole
│ [7-8] TemplateText ← whitespace
├── Section2–Section6 ← Subsections
├── SectionNumberedTitle1–6
└── SectionTitle1–6Word templates embed headers/footers directly on the DOCXSection object (no separate doc parts):
docxSection.PageHeaders:
[1] DOCXPageHeader (PageType="default") % Odd/all pages
[2] DOCXPageHeader (PageType="first") % First page only
docxSection.PageFooters:
[1] DOCXPageFooter (PageType="default")
[2] DOCXPageFooter (PageType="first")HTML Template Structure
HTML templates have no page headers or footers. Doc parts contain only Title and Content holes. To add custom content, insert it in the Section1 doc part body.
Balanced 3-Column Header Layout Pattern
Use for "default" and "even" page headers (where reader needs wayfinding):
Page text width = 8.5in - 2×1in = 6.5in
┌──────────┬────────────────────────────┬──────────┐
│ Logo │ StyleRef (centered) │ Ticker │
│ 1.25in │ 4.0in │ 1.25in │
└──────────┴────────────────────────────┴──────────┘For the "first" page header, omit StyleRef (the chapter title is already the heading on page 1):
┌──────────┬────────────────────────────┬──────────┐
│ Logo │ (empty) │ Ticker │
│ 1.25in │ 4.0in │ 1.25in │
└──────────┴────────────────────────────┴──────────┘Left + Right columns MUST be equal width for true centering.
Modifying Template Styles
PDF/HTML — modify RawFormats directly (handle semantics):
tpl = Template(outPath,"pdf",srcPath);
open(tpl);
styles = getStyle(tpl.Stylesheet,"SectionTitle1");
for i = 1:numel(styles)
styles(i).RawFormats = regexprep(styles(i).RawFormats, ...
"color:\s*[^;]+","color: red");
end
close(tpl);DOCX — TemplateDOCXStyle is read-only; create new and replace:
tpl = Template(outPath,"docx",srcPath);
open(tpl);
newStyle = TemplateLinkedStyle("SectionTitle1");
newStyle.Formats = [Bold(true),FontSize("14pt"),Color("red")];
replaceStyle(tpl.Stylesheet,newStyle);
close(tpl);DOCX table styles:
ts = TemplateTableStyle("BaseTableContent");
ts.Formats = [Border("solid","#333333","0.5pt"),FontSize("10pt")];
ts.HeaderFormats = [Bold(true),BackgroundColor("#1B3A5C"),Color("white")];
replaceStyle(tpl.Stylesheet,ts);See template-system.md for complete style modification procedures.
Common Mistakes
| Mistake | Correct Approach |
|---|---|
Writing classdef before running customizeReporter | Scaffold via MCP first, then edit generated files |
Scaffolding without @ClassName directories | Use +pkg/@ClassName/ClassName.m so each has its own resources/ |
| Leaving unqualified class names from scaffolding | Replace with fully-qualified: myrpt.TitlePage.getClassFolder() |
Removing getDefaultTemplatePath/getClassFolder | Always keep — directs framework to your templates |
| Only customizing the PDF template | Customize all formats you support |
Using same path for source and output in Template() | Write to temp path, then movefile back |
Editing .dotx XML directly | Use DOM Template API: Template(out,'docx',src) |
Modifying TemplateText with Word XML | Build new DOCXPageHeader from scratch |
Using Table({...}) shorthand with TemplateHole | Build via TableEntry + append(entry,hole) |
Setting column widths without ResizeToFitContents(false) | Add to table Style with absolute table width |
Returning Paragraph from get<HoleId> for inline hole | Return Text or Image for holes inside Paragraphs |
| Only modifying "default" and "even" PDF headers | Modify ALL headers: "first", "default", and "even" — "first" is the chapter opening page |
Using StyleRef("ChapterTitle1") | Use StyleRef("SectionTitle1") — no ChapterTitle1 exists |
Overriding getImpl on TitlePage to add borders | Embed PageBorder on layout during template customization |
Replacing heading styles without OutlineLevel(N) | Always include OutlineLevel in replacement heading styles |
Using getStyle(ss,name,type) for PDF/HTML | Use getStyle(ss,name) without type; filter by Selector |
Setting Formats on TemplateDOCXStyle | Read-only — create TemplateLinkedStyle + replaceStyle |
Using addStyle for PDF/HTML | Modify RawFormats on handle from getStyle |
Using 2-arg Template(path,fmt) to inspect styles | Use 3-arg: Template(outPath,fmt,srcPath) |
Typing optional Image property as mlreportgen.dom.Image | Use untyped {mustBeLogoValue} = [] with a get<HoleId>() accessor that handles [], Image, and path string |
Key Functions
| Class/Function | Purpose |
|---|---|
*.customizeReporter | Scaffold custom reporter class + templates |
Template (3-arg) | Create/edit templates programmatically |
TemplateHole | Define a hole (placeholder) in a template |
TemplateDocumentPart | Define a doc part template |
DOCXPageHeader/DOCXPageFooter | Word page header/footer definition |
DOCXSection | Word section break with page layout |
PDFPageHeader/PDFPageFooter | PDF page header/footer definition |
PDFPageLayout | PDF page layout (margins, headers, footers) |
PageBorder | Page border specification |
StyleRef | Dynamic running head (works PDF and Word) |
getStyle / replaceStyle | Modify template styles |
TemplatePDFStyle / TemplateHTMLStyle | PDF/HTML style (CSS via RawFormats) |
TemplateTextStyle | Character/inline style for PDF/HTML (font, bold, color) |
TemplateLinkedStyle / TemplateParagraphStyle | DOCX style replacements |
TemplateTableStyle | DOCX table style replacement |
Copyright 2026 The MathWorks, Inc.
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Needs an MCP server
Security
No issues found by our static scan· No issues found
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Created by
- MathWorks
- Publisher
- MathWorks (Official)
- Source
- matlab/matlab-agentic-toolkit · skills-catalog/reporting-and-database-access/matlab-generate-report · commit 2a7f215
- Licence
- BSD 2-Clause License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE.md)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by MathWorks.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download matlab-generate-report.zip