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.

Malabarba
  • 22,878
  • 6
  • 78
  • 163
gsl
  • 1,742
  • 17
  • 34
  • 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 '14 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, https://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 '14 at 07: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 '14 at 13:51
  • So nice how you managed to synthesize a whole working example in just a line of code! – gsl Oct 13 '14 at 13:54
  • Ah, that would explain it! I tried install latest `org-mode` using this `el-get` recipe: https://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 '14 at 15:16
  • [The system is asking to move the comments to chat, but it would be useful to others to see it here, so this is my last comment.] I have followed your advice, and using `el-get` I managed to use `org-mode` at HEAD (`Org-mode version 8.3beta (release_8.3beta-457-gce2090 @ /Users/gsl/.emacs.d/el-get/org-mode/lisp/)`, and it works. I get `\section{h}\label{h}`. Just from your comments I learned so much. Thank you! Of course feel free to move thread to chat. You could also add an answer, so I could add a vote to it. – gsl Oct 13 '14 at 17:54

3 Answers3

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
Malabarba
  • 22,878
  • 6
  • 78
  • 163
  • Thank you for the code, the comments, and the help. It is very helpful. I learned a lot, too. Thank you. – gsl Sep 24 '14 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>
rasmus
  • 2,682
  • 13
  • 20
  • 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 '14 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.

  • 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 '14 at 17:17