Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/docmaker-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
strategy:
matrix:
platform: [ubuntu-latest, windows-latest]
matlab-version: [R2022b, R2023b, R2024b, R2025b]
matlab-version: [R2022b, R2023b, R2024b, R2025b, R2026a]
runs-on: ${{ matrix.platform }}
continue-on-error: false
steps:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<Info/>
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<Info location="tex-mml-chtml.js.rights" type="File"/>
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<Info/>
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<Info location="tex-mml-chtml.js" type="File"/>
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<Info/>
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<Info location="mathjax-config.js" type="File"/>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Info>
<Category UUID="FileClassCategory">
<Label UUID="design"/>
</Category>
</Info>
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<Info location="docmakerroot.m" type="File"/>
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<Info/>
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<Info location="LaTeXExample.md" type="File"/>
2 changes: 1 addition & 1 deletion tbx/docmaker/Contents.m
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
% MATLAB DocMaker
% Version 0.7 (R2025b) 07-Feb-2026
% Version 0.8 (R2026a) 28-May-2026
%
% docconvert - convert Markdown documents to HTML
% docrun - run MATLAB code in HTML documents and insert output
Expand Down
161 changes: 160 additions & 1 deletion tbx/docmaker/docconvert.m
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,21 @@
% must be a common ancestor of the Markdown documents. If not specified,
% the root folder is the lowest common ancestor.
%
% docconvert(...,"Interpreter",in) uses the interpreter in to postprocess
% inline or display LaTeX expressions. Available interpreters are "latex"
% and "none" (default). Specify inline LaTeX expressions between single
% dollar symbols ($...$), and display LaTeX expressions between double
% dollar symbols ($$...$$).
%
% docconvert(...,"MathRenderer",mr) postprocesses the HTML output to
% ensure LaTeX expressions are well-formed for display with MathJax. This
% option has no effect unless "Interpreter" is "latex". Available math
% renderers are "GitHub", "GitLab", "auto" (uses docmaker.converter to
% select "GitHub" or "GitLab" automatically), or "none". If "Interpreter"
% is "latex" and "MathRenderer" is not specified, then the default value
% is "auto". If "Interpreter" is "none" or unspecified, then
% "MathRenderer" is "none" and no postprocessing is performed.
%
% [html, res] = docconvert(...) returns the names of the HTML document(s)
% html and the resources folder res created.
%
Expand All @@ -37,6 +52,8 @@
options.Stylesheets (1,:) string {mustBeFile}
options.Scripts (1,:) string {mustBeFile}
options.Root (1,1) string {mustBeFolder}
options.Interpreter(1, 1) string {mustBeMember(options.Interpreter, ["latex", "none"])} = "none"
options.MathRenderer(1, 1) string {mustBeMember(options.MathRenderer,["auto", "none", "GitHub", "GitLab"])} = "none"
end

% Initialize output
Expand All @@ -61,6 +78,20 @@
pRoot = superfolder( pMd{:} );
end

% Include math scripts if LaTeX interpreter is requested
if options.Interpreter == "latex"
mathScripts = fullfile( docmakerroot(), "resources", ...
["tex-mml-chtml.js", "mathjax-config.js"] );
if ~isfield( options, "Scripts" )
options.Scripts = mathScripts;
else
options.Scripts = [options.Scripts, mathScripts];
end % if
options.MathRenderer = "auto";
else
options.MathRenderer = "none";
end % if

% Folders
pTem = fullfile( fileparts( mfilename( 'fullpath' ) ), 'resources' );
pRez = fullfile( pRoot, 'resources' );
Expand Down Expand Up @@ -112,6 +143,7 @@
fHtml = fullfile( pMd, nMd + ".html" );
doc = convert( fMd, fCss, fJs );
writer.writeToFile( doc, fHtml, "utf-8" )
cleanLaTeXExpressions( fHtml, options.MathRenderer )
fprintf( 1, "[+] %s\n", fHtml );
oFiles(end+1,:) = fHtml; %#ok<AGROW>
end
Expand Down Expand Up @@ -292,4 +324,131 @@
% Return matching datatype
if iscellstr( varargin ), s = char( s ); end %#ok<ISCLSTR>

end % superfolder
end % superfolder

function cleanLaTeXExpressions( fHTML, renderer )
%CLEANLATEXEXPRESSIONS Postprocess LaTeX expressions after conversion to
%HTML via the GitHub or GitLab APIs. We remove spurious HTML tags and add
%delimiters to the expressions as needed.
%
% For the GitHub API, we replace <em>...</em> with _..._ inside $...$ or
% $$...$$.
%
% Markdown italics and LaTeX subscripts both use an underscore, causing a
% conflict when Markdown is parsed with priority over LaTeX inside a LaTeX
% expression. For example, the underscores in the expression:
%
% $$
% \Phi = \frac{\dot{m}_{th} + \dot{m}}{2} ht_{in} - ...
% \frac{\dot{m}_{th} - \dot{m}}{2} ht_{out}
% $$
%
% are converted to <em>...</em> tags in HTML.
%
% For the GitLab API, we enclose span contents with class="js-render-math"
% and data-math-style="display" or "inline" with $$ and $, respectively.
%
% Any future conflicting syntax will be added to this function on a
% case-by-case basis.

