Exporting a notebook to HTML in a way that worked with my site's styles was far easier than I anticipated:

Export["body.html", EvaluationNotebook[], "HTML",
 "CSS" -> None,
 "FullDocument" -> False,
 "GraphicsOutput" -> "PNG",
 "ConversionRules" -> {
   "Title" -> {"<h1>", "</h1>"},
   "Section" -> {"<h2>", "</h2>"},
   "Subsection" -> {"<h3>", "</h3>"},
   "Text" -> {"<p>", "</p>"},
   "Input" -> {"<pre>", "</pre>"},
   "Item" -> {"<li>", "</li>"}

However, there are a few effects that I think I need to understand cell grouping to achieve:

  • Contain list items in <ul> or <ol> tags.
  • Contain subsets of cells in a <div> tag with a custom class, to render margin notes.

My reading indicates that I should be able to achieve this by grouping cells, but no matter how I do it, the ConversionRules still get applied independently, so I haven't found a hook that would allow me to put tags around multiple cells.


1 Answer 1


Looking through the code, I am not sure this is easily achievable using ConversionRules alone. Here's an attempt at modifying the conversion functions directly:

  Cell[CellGroupData[cells_List, _]], toFormat_, toFormatStream_, 
  others__] := (
  WriteString[toFormatStream, "<div class=\"XXX\">\n"];
     System`Convert`CommonDump`ConvertCell[#, toFormat, 
       toFormatStream, others];]], cells];
  WriteString[toFormatStream, "</div>\n"];

   Cell[CellGroupData[cells_List, _]], toFormat_, toFormatStream_, 
   others__] /;
  AllTrue[MatchQ[Cell[__, "Item", ___]]]@cells :=
  WriteString[toFormatStream, "<ul>\n"];
     System`Convert`CommonDump`ConvertCell[#, toFormat, 
       toFormatStream, others];]], cells];
  WriteString[toFormatStream, "</ul>\n"];

You can then export the notebook as you did before:

ExportString[EvaluationNotebook[], "HTML", "CSS" -> None, 
 "FullDocument" -> False, "GraphicsOutput" -> "PNG", 
 "ConversionRules" -> {"Title" -> {"<h1>", "</h1>"}, 
   "Section" -> {"<h2>", "</h2>"}, "Subsection" -> {"<h3>", "</h3>"}, 
   "Text" -> {"<p>", "</p>"}, "Input" -> {"<pre>", "</pre>"}, 
   "Item" -> {"<li>", "</li>"}}]
(* "<div class=\"XXX\"><h2>Some section</h2>
<p>Some items:</p>
<ul><li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</div><div class=\"XXX\"><h2>Another section</h2>
<p>Some code:</p>
<div class=\"XXX\">
<pre><img \
src=\"HTMLFiles/m-d39f35de-cf25-41ad-8dc9-45e38a941224_1.gif\" \
alt=\"m-d39f35de-cf25-41ad-8dc9-45e38a941224_1.gif\" width=\"49\" \
height=\"15\" style=\"vertical-align:middle\" /></pre><p \
 <img src=\"HTMLFiles/m-d39f35de-cf25-41ad-8dc9-45e38a941224_2.gif\" \
alt=\"m-d39f35de-cf25-41ad-8dc9-45e38a941224_2.gif\" width=\"7\" \
height=\"15\" style=\"vertical-align:middle\" />
</div><div class=\"XXX\"><pre><img \
src=\"HTMLFiles/m-d39f35de-cf25-41ad-8dc9-45e38a941224_3.gif\" \
alt=\"m-d39f35de-cf25-41ad-8dc9-45e38a941224_3.gif\" width=\"735\" \
height=\"73\" style=\"vertical-align:middle\" /></pre><p \
 <img src=\"HTMLFiles/m-d39f35de-cf25-41ad-8dc9-45e38a941224_4.gif\" \
alt=\"m-d39f35de-cf25-41ad-8dc9-45e38a941224_4.gif\" width=\"774\" \
height=\"796\" style=\"vertical-align:middle\" />
</div>" *)

The notebook I used to test this:

enter image description here

Note that I had to manually group the list items in order for the code to identify the list. The code above works by overriding/extending the rules for how to convert Cell[CellGroupData[...]], which is what represents a cell group. I changed the default rule to print <div class="XXX"> before and </div> after the group. I then added a second rule that matches only when all child cells have style "Item", in which case I wrap the contents with <ul>/</ul>.

You can similarly handle other types of grouping by adding additional rules. Note that if you want to support nested lists for example, you will have to make the check for lists above more robust.

  • $\begingroup$ Thank you so much for this. It's my first time overriding behavior in a package, though. Do you think it will be reasonably stable across releases? If it breaks, where can I read the code to see what changed? I see exactly one result on Google for "System Convert CommonDump ConvertCell" with no quotes, and it's this page. 😅 $\endgroup$
    – alltom
    May 3 at 5:48
  • 1
    $\begingroup$ @alltom As with all undocumented things, it's "use at your own risk, anything may happen". However, with stuff like this, I am usually not too worried. In all likelihood, the overall mechanism will stay the same, maybe the exact context changes to something different than System`Convert`CommonDump` . As for how to check: See GeneralUtilities`PrintDefinitions (there are many questions on this site on how to use it, and it's even somewhat documented via the WFR) $\endgroup$
    – Lukas Lang
    May 3 at 21:02

Your Answer

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

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