Generated banner for Matlab Generate Report
Generated logo for Matlab Generate Report

Matlab Generate Report

MathWorksOfficialCreated by MathWorks

  • Skill
  • Tool integration
  • Science and research
  • BSD-2-Clause
  • No issues found

From the author

Unified skill for all MATLAB and Simulink report generation tasks. Covers report generation, PDF report, document generation, mlreportgen, Report Generator, template, TitlePage, Chapter, multipage HTML, HTMLPage, html-multipage, custom reporter, customizeReporter, template hole, page header, page border, company style package, branded reporters, buildTemplates, Simulink report, model documentation, slreportgen, block diagram report, model report, subsystem documentation, SystemDiagramFinder, SystemIO, ExecutionOrder, LookupTable, template style, page footer, TemplateSrc, report package, table variants, multiple HTML pages, PDF report generation, Word report, DOM API, FormalTable, BaseTable, Figure, FormalImage, Section.
— the description by MathWorks

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

  1. Download the skill as a ZIP:

    https://autogineer.com/agent-skills/matlab/matlab-generate-report/download
  2. Unzip 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, writematrix

  • Live 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);
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:

  1. List all content/format requirements — title page fields, page borders, headers/footers, branded styling

  2. Check built-in reporters. Built-in TitlePage has: Title, Subtitle, Author, Publisher, PubDate, Image. Built-in Chapter/Section provide headings, auto-numbering, and a Content hole.

  3. If built-in reporters meet all requirements, use them directly

  4. If any requirement is unmet, create custom reporters:

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, verificationreferences/report-api-patterns.md
Work with DOM tables, HTML inclusion, multipage HTML, Number/NumberFormatreferences/dom-api-patterns.md
Understand template file types, locate/inspect templatesreferences/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 buildTemplatesreferences/report-package.md
Generate Simulink model reports (finders, orientation, centering)references/simulink-report.md
Look up all Simulink finders and reportersreferences/finders-and-reporters.md
Iterate struct arrays into chapters with custom reportersreferences/data-driven-report-pattern.md
Edit template styles (colors, fonts, headers, footers, TOC)references/template-system.md

Key Functions

ClassPurposePackage
ReportTop-level report containermlreportgen.report
TitlePageTitle page with title, subtitle, authormlreportgen.report
TableOfContentsAuto-generated TOCmlreportgen.report
Chapter / SectionDocument structuremlreportgen.report
FigureAuto-numbered figure from MATLAB figuremlreportgen.report
FormalImageAuto-numbered image from file with captionmlreportgen.report
BaseTableAuto-numbered table wrapper (pass FormalTable)mlreportgen.report
FormalTableTable with header/body/footer from cell arraysmlreportgen.dom
Template (3-arg)Edit templates: Template(out,fmt,src)mlreportgen.dom
HTMLFile / HTMLInclude HTML content in any output formatmlreportgen.dom
HTMLPagePage in multipage HTML document (R2024a+)mlreportgen.dom
NumberNumeric DOM object (use with NumberFormat)mlreportgen.dom
TemplateHolePlaceholder filled at report timemlreportgen.dom
Page / NumPagesPage number / total pagesmlreportgen.dom
*.customizeReporterScaffold custom reporter + templatesmlreportgen.report
getStyle / replaceStyleModify template stylesmlreportgen.dom
slreportgen.report.ReportReport container for Simulink contentslreportgen.report
slreportgen.report.DiagramDiagram snapshot of a systemslreportgen.report
slreportgen.finder.SystemDiagramFinderFind system/subsystem diagramsslreportgen.finder
slreportgen.report.SystemIOReport inputs/outputs (requires compilation)slreportgen.report
slreportgen.report.ExecutionOrderReport block execution orderslreportgen.report
slreportgen.report.LookupTableReport lookup table data + plotslreportgen.report
rptviewDisplay report (updates DOCX fields)function
docviewProcess Word documents (update fields, convert)function

Common Mistakes