arguments ( Input )
fHTML(1, 1) string {mustBeFile}
renderer(1, 1) string {mustBeMember(renderer, ...
["GitHub", "GitLab", "auto", "none"])}
end % arguments ( Input )

converterType = class( docmaker.converter() );

if renderer == "none"
return
elseif (renderer == "auto" && converterType == "docmaker.GitHub") || ...
renderer == "GitHub"
cleanGitHubLaTeXExpressions( fHTML )
elseif (renderer == "auto" && converterType == "docmaker.GitLab") || ...
renderer == "GitLab"
cleanGitLabLaTeXExpressions( fHTML )
end % if

end % cleanLaTeXExpressions

function cleanGitHubLaTeXExpressions( fHTML )
%CLEANGITHUBLATEXEXPRESSIONS Postprocess the generated HTML to remove
%spurious HTML tags from the LaTeX expressions.

arguments ( Input )
fHTML(1, 1) string {mustBeFile}
end % arguments ( Input )

% Read the file contents.
rawHTML = fileread( fHTML );

% Handle display LaTeX ($$...$$) first, to avoid breaking into inline
% matches ($...$).
LaTeXPatterns = ["\$\$(.*?)\$\$", "\$(.*?)\$\$"];

for patternIdx = 1 : numel( LaTeXPatterns )

% Find matches and their positions.
[matches, startIdx, endIdx] = regexp( ...
rawHTML, LaTeXPatterns(patternIdx), "match", "start", "end" );

% Process the string from the end to the start, so that the earlier
% indices are preserved.
for matchIdx = numel( matches ) : -1 : 1

% Extract the LaTeX from inside the block.
currentBlock = matches{matchIdx};
if startsWith( currentBlock, "$$" )
currentDelimiter = "$$";
innerLaTeX = currentBlock(3:end-2);
elseif startsWith( currentBlock, "$" )
currentDelimiter = "$";
innerLaTeX = currentBlock(2:end-1);
end % if

% Replace the tags.
cleanLaTeX = replace( innerLaTeX, ["<em>", "</em>"], "_" );

% Reassemble the block.
cleanBlock = currentDelimiter + cleanLaTeX + currentDelimiter;

% Replace the block in the original text.
rawHTML = [rawHTML(1:startIdx(matchIdx)-1), ...
cleanBlock{:}, ...
rawHTML(endIdx(matchIdx)+1:end)];

end % for matches

end % for patterns

% Write the file contents.
fileID = fopen( fHTML, "w" );
fprintf( fileID, "%s", rawHTML );
fclose( fileID );

end % cleanGitHubLaTeXExpressions

function cleanGitLabLaTeXExpressions( fHTML )
%CLEANGITLABLATEXEXPRESSIONS Enclose LaTeX span contents within $$ or $.

arguments ( Input )
fHTML(1, 1) string {mustBeFile}
end % arguments ( Input )

% Read the file contents.
rawHTML = fileread( fHTML );

% Display LaTeX → wrap inner content with $$...$$.
displayPattern = '<span[^>]*data-math-style="display"[^>]*>(.*?)</span>';
cleanHTML = regexprep( rawHTML, displayPattern, "$$$1$$" );

% Inline LaTeX → wrap inner content with $...$.
inlinePattern = '<span[^>]*data-math-style="inline"[^>]*>(.*?)</span>';
cleanHTML = regexprep( cleanHTML, inlinePattern, "$$1$" );

% Write the file contents.
fileID = fopen( fHTMLOut, "w" );
fprintf( fileID, "%s", cleanHTML );
fclose( fileID );

end % cleanGitLabLaTeXExpressions
12 changes: 12 additions & 0 deletions tbx/docmaker/docmakerroot.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
function folder = docmakerroot()
%DOCMAKERROOT DocMaker root folder.

% Copyright 2026 The MathWorks, Inc.

arguments ( Output )
folder(1, 1) string {mustBeFolder}
end % arguments ( Output )

folder = fileparts( mfilename( "fullpath" ) );

end % docmakerroot
18 changes: 18 additions & 0 deletions tbx/docmaker/resources/functionSignatures.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,24 @@
"kind": "namevalue",
"type": "folder",
"purpose": "root folder"
},
{
"name": "Interpreter",
"kind": "namevalue",
"type": [
"char",
"choices={'latex','none'}"
],
"purpose": "math interpreter"
},
{
"name": "MathRenderer",
"kind": "namevalue",
"type": [
"char",
"choices={'GitHub','GitLab','auto','none'}"
],
"purpose": "LaTeX renderer"
}
]
},
Expand Down
15 changes: 15 additions & 0 deletions tbx/docmaker/resources/mathjax-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// MathJax configuration
window.MathJax = {
tex: {
inlineMath: [['$', '$']],
displayMath: [['$$', '$$']]
}
};

// Dynamically load MathJax
(function () {
const script = document.createElement('script');
script.src = "tex-mml-chtml.js";
script.async = true;
document.head.appendChild(script);
})();
1 change: 1 addition & 0 deletions tbx/docmaker/resources/tex-mml-chtml.js

Large diffs are not rendered by default.

Loading
Loading