The TMarkdownToHTML Component

TMarkdownToHTML is a non-visual Delphi component that holds the options of the Markdown Processor and converts its Markdown content into HTML. Every time the content or an option changes, the HTML is rebuilt automatically, so the component can be configured at design time in the Object Inspector and used with a single line of code at runtime.
The component is available from version 2.0.0 in the unit MarkdownProcessorComponents.pas (runtime package MarkdownProcessor) and it is registered by the design-time package dclMarkdownProcessor in the Markdown page of the Tool Palette. The names of the properties are the same as the ones of the TMarkdownViewer component of the MarkdownHelpViewer project.
Quick start
Drop a TMarkdownToHTML on a form (or create it in code), assign the Markdown and read the HTML:
uses
MarkdownUtils, MarkdownProcessorComponents;
procedure TMainForm.ShowHTML;
begin
MarkdownToHTML.MarkdownContent.Text := '# Hello' + sLineBreak + 'Some **Markdown** text';
HTMLMemo.Lines.Text := MarkdownToHTML.HtmlContent.Text;
end;Creating it in code:
var
LConverter: TMarkdownToHTML;
begin
LConverter := TMarkdownToHTML.Create(nil);
try
LConverter.CssStyle.Clear; // only the HTML fragment
LConverter.ProcessorDialect := mdGFM; // resets Extensions to the GFM defaults
LConverter.Extensions := LConverter.Extensions - [mexSmartTypography];
LConverter.FileName := 'Readme.md'; // loads and converts the file
LConverter.ExportToFileHTML('Readme.html');
finally
LConverter.Free;
end;
end;Default options
A new component converts with the default dialect, GitHub, in safe mode, and writes the default stylesheet before the HTML. Besides the extensions of the dialect, the component enables by default all the legacy extensions of the Markdown Processor (constant LegacyExtensions): they act only on their own syntax, so they do not change a document that does not use it.
| Dialect | Default Extensions of the component |
|---|---|
mdGitHub (default) | GitHubExtensions (tables, task lists, strikethrough, autolinks, tag filter, math, alerts, mermaid) + LegacyExtensions |
mdGFM | GFMExtensions (tables, task lists, strikethrough, autolinks, tag filter) + LegacyExtensions |
mdCommonMark | LegacyExtensions |
mdDaringFireball, mdTxtMark | none: the legacy dialects ignore the extensions |
LegacyExtensions = [mexSubscript, mexSuperscript, mexInsert, mexMark, mexSmartTypography, mexHeadingAttributes, mexAutoHeadingIds, mexWikiLinks]: H~2~O, x^2^, ++inserted++, ==marked==, smart typography (--, ---, ..., (C), curly quotes, guillemets), # Title {#id}, GitHub-style heading ids and [[wiki links]]. See Extensions for the complete list of the flags.
Changing ProcessorDialect always resets Extensions to the defaults of the new dialect: set the dialect first, then add or remove the extensions. In the form file Extensions is stored only when it differs from the defaults of the dialect.
Published properties
| Property | Type | Default | Description |
|---|---|---|---|
ProcessorDialect | TMarkdownProcessorDialect | mdGitHub | The Markdown dialect: mdGitHub, mdGFM, mdCommonMark, mdDaringFireball, mdTxtMark. Changing it resets Extensions. |
Extensions | TMarkdownExtensions | DefaultExtensions(ProcessorDialect) | The optional syntax of the new engine (GitHub, GFM, CommonMark); ignored by the legacy dialects. |
AllowUnsafe | Boolean | False | When True, raw HTML (script, iframe...) and every link are kept as they are: use it only with trusted content. With False raw HTML is omitted and dangerous links are emptied. |
MathRendering | TMarkdownMathRendering | mmrMarkup | mmrMarkup: markup for KaTeX/MathJax (viewers with JavaScript, e.g. WebView2); mmrCodeCogsImage: every formula is an image, for viewers without JavaScript (e.g. HTMLViewer). |
CssStyle | TStringList | MarkdownDefaultCSS | The stylesheet written before the HTML fragment in HtmlContent. Clear it to get the bare fragment. Stored only when it differs from the default. |
FileName | TFileName | Assigning the name of an existing file loads it (see LoadFromFile). | |
MarkdownContent | TStringList | The Markdown source: every change converts it again. | |
HtmlContent | TStringList | The result: CssStyle followed by the HTML fragment. It can also be assigned directly (for example to show an HTML file): when MarkdownContent is empty it is kept as it is. | |
OnChange | TNotifyEvent | Fired after HtmlContent changes. |
Public properties
| Property | Type | Description |
|---|---|---|
CodeBlockEmitter | TBlockEmitter | When assigned, receives every code block (fenced and indented, mermaid included) with its lines and its info string, and writes the HTML of the block: use it for syntax highlighting. Not owned by the component. |
SpecialLinkEmitter | TSpanEmitter | With mexWikiLinks, receives the content of every [[...]] link and writes its HTML; without it the link is written as text. Not owned by the component. |
Unlike TMarkdownProcessor.Config, the component does not free the emitters assigned to it: the application creates them and frees them (after detaching them, or after freeing the component).
type
// [[Page name]] -> <a href="Page%20name.md">Page name</a>
TWikiLinkEmitter = class(TSpanEmitter)
public
procedure emitSpan(out_: TStringBuilder; content: String); override;
end;
procedure TWikiLinkEmitter.emitSpan(out_: TStringBuilder; content: String);
begin
out_.Append('<a href="' + StringReplace(content, ' ', '%20', [rfReplaceAll]) +
'.md">' + content + '</a>');
end;
// FormCreate
FWikiLinkEmitter := TWikiLinkEmitter.Create;
MarkdownToHTML.SpecialLinkEmitter := FWikiLinkEmitter;
// FormDestroy
MarkdownToHTML.SpecialLinkEmitter := nil;
FWikiLinkEmitter.Free;Methods
| Method | Description |
|---|---|
procedure UpdateHTML | Converts MarkdownContent again into HtmlContent and fires OnChange. It is called automatically when the content or an option changes; nothing happens when MarkdownContent is empty. |
procedure LoadFromFile(const AFileName: TFileName) | Loads a file: .htm/.html files go to HtmlContent, any other file to MarkdownContent (and it is converted). The encoding is detected as in TryLoadTextFile. |
procedure LoadFromStream(const AStream: TStringStream; const IsHTMLContent: Boolean = False) | Loads the text of a stream as Markdown, or as HTML when IsHTMLContent is True. |
procedure LoadFromString(const AValue: string; const IsHTMLContent: Boolean = False) | Loads a string as Markdown (converted once), or as HTML. |
procedure ExportToFileHTML(const AFileName: TFileName) | Saves HtmlContent to a UTF-8 file. |
function TransformContent(const AMarkdownContent: string): string | Converts a Markdown text with the options of the component and returns CssStyle followed by the HTML fragment, without changing MarkdownContent and HtmlContent. |
function TransformContent(const AMarkdownContent: string; AProcessorDialect: TMarkdownProcessorDialect; const ACssStyle: string = ''; const AAllowUnsafe: Boolean = False): string | Converts a Markdown text with the given dialect (with the extensions of the dialect in the library), stylesheet and safe mode, as TMarkdownViewer.TransformContent. |
class function DefaultExtensions(const ADialect: TMarkdownProcessorDialect): TMarkdownExtensions | The default extensions of the component for a dialect (see Default options). |
function CreateProcessor: TMarkdownProcessor (protected, virtual) | Creates the TMarkdownProcessor with the options of the component: override it in a descendant of TCustomMarkdownToHTML to configure the processor further. |
Utility functions
The unit MarkdownProcessorComponents.pas also exposes:
| Function | Description |
|---|---|
function TryLoadTextFile(const AFileName: TFileName): string | Loads a text file: the BOM, when present, decides the encoding; otherwise UTF-8 if the bytes are valid UTF-8, else ANSI. |
procedure SaveUTF8File(const AFileName: TFileName; const AContent: string) | Saves a string to a UTF-8 file. |
function GetMarkdownDefaultCSS: string | The default stylesheet (MarkdownDefaultCSS). |
Showing the HTML
HtmlContent is an HTML fragment preceded by the stylesheet, not a full page. To show it in a browser or in WebView2, put it in a page; when the document contains math formulas or mermaid diagrams (GitHub dialect), the page must also load KaTeX and mermaid.js, as the Demo does:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css">
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/contrib/auto-render.min.js"
onload="renderMathInElement(document.body);"></script>
<script type="module">
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
mermaid.initialize({ startOnLoad: true });
</script>For a viewer without JavaScript (e.g. HTMLViewer) set MathRendering := mmrCodeCogsImage: the formulas become images, while mermaid diagrams show their source text.
