module
Markd::Pdf
Defined in:
hyphenation.crpdf.cr
pdf_renderer.cr
pdf_styles.cr
Constant Summary
-
DARK_CODE_THEME =
"monokai" -
Syntax highlighting theme used for the dark style when neither --code-theme nor -t is given: the light default would glow on a dark page.
-
DEFAULT_CODE_THEME =
"friendly" -
Syntax highlighting theme used when nothing better is known: a classic light style that reads well on the default light page.
-
DEFAULT_CSS =
STYLES["default"] -
The original name of the base stylesheet, kept for compatibility.
-
KDP_CSS =
"h1 { page-break-before: right }" -
The production layer kdp mode adds on top of the style: chapters (top-level headings) open on a right-hand page, with blank filler pages when the previous chapter ended on a verso one. User layers (--css) win over it, so retargeting or turning it off is a one-line override.
-
KDP_GUTTER_TABLE =
[{24, 150, 9.525}, {151, 300, 12.7}, {301, 500, 15.875}, {501, 700, 19.05}, {701, 828, 22.225}] -
KDP paperback gutter by page count (Amazon's table, in mm).
-
KDP_MIN_OUTSIDE_MARGIN_MM =
6.35 -
KDP's minimum margin on every side for no-bleed interiors, in mm (0.25 inch). All four sides are outside edges on one page parity: recto keeps the gutter on the left and the outside edge right, verso mirrors that.
-
MAX_IMAGE_BYTES =
((ENV["MARKPDF_MAX_IMAGE_MB"]?.try(&.to_i?) || 8) * 1024) * 1024 -
Remote image bodies larger than this are refused (MARKPDF_MAX_IMAGE_MB overrides the 8 MB default).
-
MAX_PAGE_DIMENSION =
5000.0 -
MAX_TOC_PASSES =
6 -
Renders until the TOC page numbers (and, when sizing the kdp gutter, the gutter) stop moving: each pass lays out the document with the current TOC block, then rebuilds that block from where the headings actually landed — the TOC's own length shifts every page after it, so one pass can never know its numbers. Two or three passes converge; the cap is a safety net for pathological documents. Yields the gutter to use this pass (-1 when the caller sizes margins itself) and the TOC block to render with (nil = none), and returns the final page count. Internal, but shared with the CLI, which owns its Renderer construction.
-
MIN_PAGE_DIMENSION =
10.0 -
Largest page dimension the PDF format allows (14400pt), and a floor small enough that a margin still leaves room to draw.
-
MINIMUM_HYPHENATED_LENGTH =
5 -
Words shorter than this are never worth hyphenating.
-
NO_HYPHENATION_ELEMENTS =
{"pre", "code", "script", "style"} -
Elements whose contents are never hyphenated: code, formulas and anything a renderer would not wrap word-by-word either.
-
PAGE_SIZES =
{"a0" => {841.0, 1189.0}, "a1" => {594.0, 841.0}, "a2" => {420.0, 594.0}, "a3" => {297.0, 420.0}, "a4" => {210.0, 297.0}, "a5" => {148.0, 210.0}, "a6" => {105.0, 148.0}, "b0" => {1000.0, 1414.0}, "b1" => {707.0, 1000.0}, "b2" => {500.0, 707.0}, "b3" => {353.0, 500.0}, "b4" => {250.0, 353.0}, "b5" => {176.0, 250.0}, "b6" => {125.0, 176.0}, "letter" => {215.9, 279.4}, "legal" => {215.9, 355.6}} -
Page sizes by name: the ISO 216 A and B series (portrait, in mm) plus the US sizes people keep asking for. Custom sizes pass a "WxH" string in millimeters instead of a name.
-
STYLE_DESCRIPTIONS =
{"default" => "clean sans-serif print style", "book" => "serif, justified, indented — for long prose / e-readers", "dark" => "dark page, light text — for screen reading", "sepia" => "warm paper tones, serif — e-reader default look"} -
STYLES =
{"default" => "body { font-family: \"DejaVu Sans\", Helvetica, Arial, sans-serif; font-size: 11px; line-height: 1.5; color: #1a1a1a; margin: 0; padding: 0; }\nh1 { font-size: 25px; font-weight: bold; margin: 20px 0 12px 0; }\nh2 { font-size: 19px; font-weight: bold; margin: 18px 0 10px 0; }\nh3 { font-size: 15px; font-weight: bold; margin: 16px 0 8px 0; }\nh4 { font-size: 12px; font-weight: bold; margin: 14px 0 8px 0; }\nh5 { font-size: 11px; font-weight: bold; margin: 12px 0 6px 0; }\nh6 { font-size: 11px; font-weight: bold; color: #555555; margin: 12px 0 6px 0; }\np { margin: 0 0 9px 0; }\na { color: #0645ad; }\nstrong { font-weight: bold; }\nem { font-style: italic; }\ndel { text-decoration: line-through; }\nul, ol { margin: 0 0 9px 0; padding-left: 18px; }\nul ul, ol ol, ul ol, ol ul { margin: 0 0 9px 0; padding-left: 16px; }\nli { margin: 0 0 3px 0; }\nli.task-list-item { list-style-type: none; }\nblockquote { border-left: 4px solid #cccccc; margin: 9px 0 9px 0; padding: 2px 0 2px 12px; color: #444444; }\nblockquote p { margin: 0 0 6px 0; }\npre { font-family: \"DejaVu Sans Mono\", \"Liberation Mono\", Courier, monospace; font-size: 10px; background-color: #f6f6f6; border: 1px solid #e0e0e0;\n margin: 9px 0 9px 0; padding: 7px 9px 7px 9px; overflow: hidden;\n white-space: pre-wrap; }\ntable { overflow: hidden; }\ncode { font-family: \"DejaVu Sans Mono\", \"Liberation Mono\", Courier, monospace; font-size: 10px; background-color: #f2f2f2; padding: 0px 2px 0px 2px; }\npre code { background-color: transparent; padding: 0; }\ntable { border-spacing: 0; margin: 9px 0 9px 0; font-size: 10.5px; }\nth { font-weight: bold; border: 1px solid #cccccc; background-color: #f2f2f2; padding: 4px 8px 4px 8px; text-align: left; }\ntd { border: 1px solid #cccccc; padding: 4px 8px 4px 8px; }\nhr { border-bottom: 1px solid #bbbbbb; margin: 18px 0 18px 0; }\nimg { margin: 6px 0 6px 0; max-width: 100%; }\n.alert { border: none; border-left: 4px solid #666666; padding: 8px 12px 4px 12px; margin: 9px 0 9px 0; }\n.alert p { margin: 0 0 6px 0; }\n.alert-title { font-weight: bold; }\n.alert-note { border-left: 4px solid #2e6f9e; }\n.alert-tip { border-left: 4px solid #2e9e5b; }\n.alert-important { border-left: 4px solid #7e4fd0; }\n.alert-warning { border-left: 4px solid #d0962e; }\n.alert-caution { border-left: 4px solid #d0342c; }\n.footnotes { border-top: 1px solid #bbbbbb; margin-top: 18px; font-size: 10px; color: #444444; }\nu, ins { text-decoration: underline; }\nq { font-style: italic; }\nabbr { border-bottom: 1px dotted #888888; }\nmark { background-color: #fff3a3; }\nkbd { font-family: \"DejaVu Sans Mono\", \"Liberation Mono\", Courier, monospace; font-size: 10px; background-color: #f2f2f2; border: 1px solid #cccccc; padding: 0px 3px 0px 3px; }\n.math { font-family: \"DejaVu Serif\", Georgia, serif; font-style: italic; }\n.math.block { display: block; text-align: center; margin: 6px 0 6px 0; }\npre.math { text-align: left; background-color: transparent; border: none; line-height: 1.1; margin: 6px auto 6px auto; }\n#{TOC_CSS}", "book" => "body { font-family: \"DejaVu Serif\", Georgia, serif; font-size: 12px; line-height: 1.6; color: #1a1a1a; margin: 0; padding: 0; }\nh1, h2, h3 { text-align: center; }\nh1 { font-size: 24px; font-weight: bold; margin: 26px 0 18px 0; }\nh2 { font-size: 19px; font-weight: bold; margin: 20px 0 12px 0; }\nh3 { font-size: 15px; font-weight: bold; margin: 16px 0 8px 0; }\nh4 { font-size: 12px; font-weight: bold; margin: 14px 0 8px 0; }\nh5 { font-size: 11.5px; font-weight: bold; margin: 12px 0 6px 0; }\nh6 { font-size: 11.5px; font-style: italic; color: #555555; margin: 12px 0 6px 0; }\np { margin: 0; text-indent: 1.5em; text-align: justify; }\na { color: #0645ad; }\nstrong { font-weight: bold; }\nem { font-style: italic; }\ndel { text-decoration: line-through; }\nul, ol { margin: 0 0 9px 0; padding-left: 18px; }\nul ul, ol ol, ul ol, ol ul { margin: 0 0 9px 0; padding-left: 16px; }\nli { margin: 0 0 3px 0; text-align: justify; }\nli.task-list-item { list-style-type: none; }\nli p { margin: 0 0 3px 0; text-indent: 0; }\nblockquote { border-left: 4px solid #cccccc; margin: 9px 0 9px 0; padding: 2px 0 2px 12px; color: #444444; font-size: 11.5px; }\nblockquote p { margin: 0 0 6px 0; text-indent: 0; }\npre { font-family: \"DejaVu Sans Mono\", \"Liberation Mono\", Courier, monospace; font-size: 10px; background-color: #f6f6f6; border: none;\n margin: 9px 0 9px 0; padding: 7px 9px 7px 9px; overflow: hidden;\n white-space: pre-wrap; }\ntable { overflow: hidden; }\ncode { font-family: \"DejaVu Sans Mono\", \"Liberation Mono\", Courier, monospace; font-size: 10px; background-color: #f2f2f2; padding: 0px 2px 0px 2px; }\npre code { background-color: transparent; padding: 0; }\ntable { border-spacing: 0; margin: 9px 0 9px 0; font-size: 10.5px; }\nth { font-weight: bold; border: 1px solid #cccccc; background-color: #f2f2f2; padding: 4px 8px 4px 8px; text-align: left; }\ntd { border: 1px solid #cccccc; padding: 4px 8px 4px 8px; }\nhr { border-bottom: 1px solid #bbbbbb; margin: 18px 0 18px 0; }\nimg { margin: 6px 0 6px 0; max-width: 100%; }\n.alert { border: none; border-left: 4px solid #666666; padding: 8px 12px 4px 12px; margin: 9px 0 9px 0; font-size: 11.5px; }\n.alert p { margin: 0 0 6px 0; text-indent: 0; }\n.alert-title { font-weight: bold; }\n.alert-note { border-left: 4px solid #2e6f9e; }\n.alert-tip { border-left: 4px solid #2e9e5b; }\n.alert-important { border-left: 4px solid #7e4fd0; }\n.alert-warning { border-left: 4px solid #d0962e; }\n.alert-caution { border-left: 4px solid #d0342c; }\n.footnotes { border-top: 1px solid #bbbbbb; margin-top: 18px; font-size: 10px; color: #444444; }\nu, ins { text-decoration: underline; }\nq { font-style: italic; }\nabbr { border-bottom: 1px dotted #888888; }\nmark { background-color: #fff3a3; }\nkbd { font-family: \"DejaVu Sans Mono\", \"Liberation Mono\", Courier, monospace; font-size: 10px; background-color: #f2f2f2; border: 1px solid #cccccc; padding: 0px 3px 0px 3px; }\n.math { font-family: \"DejaVu Serif\", Georgia, serif; font-style: italic; }\n.math.block { display: block; text-align: center; margin: 6px 0 6px 0; }\npre.math { text-align: left; background-color: transparent; border: none; line-height: 1.1; margin: 6px auto 6px auto; }\n#{TOC_CSS}", "dark" => "body { font-family: \"DejaVu Sans\", Helvetica, Arial, sans-serif; font-size: 11px; line-height: 1.5; color: #e8e8e8; background-color: #121212; margin: 0; padding: 0; }\nh1 { font-size: 25px; font-weight: bold; margin: 20px 0 12px 0; }\nh2 { font-size: 19px; font-weight: bold; margin: 18px 0 10px 0; }\nh3 { font-size: 15px; font-weight: bold; margin: 16px 0 8px 0; }\nh4 { font-size: 12px; font-weight: bold; margin: 14px 0 8px 0; }\nh5 { font-size: 11px; font-weight: bold; margin: 12px 0 6px 0; }\nh6 { font-size: 11px; font-weight: bold; color: #aaaaaa; margin: 12px 0 6px 0; }\np { margin: 0 0 9px 0; }\na { color: #8ab4f8; }\nstrong { font-weight: bold; }\nem { font-style: italic; }\ndel { text-decoration: line-through; }\nul, ol { margin: 0 0 9px 0; padding-left: 18px; }\nul ul, ol ol, ul ol, ol ul { margin: 0 0 9px 0; padding-left: 16px; }\nli { margin: 0 0 3px 0; }\nli.task-list-item { list-style-type: none; }\nblockquote { border-left: 4px solid #444444; margin: 9px 0 9px 0; padding: 2px 0 2px 12px; color: #bbbbbb; }\nblockquote p { margin: 0 0 6px 0; }\npre { font-family: \"DejaVu Sans Mono\", \"Liberation Mono\", Courier, monospace; font-size: 10px; background-color: #1e1e1e; border: 1px solid #333333;\n margin: 9px 0 9px 0; padding: 7px 9px 7px 9px; overflow: hidden;\n white-space: pre-wrap; }\ntable { overflow: hidden; }\ncode { font-family: \"DejaVu Sans Mono\", \"Liberation Mono\", Courier, monospace; font-size: 10px; background-color: #1e1e1e; padding: 0px 2px 0px 2px; }\npre code { background-color: transparent; padding: 0; }\ntable { border-spacing: 0; margin: 9px 0 9px 0; font-size: 10.5px; }\nth { font-weight: bold; border: 1px solid #444444; background-color: #1e1e1e; padding: 4px 8px 4px 8px; text-align: left; }\ntd { border: 1px solid #444444; padding: 4px 8px 4px 8px; }\nhr { border-bottom: 1px solid #444444; margin: 18px 0 18px 0; }\nimg { margin: 6px 0 6px 0; max-width: 100%; }\n.alert { border: none; border-left: 4px solid #888888; padding: 8px 12px 4px 12px; margin: 9px 0 9px 0; }\n.alert p { margin: 0 0 6px 0; }\n.alert-title { font-weight: bold; }\n.alert-note { border-left: 4px solid #6fa8dc; }\n.alert-tip { border-left: 4px solid #6fbf8b; }\n.alert-important { border-left: 4px solid #a98fe0; }\n.alert-warning { border-left: 4px solid #e0b05e; }\n.alert-caution { border-left: 4px solid #e0645a; }\n.footnotes { border-top: 1px solid #444444; margin-top: 18px; font-size: 10px; color: #bbbbbb; }\nu, ins { text-decoration: underline; }\nq { font-style: italic; }\nabbr { border-bottom: 1px dotted #888888; }\nmark { background-color: #6b5b1e; }\nkbd { font-family: \"DejaVu Sans Mono\", \"Liberation Mono\", Courier, monospace; font-size: 10px; background-color: #1e1e1e; border: 1px solid #444444; padding: 0px 3px 0px 3px; }\n.math { font-family: \"DejaVu Serif\", Georgia, serif; font-style: italic; }\n.math.block { display: block; text-align: center; margin: 6px 0 6px 0; }\npre.math { text-align: left; background-color: transparent; border: none; line-height: 1.1; margin: 6px auto 6px auto; }\n#{TOC_CSS}", "sepia" => "body { font-family: \"DejaVu Serif\", Georgia, serif; font-size: 12px; line-height: 1.6; color: #5b4636; background-color: #f4ecd8; margin: 0; padding: 0; }\nh1 { font-size: 24px; font-weight: bold; margin: 20px 0 12px 0; }\nh2 { font-size: 19px; font-weight: bold; margin: 18px 0 10px 0; }\nh3 { font-size: 15px; font-weight: bold; margin: 16px 0 8px 0; }\nh4 { font-size: 12px; font-weight: bold; margin: 14px 0 8px 0; }\nh5 { font-size: 11.5px; font-weight: bold; margin: 12px 0 6px 0; }\nh6 { font-size: 11.5px; font-style: italic; color: #8a7a5a; margin: 12px 0 6px 0; }\np { margin: 0 0 9px 0; }\na { color: #7a5c2e; }\nstrong { font-weight: bold; }\nem { font-style: italic; }\ndel { text-decoration: line-through; }\nul, ol { margin: 0 0 9px 0; padding-left: 18px; }\nul ul, ol ol, ul ol, ol ul { margin: 0 0 9px 0; padding-left: 16px; }\nli { margin: 0 0 3px 0; }\nli.task-list-item { list-style-type: none; }\nblockquote { border-left: 4px solid #d3c3a0; margin: 9px 0 9px 0; padding: 2px 0 2px 12px; color: #6e5744; }\nblockquote p { margin: 0 0 6px 0; }\npre { font-family: \"DejaVu Sans Mono\", \"Liberation Mono\", Courier, monospace; font-size: 10px; background-color: #ece1c6; border: 1px solid #d3c3a0;\n margin: 9px 0 9px 0; padding: 7px 9px 7px 9px; overflow: hidden;\n white-space: pre-wrap; }\ntable { overflow: hidden; }\ncode { font-family: \"DejaVu Sans Mono\", \"Liberation Mono\", Courier, monospace; font-size: 10px; background-color: #ece1c6; padding: 0px 2px 0px 2px; }\npre code { background-color: transparent; padding: 0; }\ntable { border-spacing: 0; margin: 9px 0 9px 0; font-size: 10.5px; }\nth { font-weight: bold; border: 1px solid #d3c3a0; background-color: #ece1c6; padding: 4px 8px 4px 8px; text-align: left; }\ntd { border: 1px solid #d3c3a0; padding: 4px 8px 4px 8px; }\nhr { border-bottom: 1px solid #d3c3a0; margin: 18px 0 18px 0; }\nimg { margin: 6px 0 6px 0; max-width: 100%; }\n.alert { border: none; border-left: 4px solid #8a7a5a; padding: 8px 12px 4px 12px; margin: 9px 0 9px 0; }\n.alert p { margin: 0 0 6px 0; }\n.alert-title { font-weight: bold; }\n.alert-note { border-left: 4px solid #4e6f8e; }\n.alert-tip { border-left: 4px solid #4e8e5e; }\n.alert-important { border-left: 4px solid #7a5fa8; }\n.alert-warning { border-left: 4px solid #a8823e; }\n.alert-caution { border-left: 4px solid #a84e42; }\n.footnotes { border-top: 1px solid #d3c3a0; margin-top: 18px; font-size: 10px; color: #6e5744; }\nu, ins { text-decoration: underline; }\nq { font-style: italic; }\nabbr { border-bottom: 1px dotted #8a7a5a; }\nmark { background-color: #ecd98b; }\nkbd { font-family: \"DejaVu Sans Mono\", \"Liberation Mono\", Courier, monospace; font-size: 10px; background-color: #ece1c6; border: 1px solid #d3c3a0; padding: 0px 3px 0px 3px; }\n.math { font-family: \"DejaVu Serif\", Georgia, serif; font-style: italic; }\n.math.block { display: block; text-align: center; margin: 6px 0 6px 0; }\npre.math { text-align: left; background-color: transparent; border: none; line-height: 1.1; margin: 6px auto 6px auto; }\n#{TOC_CSS}"} -
TOC_CSS =
".toc-title { font-size: 1.7em; font-weight: bold; margin: 0 0 1em 0; }\n.toc-entry { margin: 0 0 2px 0; }\n.toc-entry a { color: inherit; text-decoration: none; }\n.toc-page { float: right; }\n.toc-level-2 { margin-left: 1.5em; }\n.toc-level-3 { margin-left: 3em; }\n.toc-level-4 { margin-left: 4.5em; }\n.toc-level-5 { margin-left: 6em; }\n.toc-level-6 { margin-left: 7.5em; }" -
Table of contents layout, interpolated into every style so they keep covering the same property set: entries are blocks with a right-floating page number (litehtml has no ::after or leader(), so no dot leaders), and colors inherit so one block fits all four looks. No page-break rule here on purpose: the shim's page-break properties inherit down the tree, and a break on the nav would give every entry its own page — the break to the body rides an empty div inject_toc adds instead.
Class Method Summary
-
.document_html(body_html : String, css : String, title : String | Nil = nil, extra_css : String | Nil = nil) : String
Wrap rendered HTML in a document skeleton with the stylesheet.
-
.emoji_font=(path : String) : Nil
Designate the emoji/symbol fallback font used for emoji codepoints the primary font lacks.
- .fetch_remote_images=(value : Bool)
- .fetch_remote_images? : Bool
-
.html_document?(source : String) : Bool
True when the source looks like a complete HTML document rather than markdown: such input skips the markdown pipeline entirely.
-
.hyphenate_body(body_html : String, hyphenate : Bool, language : String) : String
Internal: called by Pdf::Renderer.
-
.image_fetch_allowed?(url : String) : Bool
Server-side fetches (the web playground renders untrusted markdown) must only reach public http(s) hosts: loopback, private ranges and link-local addresses — cloud metadata lives at 169.254.169.254 — would turn the renderer into a prober of the machine it runs on.
-
.inject_toc(html : String, toc : String) : String
Place the TOC ahead of the content: markdown bodies are fragments and just get it prepended, complete HTML documents get it inside the element so doctype and head stay intact.
-
.insert_soft_hyphens(html : String, language : String = "en") : String
Inserts soft hyphens () at the hyphenation points of the words in the HTML text nodes, so the layout engine can break long words at the end of a justified line (the soft hyphen only shows when the break is taken).
-
.kdp_gutter_mm(pages : Int32) : Float64
The KDP minimum gutter for a given page count (table entries are {lowest page count, highest page count, gutter in mm}).
-
.kdp_margin_warning(parsed : PageMargins) : String | Nil
The warning for margins below KDP's no-bleed minimum, or nil when the margins are printable.
-
.kdp_range_warning(pages : Int32) : String | Nil
The warning for a final page count outside KDP's printable range, or nil when the count is printable.
-
.margins_with_gutter(parsed : PageMargins, gutter : Float64) : String
A margin spec with the gutter appended (the other sides unchanged).
-
.page_background(css : String) : String
The page background declared by the CSS's
bodyrules, the last one winning as in CSS. -
.page_size_names : Array(String)
Named sizes for pickers and docs.
-
.parse_heading_map(map : String) : Array(HeadingEntry)
Parse the shim's heading map ("index\tlevel\tpage\ttitle" per line) into HeadingEntry records.
-
.parse_margins(value : String) : PageMargins
CSS-style margin shorthand in millimeters: one value (all sides), two (top/bottom, left/right), four (top, right, bottom, left), or five (...
-
.parse_page_size(value : String) : Tuple(Float64, Float64)
Resolve a page size: a name from PAGE_SIZES (case-insensitive), or "WxH" for custom sizes.
-
.pick_code_theme(code_theme : String | Nil, theme : String | Nil, style : String) : String | Nil
A dark page with the light default code theme would glow: pick a dark code theme for the dark style unless one was asked for explicitly.
-
.process_images(html : String, base_dir : String, temp_dir : String, converted : Array(String)) : String
Internal: called by Pdf::Renderer.
-
.register_font(path : String) : Nil
Register a TTF file as a font candidate for font-family matching.
-
.render(source : String, output_path : String, options : Markd::Options = Markd::Options.new, page_size : String = "a4", margin_mm : Float64 = 20.0, base_dir : String = ".", header : String = "", footer : String = "", code_theme : String | Nil = nil, theme : String | Nil = nil, html_input : Bool = false, style : String | Nil = nil, pageless : Bool = false, hyphenate : Bool = false, language : String = "en", css : String | Nil = nil, margins : String | Nil = nil, kdp : Bool = false, mirror_headers : Bool = false, toc : Bool = false, toc_depth : Int32 = 1, toc_title : String = "Contents", toc_min_level : Int32 = 1) : Int32
Render markdown source to a PDF file in one shot: a convenience that builds a throwaway Renderer.
-
.render_to_memory(source : String, options : Markd::Options = Markd::Options.new, page_size : String = "a4", margin_mm : Float64 = 20.0, base_dir : String = ".", header : String = "", footer : String = "", code_theme : String | Nil = nil, theme : String | Nil = nil, style : String | Nil = nil, html_input : Bool = false, pageless : Bool = false, hyphenate : Bool = false, language : String = "en", css : String | Nil = nil, margins : String | Nil = nil, kdp : Bool = false, mirror_headers : Bool = false, toc : Bool = false, toc_depth : Int32 = 1, toc_title : String = "Contents", toc_min_level : Int32 = 1) : Bytes
Render markdown to PDF bytes in memory: a convenience that builds a throwaway Renderer.
-
.rewrite_heading_anchors(html : String) : String
Number every heading with visible text: an empty anchor goes in as the heading's first child, N counting headings in document order.
-
.rewrite_task_lists(html : String) : String
GFM task lists: markd emits an as the first child of the
- , which litehtml drops entirely, leaving a plain bullet.