11

I have many documents as org-files which have a CUSTOM_LABEL property, like

* Introduction :PROPERTIES: :CUSTOM_LABEL: AP 1 :END:

In this case, the files need to be exported as LaTeX, translating each CUSTOM_LABEL as a \label{marker}. The example above should translate to \label{AP 1}.

I know already how to call custom functions at export time, but I am not expert enough to write a defun to do that particular conversion, ie CUSTOM_LABEL -> \label{}

How can the defun to inject a custom_label as \label{} be written?

I would appreciate even just some pseudo-code, or some pointers.

I am asking this question here instead of other places, because this is more an Emacs question, since I searched thoroughly the org-mode manual, and that kind of feature is currently unavailable.

A generic function to convert a given PROPERTY when exporting (LaTeX, HTML, or any other format), would be even better.

Thank you.

6
  • The title seems off. If I understand the question, you want to turn an org property into an arbitrary string (namely, a LaTeX label), not into another org property.
    – Malabarba
    Sep 24, 2014 at 15:03
  • @rasmus: Thank you for that pointer. I was reading about that just a few hours ago on the emacs-orgmode list (among others, lists.gnu.org/archive/html/emacs-orgmode/2014-09/msg00498.html). I tried that code, and just setting org-latex-custom-id-as-label. It works fine with HTML export, but it does not have any effect with LaTeX export. I wish I could rely just on org-mode core functions, still I do like @malababrba's answer, as it allows a nice generalization.
    – gsl
    Oct 12, 2014 at 7:47
  • @rasmus That is the behavior I need. But I ran your code, but I get \section{h}\label{sec-1} I am using GNU Emacs 24.3.94.1 (x86_64-apple-darwin13.4.0, NS apple-appkit-1265.21) of 2014-10-04 on builder10-9.porkrind.org and Org-mode version 8.2.6 (release_8.2.6-1 @ /Applications/Emacs.app/Contents/Resources/lisp/org/). Also, to make sure, I renamed my .emacs.d, so it ran with no custom stuff.
    – gsl
    Oct 13, 2014 at 13:51
  • So nice how you managed to synthesize a whole working example in just a line of code!
    – gsl
    Oct 13, 2014 at 13:54
  • Ah, that would explain it! I tried install latest org-mode using this el-get recipe: github.com/dimitri/el-get/blob/master/recipes/org-mode.rcp, but I still get Org-mode version 8.2.6 (release_8.2.6-1 @ /Users/gsl/.emacs.d/el-get/org-mode/lisp/ Would you know how to tweak that recipe so that I could use it for the dev-branch? I could also ask this as new question. Thank you so much for pointing that out.
    – gsl
    Oct 13, 2014 at 15:16

3 Answers 3

10

I've written a function that does what you want in a quite extendable manner. It checks which headlines contain the property CUSTOM_LABEL (or some other property you configure) and calls the function endless/insert-org-label-latex on each of them with the value of the property as an argument.

The example snippet also shows how to extend it for html or other backends.

Configure the replacements

With this variable you can configure properties you care about and which functions get called to handle each property.

(defcustom endless/org-property-mapping 
  '((latex ("CUSTOM_LABEL" . endless/insert-org-label-latex))
    (html ("CUSTOM_LABEL" . endless/insert-org-label-html)))
  "List of mappings from org property to arbitrary strings.
Each element is a list:
  (BACKEND (PROPERTY1 . FUNCTION1) (PROPERTY2 . FUNCTION2) ...)

FUNCTION are functions which get called with a single
argument (the value of PROPERTY) and are responsible for doing
whatever should be done."
  :type '(repeat (cons symbol (repeat (cons string string)))))

The Heavy worker

This function is what you should add to the org export hook. It takes care of checking for the properties listed above, and calling the functions associated with those properties.

(defun endless/replace-org-property (backend)
  "Convert org properties using `endless/org-property-mapping'.
Lookup BACKEND in `endless/org-property-mapping' for a list of
\(PROPERTY REPLACEMENT). For each healine being exported, if it has a
PROPERTY listed insert a string immediately after the healine given by
    (format REPLACEMENT PROPERTY-VALUE)"
  (let ((map (cdr (assoc backend endless/org-property-mapping)))
        value replacement)
    (when map      
      (org-map-entries
       (lambda () 
         (dolist (it map)
           (save-excursion
             (when (setq value (org-entry-get (point) (car it))) 
               (funcall (cdr it) value)))))))))

(add-hook 'org-export-before-processing-hook #'endless/replace-org-property)

The functions you define

These are the ones that do the actual replacement. Below is an example for the latex case.

(defun endless/insert-org-label-latex (label)
  "Insert \"\\\\label{LABEL}\\n\" after the :PROPERTY: drawer."
  (search-forward-regexp org-property-end-re)
  (forward-char 1)
  (insert (format "\\label{%s}\n" label)))

Result

Evaluate all of this code above, then export the following org buffer to latex.

* Test
  :PROPERTIES:
  :CUSTOM_LABEL: hi
  :END:
Test

The resulting latex buffer should be something like this.

\section{Test}
\label{sec-1}
\label{hi}
Test
1
  • Thank you for the code, the comments, and the help. It is very helpful. I learned a lot, too. Thank you.
    – gsl
    Sep 24, 2014 at 17:07
5

Note for the code snippets you must use the current development-version, (org-version) => "8.3beta".

Please use CUSTOM_ID and internal linking. See (info "(org) Handling links").

In most cases you should not be concerned about the exported result of internal naming in Org. Links to figures and headlines, say, will be correct when exported. See (info "(org) Internal links").

For LaTeX try:

(with-temp-buffer
  (let ((org-latex-prefer-user-labels t))
(insert "
* h
:PROPERTIES:
:CUSTOM_ID: h
:END:")
(org-mode)
(org-latex-export-as-latex nil nil nil t)))

Result:

\section{h}
\label{h}

In exporters such as ox-odt and ox-html headlines contain both internal id, ID and CUSTOM_ID. Which link is used depends on the the link:

(with-temp-buffer
  (let ((org-export-with-toc nil))
(insert "
* h
:PROPERTIES:
:CUSTOM_ID: h
:END:
[[*h]] [[#h]]")
(org-mode)
(org-html-export-as-html nil nil nil t)))

Result:

<div id="outline-container-h" class="outline-2">
<h2 id="h"><a id="sec-1"></a><span class="section-number-2">1</span> h</h2>
<div class="outline-text-2" id="text-h">
<p>
<a href="#sec-1">1</a> <a href="#h">1</a>
</p>
</div>
</div>
1
  • Thank you for pointing the default way for >8.3 users! One could use the default way for CUSTOM_ID, while still using @malabarba's for passing any other org-property. I am actually using it that way to pass a few other properties (like cite-keys, genre, venue, etc), beside CUSTOM_ID.
    – gsl
    Oct 21, 2014 at 14:08
1

I'm not sure, but you likely need to advise or even overwrite the exporter function. In Org 8, that is org-latex-export-headline.

The function gets the headline element, the headline contents and an additional property list. Within the exporter function, you can get element properties (including your custom label) with org-element-property.

1
  • Thank you so much for the pointer. As far as I understood from other posts/articles, the new org exporter does not work too much with advising, but rather one creates filter functions to be called at a certain stage of the export process, roughly like this: ``` (eval-after-load 'ox-latex '(add-to-list 'org-export-filter-final-output-functions 'my-filter-function)) ``` (I am not sure why the back-tick syntax does not work in comments?)
    – gsl
    Sep 24, 2014 at 17:17

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.