diff --git a/doc/org.texi b/doc/org.texi index bdcb46f4c..365830517 100644 --- a/doc/org.texi +++ b/doc/org.texi @@ -14442,7 +14442,9 @@ however, override everything. @subsubheading Markdown specific properties -@multitable {@code{:md-headline-style}} {@code{org-md-headline-style}} +@multitable {@code{:md-footnotes-section}} {@code{org-md-footnotes-section}} +@item @code{:md-footnote-format} @tab @code{org-md-footnote-format} +@item @code{:md-footnotes-section} @tab @code{org-md-footnotes-section} @item @code{:md-headline-style} @tab @code{org-md-headline-style} @end multitable diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS index 9125967f2..c90b37c21 100644 --- a/etc/ORG-NEWS +++ b/etc/ORG-NEWS @@ -219,6 +219,11 @@ SVG images exported in HTML are now by default assigned a CSS class ~org-svg~ if no CSS class is specified with the ~:class~ attribute. By default, the CSS styling of class ~org-svg~ specifies an image width of 90\thinsp{}% of the container the image. +**** Markdown footnote export customization +Variables ~org-md-footnotes-section~ and ~org-md-footnote-format~ +introduced for =ox-md.el=. Both new variables define template strings +which can be used to customize the format of the exported footnotes +section and individual footnotes, respectively. *** Babel **** Blocks with coderefs labels can now be evaluated The labels are removed prior to evaluating the block. diff --git a/lisp/ox-md.el b/lisp/ox-md.el index bdf7f44b1..034145a88 100644 --- a/lisp/ox-md.el +++ b/lisp/ox-md.el @@ -51,6 +51,25 @@ This variable can be set to either `atx' or `setext'." (const :tag "Use \"Setext\" style" setext))) +;;;; Footnotes + +(defcustom org-md-footnotes-section "%s%s" + "Format string for the footnotes section. +The first %s placeholder will be replaced with the localized Footnotes section +heading, the second with the contents of the Footnotes section." + :group 'org-export-md + :type 'string + :version "25.1" + :package-version '(Org . "9.0")) + +(defcustom org-md-footnote-format "%s" + "Format string for the footnote reference. +The %s will be replaced by the footnote reference itself." + :group 'org-export-md + :type 'string + :version "25.1" + :package-version '(Org . "9.0")) + ;;; Define Back-End @@ -89,7 +108,10 @@ This variable can be set to either `atx' or `setext'." (src-block . org-md-example-block) (template . org-md-template) (verbatim . org-md-verbatim)) - :options-alist '((:md-headline-style nil nil org-md-headline-style))) + :options-alist + '((:md-footnote-format nil nil org-md-footnote-format) + (:md-footnotes-section nil nil org-md-footnotes-section) + (:md-headline-style nil nil org-md-headline-style))) ;;; Filters @@ -215,21 +237,30 @@ a communication channel." (car (last (org-export-get-headline-number headline info)))) ".")))) - (concat bullet (make-string (- 4 (length bullet)) ?\s) heading tags - "\n\n" - (and contents - (replace-regexp-in-string "^" " " contents))))) - ;; Use "Setext" style. - ((eq style 'setext) - (concat heading tags anchor "\n" - (make-string (length heading) (if (= level 1) ?= ?-)) - "\n\n" - contents)) - ;; Use "atx" style. - (t (concat (make-string level ?#) " " heading tags anchor "\n\n" - contents)))))) + (concat bullet (make-string (- 4 (length bullet)) ?\s) heading tags "\n\n" + (and contents (replace-regexp-in-string "^" " " contents))))) + (t (concat (org-md--headline-title style level title anchor tags) contents)))))) +;; Headline Title + +(defun org-md--headline-title (style level title &optional anchor tags) + "Generate a headline title in the preferred Markdown headline style. +STYLE is the preferred style (`atx' or `setext'). LEVEL is the +header level. TITLE is the headline title. ANCHOR is the HTML +anchor tag for the section as a string. TAGS are the tags set on +the section." + (let ((anchor-lines (and anchor (concat anchor "\n\n")))) + ;; Use "Setext" style + (if (and (eq style 'setext) (< level 3)) + (let* ((underline-char (if (= level 1) ?= ?-)) + (underline (concat (make-string (length title) underline-char) + "\n"))) + (concat "\n" anchor-lines title tags "\n" underline "\n")) + ;; Use "Atx" style + (let ((level-mark (make-string level ?#))) + (concat "\n" anchor-lines level-mark " " title tags "\n\n"))))) + ;;;; Horizontal Rule (defun org-md-horizontal-rule (_horizontal-rule _contents _info) @@ -467,13 +498,48 @@ a communication channel." ;;;; Template +(defun org-md--footnote-formatted (footnote info) + "Formats a single footnote entry FOOTNOTE. +FOOTNOTE is a cons cell of the form (number . definition). +INFO is a plist with contextual information." + (let* ((fn-num (car footnote)) + (fn-text (cdr footnote)) + (fn-format (plist-get info :md-footnote-format)) + (fn-anchor (format "fn.%d" fn-num)) + (fn-href (format " href=\"#fnr.%d\"" fn-num)) + (fn-link-to-ref (org-html--anchor fn-anchor fn-num fn-href info))) + (concat (format fn-format fn-link-to-ref) " " fn-text "\n"))) + +(defun org-md--footnote-section (info) + "Format the footnote section. +INFO is a plist used as a communication channel." + (let* ((fn-alist (org-export-collect-footnote-definitions info)) + (fn-alist (cl-loop for (n type raw) in fn-alist collect + (cons n (org-trim (org-export-data raw info))))) + (headline-style (plist-get info :md-headline-style)) + (section-title (org-html--translate "Footnotes" info))) + (when fn-alist + (format (plist-get info :md-footnotes-section) + (org-md--headline-title headline-style 1 section-title) + (mapconcat (lambda (fn) (org-md--footnote-formatted fn info)) + fn-alist + "\n"))))) + (defun org-md-inner-template (contents info) "Return body of document after converting it to Markdown syntax. CONTENTS is the transcoded contents string. INFO is a plist holding export options." ;; Make sure CONTENTS is separated from table of contents and ;; footnotes with at least a blank line. - (org-trim (org-html-inner-template (concat "\n" contents "\n") info))) + (concat + ;; Table of contents. + (let ((depth (plist-get info :with-toc))) + (when depth (org-html-toc depth info))) + ;; Document contents. + contents + "\n" + ;; Footnotes section. + (org-md-footnote--section info))) (defun org-md-template (contents _info) "Return complete document string after Markdown conversion.