50
$\begingroup$

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!

$\endgroup$
14
  • 1
    $\begingroup$ Related meta posts: meta.mathematica.stackexchange.com/questions/990/…, meta.mathematica.stackexchange.com/questions/1286/… $\endgroup$
    – Michael E2
    Commented May 27, 2015 at 16:55
  • 7
    $\begingroup$ Mathematic's nb conversion to html is terrible, cell and graphics styles and formatting never export correctly (mostly unsupported) to html and printing notebooks is a fool's errand. Am I off base here? $\endgroup$
    – M.R.
    Commented May 27, 2015 at 21:15
  • 8
    $\begingroup$ I'd use the more politically correct 'suboptimal' ;-) $\endgroup$ Commented May 27, 2015 at 21:20
  • 2
    $\begingroup$ In R it's very elegant and straightforward. See RMarkdown $\endgroup$
    – Murta
    Commented May 29, 2015 at 12:49
  • 1
    $\begingroup$ It would suffice to export the cell group structure to json, anyone know how to do that? $\endgroup$
    – M.R.
    Commented Oct 6, 2015 at 16:12

1 Answer 1

47
+200
$\begingroup$

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:


Original post

Features

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.


Tests

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)

Result


Title bold

Subtitle italic

Subsubtitle

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

    • Subitem

Text cell

Title with Hyperlink: Wolfram Research, Inc. and

a TraditionalForm expression: $\frac{x^2}{2}+\sqrt{x}$

(both are in InlineCells)

Text with inline formula: $4$.

  1. ItemNumbered

    ItemParagraph

    1. SubitemNumbered

      SubitemParagraph

      1. SubsubitemNumbered

        SubsubitemParagraph


fun[x_] := 1 

/Result

Code for palette

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[

      "$$``$$\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;

      "$" <> teXForm <> "$"

      ];

    

    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] &;

    )

  ]
$\endgroup$
18
  • $\begingroup$ Idle thought: what if TraditionalForm[] objects are the ones converted to $\LaTeX$ (that is, TraditionalForm[Sqrt[z]] becomes $\sqrt z$, perhaps through TeXForm[])? $\endgroup$ Commented Nov 22, 2015 at 3:28
  • $\begingroup$ Nice! What about image and graphics? You could save them as files and hyperlink them in. I'll try running this as soon as I get back from the Dominican Republic. $\endgroup$
    – M.R.
    Commented Nov 22, 2015 at 6:47
  • $\begingroup$ And maybe comment the stuff it can't parse. $\endgroup$
    – M.R.
    Commented Nov 22, 2015 at 9:24
  • $\begingroup$ Have you seen this (by R.M.) already? $\endgroup$ Commented Nov 23, 2015 at 9:00
  • 2
    $\begingroup$ Just roadtested it and already seems to go a fair way. Great! Yes images, code-formatting, inline cells etc would all be handy improvements - agree with @VitaliyKaurov, be nice to bring it all together into an evolving package with R.M.'s work and perhaps is to be part of Leonid's Front-End IDE project? Being able to build large projects with an inset window to seamlessly put/get SE answers would be as valuable as the documentation IMO. Also imagine that once set up/refined, it could be included in welcoming message to all new SE users. $\endgroup$ Commented Jan 1, 2016 at 8:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.