commit | author | age
|
76bbd0
|
1 |
;;; ox-org.el --- Org Back-End for Org Export Engine -*- lexical-binding: t; -*- |
C |
2 |
|
|
3 |
;; Copyright (C) 2013-2018 Free Software Foundation, Inc. |
|
4 |
|
|
5 |
;; Author: Nicolas Goaziou <n.goaziou@gmail.com> |
|
6 |
;; Keywords: org, wp |
|
7 |
|
|
8 |
;; This file is part of GNU Emacs. |
|
9 |
|
|
10 |
;; GNU Emacs is free software: you can redistribute it and/or modify |
|
11 |
;; it under the terms of the GNU General Public License as published by |
|
12 |
;; the Free Software Foundation, either version 3 of the License, or |
|
13 |
;; (at your option) any later version. |
|
14 |
|
|
15 |
;; GNU Emacs is distributed in the hope that it will be useful, |
|
16 |
;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
17 |
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
18 |
;; GNU General Public License for more details. |
|
19 |
|
|
20 |
;; You should have received a copy of the GNU General Public License |
|
21 |
;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. |
|
22 |
|
|
23 |
;;; Commentary: |
|
24 |
|
|
25 |
;;; Code: |
|
26 |
|
|
27 |
(require 'ox) |
|
28 |
(declare-function htmlize-buffer "ext:htmlize" (&optional buffer)) |
|
29 |
(defvar htmlize-output-type) |
|
30 |
|
|
31 |
(defgroup org-export-org nil |
|
32 |
"Options for exporting Org mode files to Org." |
|
33 |
:tag "Org Export Org" |
|
34 |
:group 'org-export |
|
35 |
:version "24.4" |
|
36 |
:package-version '(Org . "8.0")) |
|
37 |
|
|
38 |
(defcustom org-org-htmlized-css-url nil |
|
39 |
"URL pointing to the CSS defining colors for htmlized Emacs buffers. |
|
40 |
Normally when creating an htmlized version of an Org buffer, |
|
41 |
htmlize will create the CSS to define the font colors. However, |
|
42 |
this does not work when converting in batch mode, and it also can |
|
43 |
look bad if different people with different fontification setup |
|
44 |
work on the same website. When this variable is non-nil, |
|
45 |
creating an htmlized version of an Org buffer using |
|
46 |
`org-org-export-as-org' will include a link to this URL if the |
|
47 |
setting of `org-html-htmlize-output-type' is `css'." |
|
48 |
:group 'org-export-org |
|
49 |
:type '(choice |
|
50 |
(const :tag "Don't include external stylesheet link" nil) |
|
51 |
(string :tag "URL or local href"))) |
|
52 |
|
|
53 |
(org-export-define-backend 'org |
|
54 |
'((babel-call . org-org-identity) |
|
55 |
(bold . org-org-identity) |
|
56 |
(center-block . org-org-identity) |
|
57 |
(clock . org-org-identity) |
|
58 |
(code . org-org-identity) |
|
59 |
(diary-sexp . org-org-identity) |
|
60 |
(drawer . org-org-identity) |
|
61 |
(dynamic-block . org-org-identity) |
|
62 |
(entity . org-org-identity) |
|
63 |
(example-block . org-org-identity) |
|
64 |
(export-block . org-org-export-block) |
|
65 |
(fixed-width . org-org-identity) |
|
66 |
(footnote-definition . ignore) |
|
67 |
(footnote-reference . org-org-identity) |
|
68 |
(headline . org-org-headline) |
|
69 |
(horizontal-rule . org-org-identity) |
|
70 |
(inline-babel-call . org-org-identity) |
|
71 |
(inline-src-block . org-org-identity) |
|
72 |
(inlinetask . org-org-identity) |
|
73 |
(italic . org-org-identity) |
|
74 |
(item . org-org-identity) |
|
75 |
(keyword . org-org-keyword) |
|
76 |
(latex-environment . org-org-identity) |
|
77 |
(latex-fragment . org-org-identity) |
|
78 |
(line-break . org-org-identity) |
|
79 |
(link . org-org-link) |
|
80 |
(node-property . org-org-identity) |
|
81 |
(template . org-org-template) |
|
82 |
(paragraph . org-org-identity) |
|
83 |
(plain-list . org-org-identity) |
|
84 |
(planning . org-org-identity) |
|
85 |
(property-drawer . org-org-identity) |
|
86 |
(quote-block . org-org-identity) |
|
87 |
(radio-target . org-org-identity) |
|
88 |
(section . org-org-section) |
|
89 |
(special-block . org-org-identity) |
|
90 |
(src-block . org-org-identity) |
|
91 |
(statistics-cookie . org-org-identity) |
|
92 |
(strike-through . org-org-identity) |
|
93 |
(subscript . org-org-identity) |
|
94 |
(superscript . org-org-identity) |
|
95 |
(table . org-org-identity) |
|
96 |
(table-cell . org-org-identity) |
|
97 |
(table-row . org-org-identity) |
|
98 |
(target . org-org-identity) |
|
99 |
(timestamp . org-org-identity) |
|
100 |
(underline . org-org-identity) |
|
101 |
(verbatim . org-org-identity) |
|
102 |
(verse-block . org-org-identity)) |
|
103 |
:menu-entry |
|
104 |
'(?O "Export to Org" |
|
105 |
((?O "As Org buffer" org-org-export-as-org) |
|
106 |
(?o "As Org file" org-org-export-to-org) |
|
107 |
(?v "As Org file and open" |
|
108 |
(lambda (a s v b) |
|
109 |
(if a (org-org-export-to-org t s v b) |
|
110 |
(org-open-file (org-org-export-to-org nil s v b))))))) |
|
111 |
:filters-alist '((:filter-parse-tree . org-org--add-missing-sections))) |
|
112 |
|
|
113 |
(defun org-org--add-missing-sections (tree _backend _info) |
|
114 |
"Ensure each headline has an associated section. |
|
115 |
|
|
116 |
TREE is the parse tree being exported. |
|
117 |
|
|
118 |
Footnotes relative to the headline are inserted in the section, |
|
119 |
using `org-org-section'. However, this function is not called if |
|
120 |
the headline doesn't contain any section in the first place, so |
|
121 |
we make sure it is always called." |
|
122 |
(org-element-map tree 'headline |
|
123 |
(lambda (h) |
|
124 |
(let ((first-child (car (org-element-contents h))) |
|
125 |
(new-section (org-element-create 'section))) |
|
126 |
(pcase (org-element-type first-child) |
|
127 |
(`section nil) |
|
128 |
(`nil (org-element-adopt-elements h new-section)) |
|
129 |
(_ (org-element-insert-before new-section first-child)))))) |
|
130 |
tree) |
|
131 |
|
|
132 |
(defun org-org-export-block (export-block _contents _info) |
|
133 |
"Transcode a EXPORT-BLOCK element from Org to LaTeX. |
|
134 |
CONTENTS and INFO are ignored." |
|
135 |
(and (equal (org-element-property :type export-block) "ORG") |
|
136 |
(org-element-property :value export-block))) |
|
137 |
|
|
138 |
(defun org-org-identity (blob contents _info) |
|
139 |
"Transcode BLOB element or object back into Org syntax. |
|
140 |
CONTENTS is its contents, as a string or nil. INFO is ignored." |
|
141 |
(let ((case-fold-search t)) |
|
142 |
(replace-regexp-in-string |
|
143 |
"^[ \t]*#\\+ATTR_[-_A-Za-z0-9]+:\\(?: .*\\)?\n" "" |
|
144 |
(org-export-expand blob contents t)))) |
|
145 |
|
|
146 |
(defun org-org-headline (headline contents info) |
|
147 |
"Transcode HEADLINE element back into Org syntax. |
|
148 |
CONTENTS is its contents, as a string or nil. INFO is ignored." |
|
149 |
(unless (org-element-property :footnote-section-p headline) |
|
150 |
(unless (plist-get info :with-todo-keywords) |
|
151 |
(org-element-put-property headline :todo-keyword nil)) |
|
152 |
(unless (plist-get info :with-tags) |
|
153 |
(org-element-put-property headline :tags nil)) |
|
154 |
(unless (plist-get info :with-priority) |
|
155 |
(org-element-put-property headline :priority nil)) |
|
156 |
(org-element-put-property headline :level |
|
157 |
(org-export-get-relative-level headline info)) |
|
158 |
(org-element-headline-interpreter headline contents))) |
|
159 |
|
|
160 |
(defun org-org-keyword (keyword _contents _info) |
|
161 |
"Transcode KEYWORD element back into Org syntax. |
|
162 |
CONTENTS is nil. INFO is ignored." |
|
163 |
(let ((key (org-element-property :key keyword))) |
|
164 |
(unless (member key |
|
165 |
'("AUTHOR" "CREATOR" "DATE" "EMAIL" "OPTIONS" "TITLE")) |
|
166 |
(org-element-keyword-interpreter keyword nil)))) |
|
167 |
|
|
168 |
(defun org-org-link (link contents _info) |
|
169 |
"Transcode LINK object back into Org syntax. |
|
170 |
CONTENTS is the description of the link, as a string, or nil. |
|
171 |
INFO is a plist containing current export state." |
|
172 |
(or (org-export-custom-protocol-maybe link contents 'org) |
|
173 |
(org-element-link-interpreter link contents))) |
|
174 |
|
|
175 |
(defun org-org-template (contents info) |
|
176 |
"Return Org document template with document keywords. |
|
177 |
CONTENTS is the transcoded contents string. INFO is a plist used |
|
178 |
as a communication channel." |
|
179 |
(concat |
|
180 |
(and (plist-get info :time-stamp-file) |
|
181 |
(format-time-string "# Created %Y-%m-%d %a %H:%M\n")) |
|
182 |
(org-element-normalize-string |
|
183 |
(mapconcat #'identity |
|
184 |
(org-element-map (plist-get info :parse-tree) 'keyword |
|
185 |
(lambda (k) |
|
186 |
(and (string-equal (org-element-property :key k) "OPTIONS") |
|
187 |
(concat "#+OPTIONS: " |
|
188 |
(org-element-property :value k))))) |
|
189 |
"\n")) |
|
190 |
(and (plist-get info :with-title) |
|
191 |
(format "#+TITLE: %s\n" (org-export-data (plist-get info :title) info))) |
|
192 |
(and (plist-get info :with-date) |
|
193 |
(let ((date (org-export-data (org-export-get-date info) info))) |
|
194 |
(and (org-string-nw-p date) |
|
195 |
(format "#+DATE: %s\n" date)))) |
|
196 |
(and (plist-get info :with-author) |
|
197 |
(let ((author (org-export-data (plist-get info :author) info))) |
|
198 |
(and (org-string-nw-p author) |
|
199 |
(format "#+AUTHOR: %s\n" author)))) |
|
200 |
(and (plist-get info :with-email) |
|
201 |
(let ((email (org-export-data (plist-get info :email) info))) |
|
202 |
(and (org-string-nw-p email) |
|
203 |
(format "#+EMAIL: %s\n" email)))) |
|
204 |
(and (plist-get info :with-creator) |
|
205 |
(org-string-nw-p (plist-get info :creator)) |
|
206 |
(format "#+CREATOR: %s\n" (plist-get info :creator))) |
|
207 |
contents)) |
|
208 |
|
|
209 |
(defun org-org-section (section contents info) |
|
210 |
"Transcode SECTION element back into Org syntax. |
|
211 |
CONTENTS is the contents of the section. INFO is a plist used as |
|
212 |
a communication channel." |
|
213 |
(concat |
|
214 |
(org-element-normalize-string contents) |
|
215 |
;; Insert footnote definitions appearing for the first time in this |
|
216 |
;; section, or in the relative headline title. Indeed, some of |
|
217 |
;; them may not be available to narrowing so we make sure all of |
|
218 |
;; them are included in the result. |
|
219 |
(let ((footnotes |
|
220 |
(org-element-map |
|
221 |
(list (org-export-get-parent-headline section) section) |
|
222 |
'footnote-reference |
|
223 |
(lambda (fn) |
|
224 |
(and (eq (org-element-property :type fn) 'standard) |
|
225 |
(org-export-footnote-first-reference-p fn info) |
|
226 |
(org-element-normalize-string |
|
227 |
(format "[fn:%s] %s" |
|
228 |
(org-element-property :label fn) |
|
229 |
(org-export-data |
|
230 |
(org-export-get-footnote-definition fn info) |
|
231 |
info))))) |
|
232 |
info nil 'headline t))) |
|
233 |
(and footnotes (concat "\n" (mapconcat #'identity footnotes "\n")))))) |
|
234 |
|
|
235 |
;;;###autoload |
|
236 |
(defun org-org-export-as-org |
|
237 |
(&optional async subtreep visible-only body-only ext-plist) |
|
238 |
"Export current buffer to an Org buffer. |
|
239 |
|
|
240 |
If narrowing is active in the current buffer, only export its |
|
241 |
narrowed part. |
|
242 |
|
|
243 |
If a region is active, export that region. |
|
244 |
|
|
245 |
A non-nil optional argument ASYNC means the process should happen |
|
246 |
asynchronously. The resulting buffer should be accessible |
|
247 |
through the `org-export-stack' interface. |
|
248 |
|
|
249 |
When optional argument SUBTREEP is non-nil, export the sub-tree |
|
250 |
at point, extracting information from the headline properties |
|
251 |
first. |
|
252 |
|
|
253 |
When optional argument VISIBLE-ONLY is non-nil, don't export |
|
254 |
contents of hidden elements. |
|
255 |
|
|
256 |
When optional argument BODY-ONLY is non-nil, strip document |
|
257 |
keywords from output. |
|
258 |
|
|
259 |
EXT-PLIST, when provided, is a property list with external |
|
260 |
parameters overriding Org default settings, but still inferior to |
|
261 |
file-local settings. |
|
262 |
|
|
263 |
Export is done in a buffer named \"*Org ORG Export*\", which will |
|
264 |
be displayed when `org-export-show-temporary-export-buffer' is |
|
265 |
non-nil." |
|
266 |
(interactive) |
|
267 |
(org-export-to-buffer 'org "*Org ORG Export*" |
|
268 |
async subtreep visible-only body-only ext-plist (lambda () (org-mode)))) |
|
269 |
|
|
270 |
;;;###autoload |
|
271 |
(defun org-org-export-to-org |
|
272 |
(&optional async subtreep visible-only body-only ext-plist) |
|
273 |
"Export current buffer to an org file. |
|
274 |
|
|
275 |
If narrowing is active in the current buffer, only export its |
|
276 |
narrowed part. |
|
277 |
|
|
278 |
If a region is active, export that region. |
|
279 |
|
|
280 |
A non-nil optional argument ASYNC means the process should happen |
|
281 |
asynchronously. The resulting file should be accessible through |
|
282 |
the `org-export-stack' interface. |
|
283 |
|
|
284 |
When optional argument SUBTREEP is non-nil, export the sub-tree |
|
285 |
at point, extracting information from the headline properties |
|
286 |
first. |
|
287 |
|
|
288 |
When optional argument VISIBLE-ONLY is non-nil, don't export |
|
289 |
contents of hidden elements. |
|
290 |
|
|
291 |
When optional argument BODY-ONLY is non-nil, strip document |
|
292 |
keywords from output. |
|
293 |
|
|
294 |
EXT-PLIST, when provided, is a property list with external |
|
295 |
parameters overriding Org default settings, but still inferior to |
|
296 |
file-local settings. |
|
297 |
|
|
298 |
Return output file name." |
|
299 |
(interactive) |
|
300 |
(let ((outfile (org-export-output-file-name ".org" subtreep))) |
|
301 |
(org-export-to-file 'org outfile |
|
302 |
async subtreep visible-only body-only ext-plist))) |
|
303 |
|
|
304 |
;;;###autoload |
|
305 |
(defun org-org-publish-to-org (plist filename pub-dir) |
|
306 |
"Publish an org file to org. |
|
307 |
|
|
308 |
FILENAME is the filename of the Org file to be published. PLIST |
|
309 |
is the property list for the given project. PUB-DIR is the |
|
310 |
publishing directory. |
|
311 |
|
|
312 |
Return output file name." |
|
313 |
(org-publish-org-to 'org filename ".org" plist pub-dir) |
|
314 |
(when (plist-get plist :htmlized-source) |
|
315 |
(or (require 'htmlize nil t) |
|
316 |
(error "Please install htmlize from https://github.com/hniksic/emacs-htmlize")) |
|
317 |
(require 'ox-html) |
|
318 |
(let* ((org-inhibit-startup t) |
|
319 |
(htmlize-output-type 'css) |
|
320 |
(html-ext (concat "." (or (plist-get plist :html-extension) |
|
321 |
org-html-extension "html"))) |
|
322 |
(visitingp (find-buffer-visiting filename)) |
|
323 |
(work-buffer (or visitingp (find-file-noselect filename))) |
|
324 |
newbuf) |
|
325 |
(with-current-buffer work-buffer |
|
326 |
(org-font-lock-ensure) |
|
327 |
(outline-show-all) |
|
328 |
(org-show-block-all) |
|
329 |
(setq newbuf (htmlize-buffer))) |
|
330 |
(with-current-buffer newbuf |
|
331 |
(when org-org-htmlized-css-url |
|
332 |
(goto-char (point-min)) |
|
333 |
(and (re-search-forward |
|
334 |
"<style type=\"text/css\">[^\000]*?\n[ \t]*</style>.*" nil t) |
|
335 |
(replace-match |
|
336 |
(format |
|
337 |
"<link rel=\"stylesheet\" type=\"text/css\" href=\"%s\">" |
|
338 |
org-org-htmlized-css-url) |
|
339 |
t t))) |
|
340 |
(write-file (concat pub-dir (file-name-nondirectory filename) html-ext))) |
|
341 |
(kill-buffer newbuf) |
|
342 |
(unless visitingp (kill-buffer work-buffer))) |
|
343 |
;; FIXME: Why? Which buffer is this supposed to apply to? |
|
344 |
(set-buffer-modified-p nil))) |
|
345 |
|
|
346 |
|
|
347 |
(provide 'ox-org) |
|
348 |
|
|
349 |
;; Local variables: |
|
350 |
;; generated-autoload-file: "org-loaddefs.el" |
|
351 |
;; End: |
|
352 |
|
|
353 |
;;; ox-org.el ends here |