Is there a way to export a Mathematica notebook into Markdown?
I'm sure it's possible to implement rules for converting each Mathematica cell subexpression into Markdown, but I'm also sure that I don't know enough about BoxForms to get this right!
Is there a way to export a Mathematica notebook into Markdown?
I'm sure it's possible to implement rules for converting each Mathematica cell subexpression into Markdown, but I'm also sure that I don't know enough about BoxForms to get this right!
10.01.2020 v0.2.0 Export to file and output images
25.05.2018 v0.1.0 GitHub release https://github.com/kubaPod/M2MD
In this post only the original code is kept. For most recent versions visit GH.
In order to install:
ResourceFunction["GitHubInstall"]["kubapod", "m2md"]
Or visit GH readme.
Here are related solutions in case this does not fit your needs:
Markdown capabilities differ from environment to environment so I expect this will be only a base that one can modify so it fits his/her needs best.
StackExchange MD supports only H1-H3 headers and Sub/sub/Titles are cell styles which I've associated with them. Sub/sub/Sections are associated with H4-H6 so you work more with those you may change it since the latter group will look here as ordinary text.
An Output cells generated by whatever // TraditionalForm are automatically $\LaTeX$ blocks. An Inline TraditionalForm is inline $\LaTeX$.
At the moment only this one type of Output cell is parsed.
Unknown cell styles leave MD comment: [//]: # (No rules defined for *CellStyleName*), which is not displayed ofc.
I tried to convert Hyperlinks and it seems to work.
Sub and Subsub ItemsNumbered only get prefix in form: 1., 2. etc. I could put there what we see in MMA, like 1.2.1. but it doesn't seem to be supported in MD. Or rather, it is sometimes but up to SubitemNumbered and no deeper.
I've put it as a procedure in palette which can convert things quite nicely.
This is a test notebook I'm using and the result of tests pasted here:
CreateDocument@ Uncompress@ "1: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"
(^ double click to select. Don't evaluate this if the last edit was made by someone else than me)
Enter text here. Enter TraditionalForm input for evaluation in a separate cell below:
Integrate[x, x] + Sqrt[x] // TraditionalForm
$$\frac{x^2}{2}+\sqrt{x}$$
Item asdasd
ItemParagraph
Text cell
Text with inline formula: $4$.
ItemNumbered
ItemParagraph
SubitemNumbered
SubitemParagraph
SubsubitemNumbered
SubsubitemParagraph
fun[x_] := 1
CreatePalette[#, CellContext -> Notebook] &@DynamicModule[{},
Button["Export to Markdown",
CreateDocument@exportMD@InputNotebook[]],
Initialization :> (
itemIndent = " ";
codeIndent = " ";
itemMark = "+ ";
itemPrefix = Function[{cellObj, style}, Module[{
ind, depth, numberedQ, paragraphQ},
ind = ToString@CurrentValue[cellObj, {"CounterValue", style}];
depth = StringCount[style, "sub", IgnoreCase -> True];
numberedQ =
StringCount[style, "numbered", IgnoreCase -> True] > 0;
paragraphQ =
StringCount[style, "paragraph", IgnoreCase -> True] > 0;
StringJoin@Flatten@{
ConstantArray[itemIndent, depth + If[paragraphQ, 2, 1]],
Which[
numberedQ, {ind, ". "},
paragraphQ, "",
True, itemMark]
}
]];
prefix[styleName_] := Switch[styleName,
"Title", "# ",
"Subtitle", "## ",
"Subsubtitle", "### ",
"Section", "#### ",
"Subsection", "##### ",
"Subsubsection", "###### ",
"Text", "",
"items", itemPrefix,
"code", codeIndent
];
styleWrapper[opts___] := Module[{italic, bold, wrapper
},
italic = MemberQ[{opts}, Verbatim[Rule][FontSlant, "Italic"]];
bold = MemberQ[{opts}, Verbatim[Rule][FontWeight, "Bold"]];
wrapper = Which[
bold, "**",
italic, "*",
True, ""
];
wrapper <> # <> wrapper &
];
parseCodeData[data_] := StringReplace[
First[FrontEndExecute[FrontEnd`ExportPacket[data, "InputText"]]],
"\n" -> "\n" <> codeIndent
];
textStyleQ = (StringCount[#, "title" | "section" | "text",
IgnoreCase -> True] > 0) &;
itemStyleQ = (StringCount[#, "item", IgnoreCase -> True] >
0) &;
codeStyleQ = MemberQ[{"Code", "Input"}, #] &;
exportMD[nb_NotebookObject] :=
StringJoin@Flatten[exportMD /@ Cells[nb]];
exportMD[cellObj_CellObject] :=
exportMD[NotebookRead[cellObj], cellObj];
exportMD[cell_Cell, cellObj_CellObject] :=
exportMD[#2, #, cellObj] & @@ cell;
exportMD[style_?textStyleQ, data_, cellObj_CellObject] := {
prefix[style], addPrefix[style] /@ Flatten@{parseData[data]},
"\n\n"
};
addPrefix[style_][expr : Except[_String]] := expr;
addPrefix[style_][s_String] :=
StringReplace[s, "\n" -> "\n" <> prefix[style]];
exportMD[style_?itemStyleQ, data_, cellObj_CellObject] := {
prefix["items"][cellObj, style], parseData@data, "\n\n"};
exportMD[style_?codeStyleQ, data_, cellObj_CellObject] := {
"\n\n----------\n\n",
codeIndent, parseCodeData@data,
"\n\n"};
exportMD["Output", BoxData[FormBox[boxes_, TraditionalForm]],
cellObj_CellObject] := TemplateApply[
"<span class="math-container">$$``$$</span>\n\n", {boxesToTeX@boxes}
];
parseData[list_List] := parseData /@ list;
parseData[string_String] := string;
parseData[data_ (BoxData | TextData)] :=
List @@ (parseData /@ data);
parseData[cell_Cell] :=
parseData@First@cell; (*inlince cells style skipped*)
parseData[StyleBox[expr_, opts___]] :=
styleWrapper[opts]@parseData[expr];
parseData[
FormBox[boxes : Except[_TagBox], TraditionalForm, ___]] :=
Module[{teXForm},
teXForm = boxesToTeX@boxes;
"<span class="math-container">$" <> teXForm <> "$</span>"
];
parseData[
box : ButtonBox[_, ___, BaseStyle -> "Hyperlink", ___]] :=
Module[{label, url},
{label, url} = {#, #2} & @@ ToExpression[box];
TemplateApply[
"[``](``)", {StringJoin@Flatten@{parseData@label}, url}]
];
(*default behaviour for boxes*)
parseData[boxes_] := parseData@First@boxes;
(*default behaviour for cell styles*)
exportMD[s_, ___] :=
TemplateApply["[//]: # (No rules defined for ``)\n\n", {s}];
boxesToTeX = ToString[ToExpression@#, TeXForm] &;
)
]
TraditionalForm[] objects are the ones converted to $\LaTeX$ (that is, TraditionalForm[Sqrt[z]] becomes $\sqrt z$, perhaps through TeXForm[])?
– J. M.'s missing motivation
Nov 22 '15 at 03:28
parseData[box:ButtonBox...] in order to get proper exports of the links in the "References" section.
– Anton Antonov
Jun 03 '16 at 02:55
StyleBox[content, "namedStyle"]). The first was easy to adjust to suite, the third likely will be, too, but the second will take some thought. Definitely, +1.
– rcollyer
Apr 24 '19 at 20:57
URLFetch["heckyesmarkdown.com/go/", "Parameters" -> {"html" -> ExportString[nb, "HTML"]}]wherenbis the NotebookObject. – bobthechemist May 27 '15 at 17:31