ox-beamer.el (44459B)
1 ;;; ox-beamer.el --- Beamer Back-End for Org Export Engine -*- lexical-binding: t; -*- 2 3 ;; Copyright (C) 2007-2023 Free Software Foundation, Inc. 4 5 ;; Author: Carsten Dominik <carsten.dominik AT gmail DOT com> 6 ;; Nicolas Goaziou <n.goaziou AT gmail DOT com> 7 ;; Maintainer: Nicolas Goaziou <mail@nicolasgoaziou.fr> 8 ;; Keywords: org, wp, tex 9 10 ;; This file is part of GNU Emacs. 11 12 ;; GNU Emacs is free software: you can redistribute it and/or modify 13 ;; it under the terms of the GNU General Public License as published by 14 ;; the Free Software Foundation, either version 3 of the License, or 15 ;; (at your option) any later version. 16 17 ;; GNU Emacs is distributed in the hope that it will be useful, 18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 ;; GNU General Public License for more details. 21 22 ;; You should have received a copy of the GNU General Public License 23 ;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. 24 25 ;;; Commentary: 26 ;; 27 ;; This library implements both a Beamer back-end, derived from the 28 ;; LaTeX one and a minor mode easing structure edition of the 29 ;; document. See Org manual for more information. 30 31 ;;; Code: 32 33 (require 'org-macs) 34 (org-assert-version) 35 36 (require 'cl-lib) 37 (require 'ox-latex) 38 39 ;; Install a default set-up for Beamer export. 40 (unless (assoc "beamer" org-latex-classes) 41 (add-to-list 'org-latex-classes 42 '("beamer" 43 "\\documentclass[presentation]{beamer}" 44 ("\\section{%s}" . "\\section*{%s}") 45 ("\\subsection{%s}" . "\\subsection*{%s}") 46 ("\\subsubsection{%s}" . "\\subsubsection*{%s}")))) 47 48 49 50 ;;; User-Configurable Variables 51 52 (defgroup org-export-beamer nil 53 "Options specific for using the beamer class in LaTeX export." 54 :tag "Org Beamer" 55 :group 'org-export 56 :version "24.2") 57 58 (defcustom org-beamer-frame-level 1 59 "The level at which headlines become frames. 60 61 Headlines at a lower level will be translated into a sectioning 62 structure. At a higher level, they will be translated into 63 blocks. 64 65 If a headline with a \"BEAMER_env\" property set to \"frame\" is 66 found within a tree, its level locally overrides this number. 67 68 This variable has no effect on headlines with the \"BEAMER_env\" 69 property set to either \"ignoreheading\", \"appendix\", or 70 \"note\", which will respectively, be invisible, become an 71 appendix or a note. 72 73 This integer is relative to the minimal level of a headline 74 within the parse tree, defined as 1." 75 :group 'org-export-beamer 76 :type 'integer) 77 78 (defcustom org-beamer-frame-default-options "" 79 "Default options string to use for frames. 80 For example, it could be set to \"allowframebreaks\"." 81 :group 'org-export-beamer 82 :type '(string :tag "[options]")) 83 84 (defcustom org-beamer-column-view-format 85 "%45ITEM %10BEAMER_env(Env) %10BEAMER_act(Act) %4BEAMER_col(Col) %8BEAMER_opt(Opt)" 86 "Column view format that should be used to fill the template." 87 :group 'org-export-beamer 88 :version "24.4" 89 :package-version '(Org . "8.0") 90 :type '(choice 91 (const :tag "Do not insert Beamer column view format" nil) 92 (string :tag "Beamer column view format"))) 93 94 (defcustom org-beamer-theme "default" 95 "Default theme used in Beamer presentations." 96 :group 'org-export-beamer 97 :version "24.4" 98 :package-version '(Org . "8.0") 99 :type '(choice 100 (const :tag "Do not insert a Beamer theme" nil) 101 (string :tag "Beamer theme"))) 102 103 (defcustom org-beamer-environments-extra nil 104 "Environments triggered by tags in Beamer export. 105 Each entry has 4 elements: 106 107 name Name of the environment 108 key Selection key for `org-beamer-select-environment' 109 open The opening template for the environment, with the following escapes 110 %a the action/overlay specification 111 %A the default action/overlay specification 112 %R the raw BEAMER_act value 113 %o the options argument, with square brackets 114 %O the raw BEAMER_opt value 115 %h the headline text 116 %r the raw headline text (i.e. without any processing) 117 %H if there is headline text, that raw text in {} braces 118 %U if there is headline text, that raw text in [] brackets 119 close The closing string of the environment." 120 :group 'org-export-beamer 121 :version "24.4" 122 :package-version '(Org . "8.1") 123 :type '(repeat 124 (list 125 (string :tag "Environment") 126 (string :tag "Selection key") 127 (string :tag "Begin") 128 (string :tag "End")))) 129 130 (defcustom org-beamer-outline-frame-title "Outline" 131 "Default title of a frame containing an outline." 132 :group 'org-export-beamer 133 :type '(string :tag "Outline frame title")) 134 135 (defcustom org-beamer-outline-frame-options "" 136 "Outline frame options appended after \\begin{frame}. 137 You might want to put e.g. \"allowframebreaks=0.9\" here." 138 :group 'org-export-beamer 139 :type '(string :tag "Outline frame options")) 140 141 142 (defcustom org-beamer-subtitle-format "\\subtitle{%s}" 143 "Format string used for transcoded subtitle. 144 The format string should have at most one \"%s\"-expression, 145 which is replaced with the subtitle." 146 :group 'org-export-beamer 147 :version "26.1" 148 :package-version '(Org . "8.3") 149 :type '(string :tag "Format string")) 150 151 152 ;;; Internal Variables 153 154 (defconst org-beamer-column-widths 155 "0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 0.0 :ETC" 156 "The column widths that should be installed as allowed property values.") 157 158 (defconst org-beamer-environments-special 159 '(("againframe" "A") 160 ("appendix" "x") 161 ("column" "c") 162 ("columns" "C") 163 ("frame" "f") 164 ("fullframe" "F") 165 ("ignoreheading" "i") 166 ("note" "n") 167 ("noteNH" "N")) 168 "Alist of environments treated in a special way by the back-end. 169 Keys are environment names, as strings, values are bindings used 170 in `org-beamer-select-environment'. Environments listed here, 171 along with their binding, are hard coded and cannot be modified 172 through `org-beamer-environments-extra' variable.") 173 174 (defconst org-beamer-environments-default 175 '(("block" "b" "\\begin{block}%a{%h}" "\\end{block}") 176 ("alertblock" "a" "\\begin{alertblock}%a{%h}" "\\end{alertblock}") 177 ("verse" "v" "\\begin{verse}%a %% %h" "\\end{verse}") 178 ("quotation" "q" "\\begin{quotation}%a %% %h" "\\end{quotation}") 179 ("quote" "Q" "\\begin{quote}%a %% %h" "\\end{quote}") 180 ("structureenv" "s" "\\begin{structureenv}%a %% %h" "\\end{structureenv}") 181 ("theorem" "t" "\\begin{theorem}%a[%h]" "\\end{theorem}") 182 ("definition" "d" "\\begin{definition}%a[%h]" "\\end{definition}") 183 ("example" "e" "\\begin{example}%a[%h]" "\\end{example}") 184 ("exampleblock" "E" "\\begin{exampleblock}%a{%h}" "\\end{exampleblock}") 185 ("proof" "p" "\\begin{proof}%a[%h]" "\\end{proof}") 186 ("beamercolorbox" "o" "\\begin{beamercolorbox}%o{%h}" "\\end{beamercolorbox}")) 187 "Environments triggered by properties in Beamer export. 188 These are the defaults - for user definitions, see 189 `org-beamer-environments-extra'.") 190 191 (defconst org-beamer-verbatim-elements 192 '(code example-block fixed-width inline-src-block src-block verbatim) 193 "List of element or object types producing verbatim text. 194 This is used internally to determine when a frame should have the 195 \"fragile\" option.") 196 197 198 199 ;;; Internal functions 200 201 (defun org-beamer--normalize-argument (argument type) 202 "Return ARGUMENT string with proper boundaries. 203 204 TYPE is a symbol among the following: 205 `action' Return ARGUMENT within angular brackets. 206 `defaction' Return ARGUMENT within both square and angular brackets. 207 `option' Return ARGUMENT within square brackets." 208 (if (not (string-match "\\S-" argument)) "" 209 (cl-case type 210 (action (format "<%s>" (org-unbracket-string "<" ">" argument))) 211 (defaction 212 (format "[<%s>]" 213 (org-unbracket-string "<" ">" (org-unbracket-string "[" "]" argument)))) 214 (option (format "[%s]" (org-unbracket-string "[" "]" argument))) 215 (otherwise (error "Invalid `type' argument to `org-beamer--normalize-argument': %s" 216 type))))) 217 218 (defun org-beamer--element-has-overlay-p (element) 219 "Non-nil when ELEMENT has an overlay specified. 220 An element has an overlay specification when it starts with an 221 `beamer' export-snippet whose value is between angular brackets. 222 Return overlay specification, as a string, or nil." 223 (let ((first-object (car (org-element-contents element)))) 224 (when (eq (org-element-type first-object) 'export-snippet) 225 (let ((value (org-element-property :value first-object))) 226 (and (string-prefix-p "<" value) (string-suffix-p ">" value) 227 value))))) 228 229 230 231 ;;; Define Back-End 232 233 (org-export-define-derived-backend 'beamer 'latex 234 :menu-entry 235 '(?l 1 236 ((?B "As LaTeX buffer (Beamer)" org-beamer-export-as-latex) 237 (?b "As LaTeX file (Beamer)" org-beamer-export-to-latex) 238 (?P "As PDF file (Beamer)" org-beamer-export-to-pdf) 239 (?O "As PDF file and open (Beamer)" 240 (lambda (a s v b) 241 (if a (org-beamer-export-to-pdf t s v b) 242 (org-open-file (org-beamer-export-to-pdf nil s v b))))))) 243 :options-alist 244 '((:headline-levels nil "H" org-beamer-frame-level) 245 (:latex-class "LATEX_CLASS" nil "beamer" t) 246 (:beamer-subtitle-format nil nil org-beamer-subtitle-format) 247 (:beamer-column-view-format "COLUMNS" nil org-beamer-column-view-format) 248 (:beamer-theme "BEAMER_THEME" nil org-beamer-theme) 249 (:beamer-color-theme "BEAMER_COLOR_THEME" nil nil t) 250 (:beamer-font-theme "BEAMER_FONT_THEME" nil nil t) 251 (:beamer-inner-theme "BEAMER_INNER_THEME" nil nil t) 252 (:beamer-outer-theme "BEAMER_OUTER_THEME" nil nil t) 253 (:beamer-header "BEAMER_HEADER" nil nil newline) 254 (:beamer-environments-extra nil nil org-beamer-environments-extra) 255 (:beamer-frame-default-options nil nil org-beamer-frame-default-options) 256 (:beamer-outline-frame-options nil nil org-beamer-outline-frame-options) 257 (:beamer-outline-frame-title nil nil org-beamer-outline-frame-title)) 258 :translate-alist '((bold . org-beamer-bold) 259 (export-block . org-beamer-export-block) 260 (export-snippet . org-beamer-export-snippet) 261 (headline . org-beamer-headline) 262 (item . org-beamer-item) 263 (keyword . org-beamer-keyword) 264 (link . org-beamer-link) 265 (plain-list . org-beamer-plain-list) 266 (radio-target . org-beamer-radio-target) 267 (template . org-beamer-template))) 268 269 270 271 ;;; Transcode Functions 272 273 ;;;; Bold 274 275 (defun org-beamer-bold (bold contents _info) 276 "Transcode BLOCK object into Beamer code. 277 CONTENTS is the text being bold. INFO is a plist used as 278 a communication channel." 279 (format "\\alert%s{%s}" 280 (or (org-beamer--element-has-overlay-p bold) "") 281 contents)) 282 283 284 ;;;; Export Block 285 286 (defun org-beamer-export-block (export-block _contents _info) 287 "Transcode an EXPORT-BLOCK element into Beamer code. 288 CONTENTS is nil. INFO is a plist used as a communication 289 channel." 290 (when (member (org-element-property :type export-block) '("BEAMER" "LATEX")) 291 (org-remove-indentation (org-element-property :value export-block)))) 292 293 294 ;;;; Export Snippet 295 296 (defun org-beamer-export-snippet (export-snippet _contents info) 297 "Transcode an EXPORT-SNIPPET object into Beamer code. 298 CONTENTS is nil. INFO is a plist used as a communication 299 channel." 300 (let ((backend (org-export-snippet-backend export-snippet)) 301 (value (org-element-property :value export-snippet))) 302 ;; Only "latex" and "beamer" snippets are retained. 303 (cond ((eq backend 'latex) value) 304 ;; Ignore "beamer" snippets specifying overlays. 305 ((and (eq backend 'beamer) 306 (or (org-export-get-previous-element export-snippet info) 307 (not (string-match "\\`<.*>\\'" value)))) 308 value)))) 309 310 311 ;;;; Headline 312 ;; 313 ;; The main function to translate a headline is 314 ;; `org-beamer-headline'. 315 ;; 316 ;; Depending on the level at which a headline is considered as 317 ;; a frame (given by `org-beamer--frame-level'), the headline is 318 ;; either a section (`org-beamer--format-section'), a frame 319 ;; (`org-beamer--format-frame') or a block 320 ;; (`org-beamer--format-block'). 321 ;; 322 ;; `org-beamer-headline' also takes care of special environments 323 ;; like "ignoreheading", "note", "noteNH", "appendix" and 324 ;; "againframe". 325 326 (defun org-beamer--get-label (headline info) 327 "Return label for HEADLINE, as a string. 328 329 INFO is a plist used as a communication channel. 330 331 The value is either the label specified in \"BEAMER_opt\" 332 property, the custom ID, if there is one and 333 `:latex-prefer-user-labels' property has a non-nil value, or 334 a unique internal label. This function assumes HEADLINE will be 335 treated as a frame." 336 (cond 337 ((let ((opt (org-element-property :BEAMER_OPT headline))) 338 (and (stringp opt) 339 (string-match "\\(?:^\\|,\\)label=\\(.*?\\)\\(?:$\\|,\\)" opt) 340 (let ((label (match-string 1 opt))) 341 (if (string-match-p "\\`{.*}\\'" label) 342 (substring label 1 -1) 343 label))))) 344 ((and (plist-get info :latex-prefer-user-labels) 345 (org-element-property :CUSTOM_ID headline))) 346 (t (format "sec:%s" (org-export-get-reference headline info))))) 347 348 (defun org-beamer--frame-level (headline info) 349 "Return frame level in subtree containing HEADLINE. 350 INFO is a plist used as a communication channel." 351 (or 352 ;; 1. Look for "frame" environment in parents, starting from the 353 ;; farthest. 354 (catch 'exit 355 (dolist (parent (nreverse (org-element-lineage headline))) 356 (let ((env (org-element-property :BEAMER_ENV parent))) 357 (when (and env (member-ignore-case env '("frame" "fullframe"))) 358 (throw 'exit (org-export-get-relative-level parent info)))))) 359 ;; 2. Look for "frame" environment in HEADLINE. 360 (let ((env (org-element-property :BEAMER_ENV headline))) 361 (and env (member-ignore-case env '("frame" "fullframe")) 362 (org-export-get-relative-level headline info))) 363 ;; 3. Look for "frame" environment in sub-tree. 364 (org-element-map headline 'headline 365 (lambda (hl) 366 (let ((env (org-element-property :BEAMER_ENV hl))) 367 (when (and env (member-ignore-case env '("frame" "fullframe"))) 368 (org-export-get-relative-level hl info)))) 369 info 'first-match) 370 ;; 4. No "frame" environment in tree: use default value. 371 (plist-get info :headline-levels))) 372 373 (defun org-beamer--format-section (headline contents info) 374 "Format HEADLINE as a sectioning part. 375 CONTENTS holds the contents of the headline. INFO is a plist 376 used as a communication channel." 377 (let ((latex-headline 378 (org-export-with-backend 379 ;; We create a temporary export back-end which behaves the 380 ;; same as current one, but adds "\protect" in front of the 381 ;; output of some objects. 382 (org-export-create-backend 383 :parent 'latex 384 :transcoders 385 (let ((protected-output 386 (lambda (object contents info) 387 (let ((code (org-export-with-backend 388 'beamer object contents info))) 389 (if (org-string-nw-p code) (concat "\\protect" code) 390 code))))) 391 (mapcar (lambda (type) (cons type protected-output)) 392 '(bold footnote-reference italic strike-through timestamp 393 underline)))) 394 headline 395 contents 396 info)) 397 (mode-specs (org-element-property :BEAMER_ACT headline))) 398 (if (and mode-specs 399 (string-match "\\`\\\\\\(.*?\\)\\(?:\\*\\|\\[.*\\]\\)?{" 400 latex-headline)) 401 ;; Insert overlay specifications. 402 (replace-match (concat (match-string 1 latex-headline) 403 (format "<%s>" mode-specs)) 404 nil nil latex-headline 1) 405 latex-headline))) 406 407 (defun org-beamer--format-frame (headline contents info) 408 "Format HEADLINE as a frame. 409 CONTENTS holds the contents of the headline. INFO is a plist 410 used as a communication channel." 411 (let ((fragilep 412 ;; FRAGILEP is non-nil when HEADLINE contains an element 413 ;; among `org-beamer-verbatim-elements'. 414 (org-element-map headline org-beamer-verbatim-elements 'identity 415 info 'first-match))) 416 (concat "\\begin{frame}" 417 ;; Overlay specification, if any. When surrounded by 418 ;; square brackets, consider it as a default 419 ;; specification. 420 (let ((action (org-element-property :BEAMER_ACT headline))) 421 (cond 422 ((not action) "") 423 ((string-match "\\`\\[.*\\]\\'" action ) 424 (org-beamer--normalize-argument action 'defaction)) 425 (t (org-beamer--normalize-argument action 'action)))) 426 ;; Options, if any. 427 (let* ((beamer-opt (org-element-property :BEAMER_OPT headline)) 428 (options 429 ;; Collect nonempty options from default value and 430 ;; headline's properties. 431 (cl-remove-if-not #'org-string-nw-p 432 (append 433 (org-split-string 434 (plist-get info :beamer-frame-default-options) ",") 435 (and beamer-opt 436 (org-split-string 437 ;; Remove square brackets if user provided 438 ;; them. 439 (and (string-match "^\\[?\\(.*\\)\\]?$" beamer-opt) 440 (match-string 1 beamer-opt)) 441 ","))))) 442 (fragile 443 ;; Add "fragile" option if necessary. 444 (and fragilep 445 (not (member "fragile" options)) 446 (list "fragile"))) 447 (label 448 ;; Provide an automatic label for the frame unless 449 ;; the user specified one. Also refrain from 450 ;; labeling `allowframebreaks' frames; this is not 451 ;; allowed by Beamer. 452 (and (not (member "allowframebreaks" options)) 453 (not (cl-some (lambda (s) (string-match-p "^label=" s)) 454 options)) 455 (list 456 (let ((label (org-beamer--get-label headline info))) 457 ;; Labels containing colons need to be 458 ;; wrapped within braces. 459 (format (if (string-match-p ":" label) 460 "label={%s}" 461 "label=%s") 462 label)))))) 463 ;; Change options list into a string. 464 (org-beamer--normalize-argument 465 (mapconcat #'identity (append label fragile options) ",") 466 'option)) 467 ;; Title. 468 (let ((env (org-element-property :BEAMER_ENV headline))) 469 (format "{%s}" 470 (if (and env (equal (downcase env) "fullframe")) "" 471 (org-export-data 472 (org-element-property :title headline) info)))) 473 "\n" 474 ;; The following workaround is required in fragile frames 475 ;; as Beamer will append "\par" to the beginning of the 476 ;; contents. So we need to make sure the command is 477 ;; separated from the contents by at least one space. If 478 ;; it isn't, it will create "\parfirst-word" command and 479 ;; remove the first word from the contents in the PDF 480 ;; output. 481 (if (not fragilep) contents 482 (replace-regexp-in-string "\\`\n*" "\\& " (or contents ""))) 483 "\\end{frame}"))) 484 485 (defun org-beamer--format-block (headline contents info) 486 "Format HEADLINE as a block. 487 CONTENTS holds the contents of the headline. INFO is a plist 488 used as a communication channel." 489 (let* ((column-width (org-element-property :BEAMER_COL headline)) 490 ;; ENVIRONMENT defaults to "block" if none is specified and 491 ;; there is no column specification. If there is a column 492 ;; specified but still no explicit environment, ENVIRONMENT 493 ;; is "column". 494 (environment (let ((env (org-element-property :BEAMER_ENV headline))) 495 (cond 496 ;; "block" is the fallback environment. 497 ((and (not env) (not column-width)) "block") 498 ;; "column" only. 499 ((not env) "column") 500 ;; Use specified environment. 501 (t env)))) 502 (raw-title (org-element-property :raw-value headline)) 503 (env-format 504 (cond ((member environment '("column" "columns")) nil) 505 ((assoc environment 506 (append (plist-get info :beamer-environments-extra) 507 org-beamer-environments-default))) 508 (t (user-error "Wrong block type at a headline named \"%s\"" 509 raw-title)))) 510 (title (org-export-data (org-element-property :title headline) info)) 511 (raw-options (org-element-property :BEAMER_OPT headline)) 512 (options (if raw-options 513 (org-beamer--normalize-argument raw-options 'option) 514 "")) 515 ;; Start a "columns" environment when explicitly requested or 516 ;; when there is no previous headline or the previous 517 ;; headline do not have a BEAMER_column property. 518 (parent-env (org-element-property 519 :BEAMER_ENV (org-export-get-parent-headline headline))) 520 (start-columns-p 521 (or (equal environment "columns") 522 (and column-width 523 (not (and parent-env 524 (equal (downcase parent-env) "columns"))) 525 (or (org-export-first-sibling-p headline info) 526 (not (org-element-property 527 :BEAMER_COL 528 (org-export-get-previous-element 529 headline info))))))) 530 ;; End the "columns" environment when explicitly requested or 531 ;; when there is no next headline or the next headline do not 532 ;; have a BEAMER_column property. 533 (end-columns-p 534 (or (equal environment "columns") 535 (and column-width 536 (not (and parent-env 537 (equal (downcase parent-env) "columns"))) 538 (or (org-export-last-sibling-p headline info) 539 (not (org-element-property 540 :BEAMER_COL 541 (org-export-get-next-element headline info)))))))) 542 (concat 543 (when start-columns-p 544 ;; Column can accept options only when the environment is 545 ;; explicitly defined. 546 (if (not (equal environment "columns")) "\\begin{columns}\n" 547 (format "\\begin{columns}%s\n" options))) 548 (when column-width 549 (format "\\begin{column}%s{%s}\n" 550 ;; One can specify placement for column only when 551 ;; HEADLINE stands for a column on its own. 552 (if (equal environment "column") options "") 553 (format "%s\\columnwidth" column-width))) 554 ;; Block's opening string. 555 (when (nth 2 env-format) 556 (concat 557 (org-fill-template 558 (nth 2 env-format) 559 (nconc 560 ;; If BEAMER_act property has its value enclosed in square 561 ;; brackets, it is a default overlay specification and 562 ;; overlay specification is empty. Otherwise, it is an 563 ;; overlay specification and the default one is nil. 564 (let ((action (org-element-property :BEAMER_ACT headline))) 565 (cond 566 ((not action) (list (cons "a" "") (cons "A" "") (cons "R" ""))) 567 ((and (string-prefix-p "[" action) 568 (string-suffix-p "]" action)) 569 (list 570 (cons "A" (org-beamer--normalize-argument action 'defaction)) 571 (cons "a" "") 572 (cons "R" action))) 573 (t 574 (list (cons "a" (org-beamer--normalize-argument action 'action)) 575 (cons "A" "") 576 (cons "R" action))))) 577 (list (cons "o" options) 578 (cons "O" (or raw-options "")) 579 (cons "h" title) 580 (cons "r" raw-title) 581 (cons "H" (if (equal raw-title "") "" 582 (format "{%s}" raw-title))) 583 (cons "U" (if (equal raw-title "") "" 584 (format "[%s]" raw-title)))))) 585 "\n")) 586 contents 587 ;; Block's closing string, if any. 588 (and (nth 3 env-format) (concat (nth 3 env-format) "\n")) 589 (when column-width "\\end{column}\n") 590 (when end-columns-p "\\end{columns}")))) 591 592 (defun org-beamer-headline (headline contents info) 593 "Transcode HEADLINE element into Beamer code. 594 CONTENTS is the contents of the headline. INFO is a plist used 595 as a communication channel." 596 (unless (org-element-property :footnote-section-p headline) 597 (let ((level (org-export-get-relative-level headline info)) 598 (frame-level (org-beamer--frame-level headline info)) 599 (environment (let ((env (org-element-property :BEAMER_ENV headline))) 600 (or (org-string-nw-p env) "block")))) 601 (cond 602 ;; Case 1: Resume frame specified by "BEAMER_ref" property. 603 ((equal environment "againframe") 604 (let ((ref (org-element-property :BEAMER_REF headline))) 605 ;; Reference to frame being resumed is mandatory. Ignore 606 ;; the whole headline if it isn't provided. 607 (when (org-string-nw-p ref) 608 (concat "\\againframe" 609 ;; Overlay specification. 610 (let ((overlay (org-element-property :BEAMER_ACT headline))) 611 (when overlay 612 (org-beamer--normalize-argument 613 overlay 614 (if (string-match "\\`\\[.*\\]\\'" overlay) 'defaction 615 'action)))) 616 ;; Options. 617 (let ((options (org-element-property :BEAMER_OPT headline))) 618 (when options 619 (org-beamer--normalize-argument options 'option))) 620 ;; Resolve reference provided by "BEAMER_ref" 621 ;; property. This is done by building a minimal 622 ;; fake link and calling the appropriate resolve 623 ;; function, depending on the reference syntax. 624 (let ((target 625 (if (string-match "\\`\\(id:\\|#\\)" ref) 626 (org-export-resolve-id-link 627 `(link (:path ,(substring ref (match-end 0)))) 628 info) 629 (org-export-resolve-fuzzy-link 630 `(link (:path 631 ;; Look for headlines only. 632 ,(if (eq (string-to-char ref) ?*) ref 633 (concat "*" ref)))) 634 info)))) 635 ;; Now use user-defined label provided in TARGET 636 ;; headline, or fallback to standard one. 637 (format "{%s}" (org-beamer--get-label target info))))))) 638 ;; Case 2: Creation of an appendix is requested. 639 ((equal environment "appendix") 640 (concat "\\appendix" 641 (org-element-property :BEAMER_ACT headline) 642 "\n" 643 (make-string (org-element-property :pre-blank headline) ?\n) 644 contents)) 645 ;; Case 3: Ignore heading. 646 ((equal environment "ignoreheading") 647 (concat (make-string (org-element-property :pre-blank headline) ?\n) 648 contents)) 649 ;; Case 4: HEADLINE is a note. 650 ((member environment '("note" "noteNH")) 651 (concat "\\note" 652 ;; Overlay specification. 653 (let ((overlay (org-element-property :BEAMER_ACT headline))) 654 (when overlay 655 (org-beamer--normalize-argument 656 overlay 657 (if (string-match "\\`\\[.*\\]\\'" overlay) 658 'defaction 'action)))) 659 (format "{%s}" 660 (concat (and (equal environment "note") 661 (concat 662 (org-export-data 663 (org-element-property :title headline) 664 info) 665 "\n")) 666 (org-trim contents))))) 667 ;; Case 5: HEADLINE is a frame. 668 ((= level frame-level) 669 (org-beamer--format-frame headline contents info)) 670 ;; Case 6: Regular section, extracted from 671 ;; `org-latex-classes'. 672 ((< level frame-level) 673 (org-beamer--format-section headline contents info)) 674 ;; Case 7: Otherwise, HEADLINE is a block. 675 (t (org-beamer--format-block headline contents info)))))) 676 677 678 ;;;; Item 679 680 (defun org-beamer-item (item contents info) 681 "Transcode an ITEM element into Beamer code. 682 CONTENTS holds the contents of the item. INFO is a plist holding 683 contextual information." 684 (org-export-with-backend 685 ;; Delegate item export to `latex'. However, we use `beamer' 686 ;; transcoders for objects in the description tag. 687 (org-export-create-backend 688 :parent 'beamer 689 :transcoders 690 (list 691 (cons 692 'item 693 (lambda (item _c _i) 694 (let ((action 695 (let ((first (car (org-element-contents item)))) 696 (and (eq (org-element-type first) 'paragraph) 697 (org-beamer--element-has-overlay-p first)))) 698 (output (org-latex-item item contents info))) 699 (if (not (and action (string-match "\\\\item" output))) output 700 ;; If the item starts with a paragraph and that paragraph 701 ;; starts with an export snippet specifying an overlay, 702 ;; append it to the \item command. 703 (replace-match (concat "\\\\item" action) nil nil output))))))) 704 item contents info)) 705 706 707 ;;;; Keyword 708 709 (defun org-beamer-keyword (keyword contents info) 710 "Transcode a KEYWORD element into Beamer code. 711 CONTENTS is nil. INFO is a plist used as a communication 712 channel." 713 (let ((key (org-element-property :key keyword)) 714 (value (org-element-property :value keyword))) 715 ;; Handle specifically BEAMER and TOC (headlines only) keywords. 716 ;; Otherwise, fallback to `latex' back-end. 717 (cond 718 ((equal key "BEAMER") value) 719 ((and (equal key "TOC") (string-match "\\<headlines\\>" value)) 720 (let ((depth (or (and (string-match "[0-9]+" value) 721 (string-to-number (match-string 0 value))) 722 (plist-get info :with-toc))) 723 (options (and (string-match "\\[.*?\\]" value) 724 (match-string 0 value)))) 725 (concat 726 (when (wholenump depth) (format "\\setcounter{tocdepth}{%s}\n" depth)) 727 "\\tableofcontents" options))) 728 (t (org-export-with-backend 'latex keyword contents info))))) 729 730 731 ;;;; Link 732 733 (defun org-beamer-link (link contents info) 734 "Transcode a LINK object into Beamer code. 735 CONTENTS is the description part of the link. INFO is a plist 736 used as a communication channel." 737 (or (org-export-custom-protocol-maybe link contents 'beamer info) 738 ;; Fall-back to LaTeX export. However, prefer "\hyperlink" over 739 ;; "\hyperref" since the former handles overlay specifications. 740 (let* ((latex-link (org-export-with-backend 'latex link contents info)) 741 (parent (org-export-get-parent-element link)) 742 (attr (org-export-read-attribute :attr_beamer parent)) 743 (overlay (plist-get attr :overlay))) 744 (cond ((string-match "\\`\\\\hyperref\\[\\(.*?\\)\\]" latex-link) 745 (replace-match 746 (format "\\\\hyperlink%s{\\1}" 747 (or (org-beamer--element-has-overlay-p link) "")) 748 nil nil latex-link)) 749 ((string-match "\\\\include\\(graphics\\|svg\\)\\([[{]?\\)" latex-link) 750 ;; Check for overlay specification and insert if 751 ;; present. 752 (replace-match 753 (format "\\\\include\\1%s\\2" 754 (if overlay overlay "")) 755 nil nil latex-link)) 756 (t latex-link))))) 757 758 759 ;;;; Plain List 760 ;; 761 ;; Plain lists support `:environment', `:overlay' and `:options' 762 ;; attributes. 763 764 (defun org-beamer-plain-list (plain-list contents info) 765 "Transcode a PLAIN-LIST element into Beamer code. 766 CONTENTS is the contents of the list. INFO is a plist holding 767 contextual information." 768 (let* ((type (org-element-property :type plain-list)) 769 (attributes (org-combine-plists 770 (org-export-read-attribute :attr_latex plain-list) 771 (org-export-read-attribute :attr_beamer plain-list))) 772 (latex-type (let ((env (plist-get attributes :environment))) 773 (cond (env) 774 ((eq type 'ordered) "enumerate") 775 ((eq type 'descriptive) "description") 776 (t "itemize"))))) 777 (org-latex--wrap-label 778 plain-list 779 (format "\\begin{%s}%s%s\n%s\\end{%s}" 780 latex-type 781 ;; Default overlay specification, if any. 782 (org-beamer--normalize-argument 783 (or (plist-get attributes :overlay) "") 784 'defaction) 785 ;; Second optional argument depends on the list type. 786 (org-beamer--normalize-argument 787 (or (plist-get attributes :options) "") 788 'option) 789 ;; Eventually insert contents and close environment. 790 contents 791 latex-type) 792 info))) 793 794 795 ;;;; Radio Target 796 797 (defun org-beamer-radio-target (radio-target text info) 798 "Transcode a RADIO-TARGET object into Beamer code. 799 TEXT is the text of the target. INFO is a plist holding 800 contextual information." 801 (format "\\hypertarget%s{%s}{%s}" 802 (or (org-beamer--element-has-overlay-p radio-target) "") 803 (org-export-get-reference radio-target info) 804 text)) 805 806 807 ;;;; Template 808 ;; 809 ;; Template used is similar to the one used in `latex' back-end, 810 ;; excepted for the table of contents and Beamer themes. 811 812 (defun org-beamer-template (contents info) 813 "Return complete document string after Beamer conversion. 814 CONTENTS is the transcoded contents string. INFO is a plist 815 holding export options." 816 (let ((title (org-export-data (plist-get info :title) info)) 817 (subtitle (org-export-data (plist-get info :subtitle) info))) 818 (concat 819 ;; Time-stamp. 820 (and (plist-get info :time-stamp-file) 821 (format-time-string "%% Created %Y-%m-%d %a %H:%M\n")) 822 ;; LaTeX compiler 823 (org-latex--insert-compiler info) 824 ;; Document class and packages. 825 (org-latex-make-preamble info) 826 ;; Insert themes. 827 (let ((format-theme 828 (lambda (prop command) 829 (let ((theme (plist-get info prop))) 830 (when theme 831 (concat command 832 (if (not (string-match "\\[.*\\]" theme)) 833 (format "{%s}\n" theme) 834 (format "%s{%s}\n" 835 (match-string 0 theme) 836 (org-trim 837 (replace-match "" nil nil theme)))))))))) 838 (mapconcat (lambda (args) (apply format-theme args)) 839 '((:beamer-theme "\\usetheme") 840 (:beamer-color-theme "\\usecolortheme") 841 (:beamer-font-theme "\\usefonttheme") 842 (:beamer-inner-theme "\\useinnertheme") 843 (:beamer-outer-theme "\\useoutertheme")) 844 "")) 845 ;; Possibly limit depth for headline numbering. 846 (let ((sec-num (plist-get info :section-numbers))) 847 (when (integerp sec-num) 848 (format "\\setcounter{secnumdepth}{%d}\n" sec-num))) 849 ;; Author. 850 (let ((author (and (plist-get info :with-author) 851 (let ((auth (plist-get info :author))) 852 (and auth (org-export-data auth info))))) 853 (email (and (plist-get info :with-email) 854 (org-export-data (plist-get info :email) info)))) 855 (cond ((and author email (not (string= "" email))) 856 (format "\\author{%s\\thanks{%s}}\n" author email)) 857 ((or author email) (format "\\author{%s}\n" (or author email))))) 858 ;; Date. 859 (let ((date (and (plist-get info :with-date) (org-export-get-date info)))) 860 (format "\\date{%s}\n" (org-export-data date info))) 861 ;; Title 862 (format "\\title{%s}\n" title) 863 (when (org-string-nw-p subtitle) 864 (concat (format (plist-get info :beamer-subtitle-format) subtitle) "\n")) 865 ;; Beamer-header 866 (let ((beamer-header (plist-get info :beamer-header))) 867 (when beamer-header 868 (format "%s\n" (plist-get info :beamer-header)))) 869 ;; 9. Hyperref options. 870 (let ((template (plist-get info :latex-hyperref-template))) 871 (and (stringp template) 872 (format-spec template (org-latex--format-spec info)))) 873 ;; engrave-faces-latex preamble 874 (when (and (eq org-latex-src-block-backend 'engraved) 875 (org-element-map (plist-get info :parse-tree) 876 '(src-block inline-src-block) #'identity 877 info t)) 878 (org-latex-generate-engraved-preamble info)) 879 ;; Document start. 880 "\\begin{document}\n\n" 881 ;; Title command. 882 (org-element-normalize-string 883 (cond ((not (plist-get info :with-title)) nil) 884 ((string= "" title) nil) 885 ((not (stringp org-latex-title-command)) nil) 886 ((string-match "\\(?:[^%]\\|^\\)%s" 887 org-latex-title-command) 888 (format org-latex-title-command title)) 889 (t org-latex-title-command))) 890 ;; Table of contents. 891 (let ((depth (plist-get info :with-toc))) 892 (when depth 893 (concat 894 (format "\\begin{frame}%s{%s}\n" 895 (org-beamer--normalize-argument 896 (plist-get info :beamer-outline-frame-options) 'option) 897 (plist-get info :beamer-outline-frame-title)) 898 (when (wholenump depth) 899 (format "\\setcounter{tocdepth}{%d}\n" depth)) 900 "\\tableofcontents\n" 901 "\\end{frame}\n\n"))) 902 ;; Document's body. 903 contents 904 ;; Creator. 905 (if (plist-get info :with-creator) 906 (concat (plist-get info :creator) "\n") 907 "") 908 ;; Document end. 909 "\\end{document}"))) 910 911 912 913 ;;; Minor Mode 914 915 916 (defvar org-beamer-mode-map 917 (let ((map (make-sparse-keymap))) 918 (define-key map "\C-c\C-b" 'org-beamer-select-environment) 919 map) 920 "The keymap for `org-beamer-mode'.") 921 922 ;;;###autoload 923 (define-minor-mode org-beamer-mode 924 "Support for editing Beamer oriented Org mode files." 925 :lighter " Bm") 926 927 (when (fboundp 'font-lock-add-keywords) 928 (font-lock-add-keywords 929 'org-mode 930 '((":\\(B_[a-z]+\\|BMCOL\\):" 1 'org-beamer-tag prepend)) 931 'prepend)) 932 933 (defface org-beamer-tag '((t (:box (:line-width 1 :color grey40)))) 934 "The special face for beamer tags." 935 :group 'org-export-beamer) 936 937 (defun org-beamer-property-changed (property value) 938 "Track the BEAMER_env property with tags. 939 PROPERTY is the name of the modified property. VALUE is its new 940 value." 941 (cond 942 ((equal property "BEAMER_env") 943 (save-excursion 944 (org-back-to-heading t) 945 ;; Filter out Beamer-related tags and install environment tag. 946 (let ((tags (cl-remove-if (lambda (x) (string-match "^B_" x)) 947 (org-get-tags nil t))) 948 (env-tag (and (org-string-nw-p value) (concat "B_" value)))) 949 (org-set-tags (if env-tag (cons env-tag tags) tags)) 950 (when env-tag (org-toggle-tag env-tag 'on))))) 951 ((equal property "BEAMER_col") 952 (org-toggle-tag "BMCOL" (if (org-string-nw-p value) 'on 'off))))) 953 954 (add-hook 'org-property-changed-functions 'org-beamer-property-changed) 955 956 (defun org-beamer-allowed-property-values (property) 957 "Supply allowed values for PROPERTY." 958 (cond 959 ((and (equal property "BEAMER_env") 960 (not (org-entry-get nil (concat property "_ALL") 'inherit))) 961 ;; If no allowed values for BEAMER_env have been defined, 962 ;; supply all defined environments 963 (mapcar 'car (append org-beamer-environments-special 964 org-beamer-environments-extra 965 org-beamer-environments-default))) 966 ((and (equal property "BEAMER_col") 967 (not (org-entry-get nil (concat property "_ALL") 'inherit))) 968 ;; If no allowed values for BEAMER_col have been defined, supply 969 ;; some. 970 (split-string org-beamer-column-widths " ")))) 971 972 (add-hook 'org-property-allowed-value-functions 973 'org-beamer-allowed-property-values) 974 975 976 977 ;;; Commands 978 979 ;;;###autoload 980 (defun org-beamer-export-as-latex 981 (&optional async subtreep visible-only body-only ext-plist) 982 "Export current buffer as a Beamer buffer. 983 984 If narrowing is active in the current buffer, only export its 985 narrowed part. 986 987 If a region is active, export that region. 988 989 A non-nil optional argument ASYNC means the process should happen 990 asynchronously. The resulting buffer should be accessible 991 through the `org-export-stack' interface. 992 993 When optional argument SUBTREEP is non-nil, export the sub-tree 994 at point, extracting information from the headline properties 995 first. 996 997 When optional argument VISIBLE-ONLY is non-nil, don't export 998 contents of hidden elements. 999 1000 When optional argument BODY-ONLY is non-nil, only write code 1001 between \"\\begin{document}\" and \"\\end{document}\". 1002 1003 EXT-PLIST, when provided, is a property list with external 1004 parameters overriding Org default settings, but still inferior to 1005 file-local settings. 1006 1007 Export is done in a buffer named \"*Org BEAMER Export*\", which 1008 will be displayed when `org-export-show-temporary-export-buffer' 1009 is non-nil." 1010 (interactive) 1011 (org-export-to-buffer 'beamer "*Org BEAMER Export*" 1012 async subtreep visible-only body-only ext-plist (lambda () (LaTeX-mode)))) 1013 1014 ;;;###autoload 1015 (defun org-beamer-export-to-latex 1016 (&optional async subtreep visible-only body-only ext-plist) 1017 "Export current buffer as a Beamer presentation (tex). 1018 1019 If narrowing is active in the current buffer, only export its 1020 narrowed part. 1021 1022 If a region is active, export that region. 1023 1024 A non-nil optional argument ASYNC means the process should happen 1025 asynchronously. The resulting file should be accessible through 1026 the `org-export-stack' interface. 1027 1028 When optional argument SUBTREEP is non-nil, export the sub-tree 1029 at point, extracting information from the headline properties 1030 first. 1031 1032 When optional argument VISIBLE-ONLY is non-nil, don't export 1033 contents of hidden elements. 1034 1035 When optional argument BODY-ONLY is non-nil, only write code 1036 between \"\\begin{document}\" and \"\\end{document}\". 1037 1038 EXT-PLIST, when provided, is a property list with external 1039 parameters overriding Org default settings, but still inferior to 1040 file-local settings. 1041 1042 Return output file's name." 1043 (interactive) 1044 (let ((file (org-export-output-file-name ".tex" subtreep))) 1045 (org-export-to-file 'beamer file 1046 async subtreep visible-only body-only ext-plist))) 1047 1048 ;;;###autoload 1049 (defun org-beamer-export-to-pdf 1050 (&optional async subtreep visible-only body-only ext-plist) 1051 "Export current buffer as a Beamer presentation (PDF). 1052 1053 If narrowing is active in the current buffer, only export its 1054 narrowed part. 1055 1056 If a region is active, export that region. 1057 1058 A non-nil optional argument ASYNC means the process should happen 1059 asynchronously. The resulting file should be accessible through 1060 the `org-export-stack' interface. 1061 1062 When optional argument SUBTREEP is non-nil, export the sub-tree 1063 at point, extracting information from the headline properties 1064 first. 1065 1066 When optional argument VISIBLE-ONLY is non-nil, don't export 1067 contents of hidden elements. 1068 1069 When optional argument BODY-ONLY is non-nil, only write code 1070 between \"\\begin{document}\" and \"\\end{document}\". 1071 1072 EXT-PLIST, when provided, is a property list with external 1073 parameters overriding Org default settings, but still inferior to 1074 file-local settings. 1075 1076 Return PDF file's name." 1077 (interactive) 1078 (let ((file (org-export-output-file-name ".tex" subtreep))) 1079 (org-export-to-file 'beamer file 1080 async subtreep visible-only body-only ext-plist 1081 #'org-latex-compile))) 1082 1083 ;;;###autoload 1084 (defun org-beamer-select-environment () 1085 "Select the environment to be used by beamer for this entry. 1086 While this uses (for convenience) a tag selection interface, the 1087 result of this command will be that the BEAMER_env *property* of 1088 the entry is set. 1089 1090 In addition to this, the command will also set a tag as a visual 1091 aid, but the tag does not have any semantic meaning." 1092 (interactive) 1093 ;; Make sure `org-beamer-environments-special' has a higher 1094 ;; priority than `org-beamer-environments-extra'. 1095 (let* ((envs (append org-beamer-environments-special 1096 org-beamer-environments-extra 1097 org-beamer-environments-default)) 1098 (org-current-tag-alist 1099 (append '((:startgroup)) 1100 (mapcar (lambda (e) (cons (concat "B_" (car e)) 1101 (string-to-char (nth 1 e)))) 1102 envs) 1103 '((:endgroup)) 1104 '(("BMCOL" . ?|)))) 1105 (org-tag-persistent-alist nil) 1106 (org-use-fast-tag-selection t) 1107 (org-fast-tag-selection-single-key t)) 1108 (org-set-tags-command) 1109 (let ((tags (org-get-tags nil t))) 1110 (cond 1111 ;; For a column, automatically ask for its width. 1112 ((eq org-last-tag-selection-key ?|) 1113 (if (member "BMCOL" tags) 1114 (org-set-property "BEAMER_col" (read-string "Column width: ")) 1115 (org-delete-property "BEAMER_col"))) 1116 ;; For an "againframe" section, automatically ask for reference 1117 ;; to resumed frame and overlay specifications. 1118 ((eq org-last-tag-selection-key ?A) 1119 (if (equal (org-entry-get nil "BEAMER_env") "againframe") 1120 (progn (org-entry-delete nil "BEAMER_env") 1121 (org-entry-delete nil "BEAMER_ref") 1122 (org-entry-delete nil "BEAMER_act")) 1123 (org-entry-put nil "BEAMER_env" "againframe") 1124 (org-set-property 1125 "BEAMER_ref" 1126 (read-string "Frame reference (*Title, #custom-id, id:...): ")) 1127 (org-set-property "BEAMER_act" 1128 (read-string "Overlay specification: ")))) 1129 ((let* ((tags-re (concat "B_" (regexp-opt (mapcar #'car envs) t))) 1130 (env (cl-some (lambda (tag) 1131 (and (string-match tags-re tag) 1132 (match-string 1 tag))) 1133 tags))) 1134 (and env (progn (org-entry-put nil "BEAMER_env" env) t)))) 1135 (t (org-entry-delete nil "BEAMER_env")))))) 1136 1137 ;;;###autoload 1138 (defun org-beamer-publish-to-latex (plist filename pub-dir) 1139 "Publish an Org file to a Beamer presentation (LaTeX). 1140 1141 FILENAME is the filename of the Org file to be published. PLIST 1142 is the property list for the given project. PUB-DIR is the 1143 publishing directory. 1144 1145 Return output file name." 1146 (org-publish-org-to 'beamer filename ".tex" plist pub-dir)) 1147 1148 ;;;###autoload 1149 (defun org-beamer-publish-to-pdf (plist filename pub-dir) 1150 "Publish an Org file to a Beamer presentation (PDF, via LaTeX). 1151 1152 FILENAME is the filename of the Org file to be published. PLIST 1153 is the property list for the given project. PUB-DIR is the 1154 publishing directory. 1155 1156 Return output file name." 1157 ;; Unlike to `org-beamer-publish-to-latex', PDF file is generated in 1158 ;; working directory and then moved to publishing directory. 1159 (org-publish-attachment 1160 plist 1161 ;; Default directory could be anywhere when this function is 1162 ;; called. We ensure it is set to source file directory during 1163 ;; compilation so as to not break links to external documents. 1164 (let ((default-directory (file-name-directory filename))) 1165 (org-latex-compile 1166 (org-publish-org-to 1167 'beamer filename ".tex" plist (file-name-directory filename)))) 1168 pub-dir)) 1169 1170 1171 (provide 'ox-beamer) 1172 1173 ;; Local variables: 1174 ;; generated-autoload-file: "org-loaddefs.el" 1175 ;; End: 1176 1177 ;;; ox-beamer.el ends here