MistakeCorrect Approach
Using mlreportgen.dom.Document as report containerUse mlreportgen.report.Report. Exception: multipage HTML requires Document with "html-multipage"
Using mlreportgen.report.Report for Simulink contentUse slreportgen.report.Report — needed for model compilation
Using Report with "html" for multipage outputUse 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 cellsUse 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 headersCustomize ALL page types: "first", "default", "even" — but differentiate content (see below)
Putting StyleRef (chapter title) in the first-page headerFirst page already displays the chapter heading — omit StyleRef from "first" header. Use it only in "default"/"even" for wayfinding
Opening DOCX with winopen/open/startUse rptview(rpt) or docview(file,"updatefields") — triggers field update
Using ColSpec/ColSpecGroup for widthsUse 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 template2-arg creates blank. Use 3-arg: Template(out,"pdf",src)
Same path for source and output in Template constructorOPC 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 WidthDOCX does not auto-swap — set both explicitly
Page numbers resetting after PDFPageLayoutSet pl.FirstPageNumber = '' (empty string) to continue numbering
Wrapping reporter in Paragraph to center itCustomize the reporter's template style via Template API
Using RawText(html) for HTML in PDFUse HTMLFile(path) or HTML(string) — works for any format
Using Group in a Table cell arrayUse nested cell arrays: Table({{img,caption},desc})
Passing MATLAB table() to BaseTablePass 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.SubtitleCreate a custom title page reporter via customizeReporter
Writing custom reporter class from scratchUse customizeReporter to scaffold — creates required binary templates
Writing classdef before running customizeReporterScaffolding overwrites classdef files. Scaffold first, then edit
Unqualified TitlePage.getClassFolder() in packageUse fully-qualified: mypkg.TitlePage.getClassFolder()
getStyle(ss,name,type) for PDF/HTMLType arg returns []. Use getStyle(ss,name) without type
OuterMargin("12pt","6pt","0","0") for vertical spacingArgs are (left,right,top,bottom) — NOT CSS order. Use OuterMargin("0","0","12pt","6pt") for top/bottom spacing
Using TemplateCharacterStyleDoes not exist. Use TemplateTextStyle for character/inline styles in PDF/HTML
Unzipping .pdftx/.htmtx/.dotx to edit with fileread/regexprepALWAYS use DOM Template API — Template(out,fmt,src) + open + modify objects + close. Raw edits bypass framework validation
find_system + DiagramFinder for discoveryUse SystemDiagramFinder with filter properties
Diagram(result.Path) on ChartDiagramFinder resultsUse getReporter(result) — chart paths are invalid for Diagram constructor
find() + for loop on findersAlways use iterator: while finder.hasNext() / result = finder.next()
BlockFinder with SearchDepth=inf expecting referenced modelsBlockFinder does NOT traverse Model References. Use SystemDiagramFinder (IncludeReferencedModels=true) + per-system BlockFinder (SearchDepth=1)
ExecutionOrder on virtual subsystemCheck get_param(obj,'IsSubsystemVirtual') first
ExecutionOrder/SystemIO on Simulink FunctionsGuard with slreportgen.utils.isSimulinkFunction(result.Object)

Conventions

  • Always close(rpt) after appending all content — generates the output file

  • Always rptview or docview to open DOCX — never winopen or open

  • Use figure("Visible","off") for plots destined for reports

  • Use exportgraphics (not print) when saving figures to files

  • One Chapter = one numbering scope — figures/tables numbered Chapter-Sequence (e.g., "Figure 2-1")

  • BaseTable wraps FormalTable — pass FormalTable(headers,body), not a MATLAB table()

  • Cell-level styling: Use entry(t,row,col) after construction, not during

  • Template styles use [] arrays; inline .Style properties use {}

  • Reporters in @ClassName directories — each needs its own resources/templates/

  • Scaffold ALL reporters before editing ANY — customizeReporter creates binary templates + overwrites classdef

  • Fix unqualified class names after scaffolding — use package-qualified names

  • Customize all output formats — PDF/Word/HTML templates are independent

  • Simulink: Always slreportgen.report.Report; always Diagram.Snapshot.ScaleToFit = true

  • Simulink finders: Use .Object handle (not path string) for get_param; use result.Name for titles

  • JSON 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 display

