1

This is my org file:

#+TITLE: Post title
#+DESCRIPTION: 
#+AUTHOR: MyName
#+DATE: <2022-11-10 Thu>
#+TAGS: tag1 tag2
#+OPTIONS: toc:nil

*Section 1
Content   

I would like to export it to markdown (C-c C-e m m) and have the following content:

---
author:
- MyName
date: 2022-11-10 Thu
tags: 'tag1 tag2'
title: 'Post title'
---

# Section 1

Content

So the problem comes down to appending some text before the text exported with org-mode exporter by default.

I tried two following approaches. Unfortunately none of them works right.

  1. When I add at the beginning of the file:

    #+begin_export markdown
    ---
    author:
    - {{{author}}}
    date: {{{date}}}
    tags: ?????
    title: 'How to tailor the output of org-mode exporter to our needs'
    ---
    #+end_export
    

    the macros are not evaluated. What is more, I don't know how to refer to the #+TAGS field.

  2. When I try with:

    # #+MACRO: newline    src_emacs-lisp[:results raw]{"\n"}
    # #+MACRO: mdheader --- {{{newline}}} author: -{{{author}}} ---
    
    {{{mdheader}}}
    

    the macros are evaluated, however I cannot escape minus signs in macro definition and I obtain:

    &#x2014; 
    author: -MyName &#x2014;
    

    in the markdown output file. Besides, in this approach there also exist a problem with accessing tag names.

How can those snippets be corrected to get desired result? Or is there any better way of handling this problem?

1 Answer 1

2

You can define a global macro as follows:

(defun org-export-md-format-front-matter ()
  (let* ((kv-alist (org-element-map (org-element-parse-buffer 'greater-element)
                       'keyword
                     (lambda (keyword)
                       (cons (intern (downcase (org-element-property :key keyword)))
                             (org-element-property :value keyword)))))
         (lines (mapcar (lambda (kw)
                          (let ((val (alist-get kw kv-alist)))
                            (format (pcase kw
                                      ('author "%s: %s")
                                      ((or 'tags 'title) "%s: '%s'")
                                      (_ "%s: %s"))
                                    (downcase (symbol-name kw))
                                    (pcase kw
                                      ('date (substring val 1 -1))
                                      (_ val)))))
                        '(author date tags title))))
    (concat "---\n" (concat (mapconcat #'identity lines "\n")) "\n---")))

(setq org-export-global-macros
      '((front-matter . "(eval (org-export-md-format-front-matter))")))

additionally, as described in the footnotes (currently 108) in section Special Symbols of the org manual, you can toggle the 'special' exporting of - via the - export setting, i.e. add:

#+OPTIONS: -:nil

to the keywords at the start of your org file.

These two things should do the trick...


Alternatively, you can use the org-export-before-processing-hook to run a function that inserts the desired string (e.g. as produced by the function org-export-md-format-front-matter above) before any other processing is done:

(defun my/org-export-markdown-hook-function (backend)
    (if (eq backend 'md)
        (insert (org-export-md-format-front-matter) "\n")))

(add-hook 'org-export-before-processing-hook #'my/org-export-markdown-hook-function)

If you prefer to run this function only with 'specific' files, then you can add the hook only locally in those files using a File Variable. In this case, do not add the hook globally (as in the example above), but add a file variable to the end of your 'specified' files as follows:

# Local Variables:
# eval: (add-hook 'org-export-before-processing-hook #'my/org-export-markdown-hook-function nil t)
# End:

(Caution! Remember that after adding these lines not only saving (C-x C-s) but also reloading the file from the disk ( C-x C-v RET or M-x revert-buffer) is necessary to make Emacs load the local variables.)

Alternatively, you could add a condition to only add the front matter when the file is a member of a list of specified filenames, as follows:

(defun my/org-export-markdown-hook-function (backend)
  (if (and (eq backend 'md)
           (member buffer-file-name '("/path/to/some/org-file.org"
                                      "/path/to/another/org-file.org")))
      (insert (org-export-md-format-front-matter) "\n")))
10
  • 1
    I added an alternative way to your answer. I hope you don't mind.
    – NickD
    Commented Nov 10, 2022 at 20:38
  • 1
    And BTW, thanks for footnote 109 !-) TIL...
    – NickD
    Commented Nov 10, 2022 at 20:55
  • 1
    I don't mind at all, on the contrary, it is a nice improvement! It adds the possibility to selectively (on backend) insert the 'front matter'. I took the macro path, as I first only wanted to use it to get the "TAGS", but then I thought I could as well add (and format) the other 'Export Settings' (to have it all in one macro). So I did not think about using the hook, which looks preferable to me (in most cases). Commented Nov 10, 2022 at 21:23
  • 1
    Change (format "%s: %s" kw ...) in the org--export-md-format-front-matter function to (format "%s: %s" (downcase kw) ...). That formats the string that is printed out not with the original keyword kw but with the lower case version of it produced by calling the function downcase on it.
    – NickD
    Commented Nov 11, 2022 at 0:00
  • 2
    @dalanicolai OK. Finally, I've managed to get things working. Since it took me some time to find out what was the reason your code wasn't working at first, I decided to add a comment about the necessity of reloading the file after inserting "Local Variables" lines. I hope you don't mind. Commented Nov 13, 2022 at 17:09

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.