Verification: Running without error does NOT confirm correctness. After close(rpt):

  • Check isfile(rpt.OutputPath) and dir(rpt.OutputPath).bytes > 1000

  • For PDF: call open(rpt), then set rpt.Document.RetainFO = true before appending content, search *_FO/*.fo files

  • For DOCX: unzip and search word/document.xml

  • Assert 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

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

  1. Template-based reporter — uses customizeReporter scaffold with holes filled by properties or get<HoleId>() methods

  2. Derived reporter — extends an existing reporter, overrides getImpl to add content

  3. DOM-only reporter — overrides getImpl to return DOM objects directly (no template)

Choosing an Approach
ScenarioApproach
Content layout should be restyled by end usersTemplate-based (1)
Need to fill placeholders with dynamic dataTemplate with holes (1)
Extending built-in reporter with extra featuresDerived reporter (2)
Fully programmatic content, no restyling neededDOM-only reporter (3)
Simple one-off content additionDOM-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_code to run customizeReporter?

  • [ ] 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 .m setup script that contains customizeReporter calls → 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.");
end

The 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
end

Critical: 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
end

Step 4: Customize Templates for Each Format

You must customize the template for every output format you support.

FormatType ArgumentHeaders/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/.htmtx and edit raw HTML/XML with fileread/regexprep/fopen. The DOM API ensures the agent interprets template structure the same way the Report Generator framework does.

  • Use a Table for side-by-side layout — without one, elements stack vertically

  • Do not use Table({...}) shorthand with TemplateHole — build via TableEntry + append(entry,hole)

  • Holes inside Paragraphs are inline holes — can only accept inline content (Text, Image)

  • PDF column widths: Use TableColSpecGroup + TableColSpec with ResizeToFitContents(false) on the table

  • Word: Build new DOCXPageHeader objects from scratch — do NOT modify TemplateText

  • Modify 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);
end
Adding 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);
end

Step 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);
end

Step 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 buildTemplates specifies

  • Error 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
end

DOM-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
end

Key rules for DOM-only reporters:

  1. Call superclass constructor: obj = [email protected]()

  2. getImpl must be public with signature (obj, rpt)

  3. Return any DOM object appendable to a Document

  4. Do NOT use template infrastructure methods (no getDefaultTemplatePath etc.)


Customizing Built-In TitlePage

Template Architecture

The TitlePage template has the same structure across all formats:

FormatFirst Child (Layout)Holes
PDFPDFPageLayoutTitle, Subtitle, Image, Author, Publisher, PubDate
DOCXDOCXSection + TemplateText spacersTitle, Subtitle, Image, Author, Publisher, PubDate
HTMLTemplateText (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 text

The 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
  1. Base getImpl creates a DocumentPart from the TitlePage template

  2. Iterates over all holes in the template

  3. For each hole, checks if the reporter has a get<HoleId> method (e.g., getTitle)

  4. If method exists → calls it. Built-in methods use InlineContent reporters backed by sub-doc part templates for styling

  5. If no method but a property with matching name exists → fills the hole directly with the property value

  6. 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
end

No 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-size

This 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);
end

Key points:

  • Append to the existing doc part (tpl.TemplateDocumentParts(1)) — do NOT rebuild with TemplateDocumentPart("TitlePage"). Rebuilding loses DefaultHoleStyleName in DOCX.

  • TemplateParagraphStyle controls paragraph layout (centering, margins)

  • TemplateTextStyle controls character formatting (font, bold, color)

  • OuterMargin(left,right,top,bottom) — NOT CSS order. Use OuterMargin("0","0","12pt","6pt") for vertical spacing between title page elements

  • Use addStyle to 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);
end

Why 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:

  • PageBorder applies only to the reporter's pages (not subsequent chapters)

  • MeasureFrom = "pageboundary" positions relative to page edge

  • HTML has no page concept — PageBorder is 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–6

Word 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

MistakeCorrect Approach
Writing classdef before running customizeReporterScaffold via MCP first, then edit generated files
Scaffolding without @ClassName directoriesUse +pkg/@ClassName/ClassName.m so each has its own resources/
Leaving unqualified class names from scaffoldingReplace with fully-qualified: myrpt.TitlePage.getClassFolder()
Removing getDefaultTemplatePath/getClassFolderAlways keep — directs framework to your templates
Only customizing the PDF templateCustomize all formats you support
Using same path for source and output in Template()Write to temp path, then movefile back
Editing .dotx XML directlyUse DOM Template API: Template(out,'docx',src)
Modifying TemplateText with Word XMLBuild new DOCXPageHeader from scratch
Using Table({...}) shorthand with TemplateHoleBuild 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 holeReturn Text or Image for holes inside Paragraphs
Only modifying "default" and "even" PDF headersModify 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 bordersEmbed 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/HTMLUse getStyle(ss,name) without type; filter by Selector
Setting Formats on TemplateDOCXStyleRead-only — create TemplateLinkedStyle + replaceStyle
Using addStyle for PDF/HTMLModify RawFormats on handle from getStyle
Using 2-arg Template(path,fmt) to inspect stylesUse 3-arg: Template(outPath,fmt,srcPath)
Typing optional Image property as mlreportgen.dom.ImageUse untyped {mustBeLogoValue} = [] with a get<HoleId>() accessor that handles [], Image, and path string

Key Functions

Class/FunctionPurpose
*.customizeReporterScaffold custom reporter class + templates
Template (3-arg)Create/edit templates programmatically
TemplateHoleDefine a hole (placeholder) in a template
TemplateDocumentPartDefine a doc part template
DOCXPageHeader/DOCXPageFooterWord page header/footer definition
DOCXSectionWord section break with page layout
PDFPageHeader/PDFPageFooterPDF page header/footer definition
PDFPageLayoutPDF page layout (margins, headers, footers)
PageBorderPage border specification
StyleRefDynamic running head (works PDF and Word)
getStyle / replaceStyleModify template styles
TemplatePDFStyle / TemplateHTMLStylePDF/HTML style (CSS via RawFormats)
TemplateTextStyleCharacter/inline style for PDF/HTML (font, bold, color)
TemplateLinkedStyle / TemplateParagraphStyleDOCX style replacements
TemplateTableStyleDOCX 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 or MathWorks.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download matlab-generate-report.zip