org-show image
[org-ref.git] / org-show.org
1 #+TITLE: org-show - simple presentations in org-mode
2 #+AUTHOR: John Kitchin
3
4 There are several options for "presenting" from org-mode. Here are the options I found.
5
6 https://github.com/rlister/org-present
7 https://github.com/eschulte/epresent
8
9 https://github.com/yjwen/org-reveal
10 https://github.com/takaxp/org-tree-slide/
11 https://github.com/tucasp/org-presie
12
13 http://orgmode.org/worg/exporters/beamer/tutorial.html
14 http://orgmode.org/worg/org-tutorials/non-beamer-presentations.html#sec-3
15
16 The one I like the best is discussed here: http://sachachua.com/blog/2013/04/how-to-present-using-org-mode-in-emacs/. I like it best because you can edit the slides as you go, execute arbitrary emacs code, and it is still pretty simple. I used Sacha's code as the basis for org-show. There is a lot of similarity.
17
18 elisp:org-show-mode
19
20 elisp:org-show-start-slideshow
21
22 * org-show
23 You can have: 
24
25 1. code run
26 2. split to show slide and full image
27 3. plain text
28
29 ** Title slide                                                        :slide:
30 #+BEGIN_SRC emacs-lisp-slide
31 (org-show-animate '("Welcome to the org-show" "John Kitchin"))
32 #+END_SRC
33
34 ** Presentations in org-mode                                          :slide:
35 This should be easy
36
37 1. Create your org-file. Tag headlines with :slide:
38 2. Enter org-show-mode, press f5. Use PageUp and PageDn to navigate slides
39 3. Go back and forth from the presentation to other files in Emacs or other software.
40 4. Edit yours slides as you go, e.g. to demonstrate something 
41 5. Totally interactive, run code, etc...
42
43 org-show is based on this blog post: http://sachachua.com/blog/2013/04/how-to-present-using-org-mode-in-emacs/
44
45 Thanks Sacha!
46 ** Test out some themes                                               :slide:
47
48 [[elisp:(load-theme 'my)]] [[elisp:(disable-theme 'my)]]
49
50 [[elisp:(load-theme 'adwaita)]] [[elisp:(disable-theme 'adwaita)]]
51
52 [[elisp:(load-theme 'deeper-blue)]] [[elisp:(disable-theme 'deeper-blue)]]
53
54 [[elisp:(load-theme 'light-blue)]] [[elisp:(disable-theme 'light-blue)]]
55
56 [[elisp:(load-theme 'manoj-dark)]] [[elisp:(disable-theme 'manoj-dark)]]
57
58 [[elisp:(load-theme 'misterioso)]] [[elisp:(disable-theme 'misterioso)]]
59
60 [[elisp:(load-theme 'tango)]] [[elisp:(disable-theme 'tango)]]
61
62 [[elisp:(load-theme 'tango-dark)]] [[elisp:(disable-theme 'tango-dark)]]
63
64 [[elisp:(load-theme 'tsdh-dark)]] [[elisp:(disable-theme 'tsdh-dark)]]
65
66 [[elisp:(load-theme 'tsdh-light)]] [[elisp:(disable-theme 'tsdh-light)]]
67
68 [[elisp:(load-theme 'wheatgrass)]] [[elisp:(disable-theme 'wheatgrass)]]
69
70 [[elisp:(load-theme 'whiteboard)]] [[elisp:(disable-theme 'whiteboard)]]
71
72 [[elisp:(load-theme 'wombat)]] [[elisp:(disable-theme 'wombat)]]
73
74 [[elisp:(load-theme 'solarized-light t)]] [[elisp:(disable-theme 'solarized-light)]] 
75
76 [[elisp:(load-theme 'solarized-dark t)]] [[elisp:(disable-theme 'solarized-dark)]]
77
78 [[elisp:(load-theme 'zenburn t)]] [[elisp:(disable-theme 'zenburn)]]
79
80 [[elisp:(load-theme 'anti-zenburn t)]] [[elisp:(disable-theme 'anti-zenburn)]]
81
82 ** Equations                                                          :slide:
83 It should be easy to show equations like this  $\int_0^x \frac{1}{2} \sin x dx = 6$.
84
85 It is. Maybe you prefer equation environments?
86
87 \begin{equation}
88 e^x = 55
89 \end{equation}
90
91 Want to see the equation source? [[elisp:(org-ctrl-c-ctrl-c)][click here]]
92
93 Back to equations: C-c C-x C-l
94 ** Figures                                                            :slide:
95
96 Figures should show up in two panes.
97 The left pane shows the slide. The right pane shows the figure, scaled to fit in the window.
98
99 Here is a little screen capture:
100 [[./taskbar.png]]
101
102 ** Need a more complicated layout?                                    :slide:
103 Write some code to generate it, and put it in an emacs-lisp-slide block. org-show will run it and show you the result
104
105 #+BEGIN_SRC emacs-lisp
106 (delete-other-windows)
107 (split-window-right)
108 (other-window 1)
109 (find-file "taskbar.png")
110 (split-window-below)
111 (other-window 1)
112 (find-file "doi-utils.org")
113 #+END_SRC
114
115 #+RESULTS:
116 : #<buffer doi-utils.org>
117
118 #+BEGIN_EXAMPLE
119 ,#+BEGIN_SRC emacs-lisp-slide
120 (delete-other-windows)
121 (split-window-right)
122 (other-window 1)
123 (find-file "taskbar.png")
124 (split-window-below)
125 (other-window 1)
126 (find-file "doi-utils.org")
127 ,#+END_SRC
128 #+END_EXAMPLE
129
130 ** A complicated layout                                               :slide:
131 #+BEGIN_SRC emacs-lisp-slide
132 (delete-other-windows)
133 (split-window-right)
134 (other-window 1)
135 (find-file "taskbar.png")
136 (split-window-below)
137 (other-window 1)
138 (find-file "doi-utils.org")
139 #+END_SRC
140 ** Code blocks should be runnable and editable                        :slide:
141
142 #+BEGIN_SRC python
143 print 6 + 62
144 #+END_SRC
145
146
147 They are.
148 ** We can use many languages                                          :slide:
149 (of course, you must have them installed on your computer)
150
151 #+BEGIN_SRC emacs-lisp
152 (+ 6 6)
153 #+END_SRC
154
155
156 #+BEGIN_SRC R 
157 sum(c(6, 6))
158 #+END_SRC
159
160
161
162 #+BEGIN_SRC perl :results output
163 print 6 + 6
164 #+END_SRC
165
166
167 #+BEGIN_SRC ruby
168 print 6 + 6
169 #+END_SRC
170
171 #+RESULTS:
172
173
174 ** Interactivity is important   
175 We get it.
176 *** Snake                                                             :slide:
177 #+BEGIN_SRC emacs-lisp-slide
178 (snake)
179 #+END_SRC
180
181 *** tetris                                                            :slide:
182
183 #+BEGIN_SRC emacs-lisp-slide
184 (when (and (boundp 'snake-buffer-name) (get-buffer snake-buffer-name))
185   (kill-buffer snake-buffer-name))
186 (delete-other-windows)
187 (tetris)
188 #+END_SRC
189
190
191 *** doctor                                                            :slide:
192 #+BEGIN_SRC emacs-lisp-slide
193 (doctor)
194 #+END_SRC
195
196
197 *** Become a graffiti artist                                          :slide:
198 #+BEGIN_SRC emacs-lisp-slide
199 (progn
200   (switch-to-buffer (get-buffer-create "*artist*"))
201   (erase-buffer)
202   (artist-mode 1)
203   (menu-bar-mode 1)
204   (text-scale-set 0)
205   (artist-select-op-spray-can))
206 #+END_SRC
207
208
209 *** Or draw lines                                                     :slide:
210 #+BEGIN_SRC emacs-lisp-slide
211 (progn
212   (switch-to-buffer (get-buffer-create "*artist*"))
213   (artist-select-op-line))
214 #+END_SRC
215
216 ** No seriously, we can do real work!                                 :slide:
217    :PROPERTIES:
218    :CUSTOM_ID: sec:data-tab-code
219    :END:
220
221 Use this table as a data source.
222 #+tblname: tab-data
223 | x |  y |
224 |---+----|
225 | 1 |  1 |
226 | 2 |  4 |
227 | 3 |  9 |
228 | 4 | 16 |
229
230 #+BEGIN_SRC python :var data=tab-data
231 import matplotlib.pyplot as plt
232 import numpy as np
233 d = np.array(data)
234 plt.plot(d[:,0], d[:,1])
235 plt.show()
236 #+END_SRC  
237
238 #+RESULTS:
239
240 You can make links to a table like this: ref:tab-data.
241 ** Interactive links                                                  :slide:
242 <<beginning>>
243
244 You can have links that take you to places: [[beginning]], [[end]], to a [[#sec:data-tab-code][section]],
245
246 Or links that are functional: cite:mehta-2014-ident-poten. 
247
248 Or that run code [[elisp:(message "Hello %s" user-full-name)]]
249
250 Or links to info: [[info:org#External%20links][info:org#External links]]
251
252 Or to open a [[http://kitchingroup.cheme.cmu.edu][website]].
253
254 <<end>>
255 ** Conclusions                                                        :slide:
256 That is the end!
257
258 #+BEGIN_SRC emacs-lisp-slide
259 (org-show-animate '("That's the end of the org-show." "Thank you for your attention!" "http://github.com/jkitchin/jmax"))
260 #+END_SRC
261
262
263 * The code
264
265 ** The header
266
267 #+BEGIN_SRC emacs-lisp :tangle org-show.el
268 ;;; org-show.el --- Summary
269 ;; Copyright(C) 2014 John Kitchin
270
271 ;; Author: John Kitchin <jkitchin@andrew.cmu.edu>
272 ;; Contributions from Sacha Chua.
273 ;; This file is not currently part of GNU Emacs.
274
275 ;; This program is free software; you can redistribute it and/or
276 ;; modify it under the terms of the GNU General Public License as
277 ;; published by the Free Software Foundation; either version 2, or (at
278 ;; your option) any later version.
279
280 ;; This program is distributed in the hope that it will be useful, but
281 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
282 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
283 ;; General Public License for more details.
284
285 ;; You should have received a copy of the GNU General Public License
286 ;; along with this program ; see the file COPYING.  If not, write to
287 ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
288 ;; Boston, MA 02111-1307, USA.
289
290 ;;; Commentary:
291 ;; A simple mode for presenting org-files as slide-shows
292 #+END_SRC
293
294 ** Some basic setup
295 #+BEGIN_SRC emacs-lisp :tangle org-show.el
296 (defvar org-show-presentation-file nil "File containing the presentation.")
297
298 (defvar org-show-slide-tag "slide" "Tag that marks slides.")
299
300 (defvar org-show-slide-tag-regexp (concat ":" (regexp-quote org-show-slide-tag) ":"))
301
302 (defvar org-show-latex-scale 4.0 "scale for latex preview")
303
304 (defvar org-show-original-latex-scale
305   (plist-get org-format-latex-options :scale)
306   "Original scale for latex preview, so we can reset it.")
307
308 (defvar org-show-text-scale 4 "scale for text in presentation")
309
310 (defvar org-show-current-slide-number 1 "holds current slide number")
311
312 (defvar org-show-mogrify-p (executable-find "mogrify"))
313
314 (defvar org-show-tags-column -60 "column position to move tags to in slide mode")
315
316 (defvar org-show-original-tags-column org-tags-column "Save value so we can change back to it")
317
318 (defvar *org-show-flyspell-mode* (when (boundp flyspell-mode)
319                                    (flyspell-mode))
320   "whether flyspell mode is enabled at beginning of show")
321
322 (defvar *org-show-running* nil
323   "Flag for if the show is running")
324
325 (when org-show-mogrify-p (ignore-errors (require 'eimp)))
326 #+END_SRC
327
328 ** Make a minor mode and menu
329
330 #+BEGIN_SRC emacs-lisp :tangle org-show.el
331 (require 'easymenu)
332
333 (defvar org-show-mode-map
334   (let ((map (make-sparse-keymap)))
335     (define-key map [next] 'org-show-next-slide)
336     (define-key map [prior] 'org-show-previous-slide)
337     
338     (define-key map [f5] 'org-show-start-slideshow)
339     (define-key map [f6] 'org-show-execute-slide)
340     (define-key map (kbd "C--") 'org-show-decrease-text-size)
341     (define-key map (kbd "C-=") 'org-show-increase-text-size)
342     (define-key map (kbd "\e\eg") 'org-show-goto-slide)
343     (define-key map (kbd "\e\et") 'org-show-toc)
344     (define-key map (kbd "\e\eq") 'org-show-stop-slideshow)
345     map)
346   "Keymap for org-show-mode.")
347
348 (easy-menu-define my-menu org-show-mode-map "My own menu"
349   '("org-show"
350     ["Start slide show" org-show-start-slideshow t]
351     ["Next slide" org-show-next-slide t]
352     ["Previous slide" org-show-previous-slide t]
353     ["Open this slide" org-show-open-slide t]
354     ["Goto slide" org-show-goto-slide t]
355     ["Table of contents" org-show-toc t]
356     ["Stop slide show"  org-show-stop-slideshow t]
357 ))
358
359
360 (define-minor-mode org-show-mode
361   "Minor mode for org-show
362
363 \\{org-show-mode-map}"
364   :lighter " org-show"
365   :global t
366   :keymap org-show-mode-map
367
368   (if org-show-mode
369       ;; we are in org-show mode
370       (progn
371         ;; turn off flyspell mode
372         (if flyspell-mode
373             (progn
374               (setq *org-show-flyspell-mode* t)
375               (flyspell-mode-off))
376           (setq *org-show-flyspell-mode* nil)))
377     ;; we are leaving flyspell mode
378     (when  *org-show-flyspell-mode*
379       (flyspell-mode-on))
380     
381     ;; close the show.
382     (when *org-show-running*
383        (org-show-stop-slideshow))))
384 #+END_SRC
385
386 ** Prepare and show the slide 
387
388 #+BEGIN_SRC emacs-lisp :tangle org-show.el
389
390 (defvar org-show-temp-images '() "list of temporary images")
391
392 (defun org-show-execute-slide ()
393   "Process slide at point.
394   If it contains an Emacs Lisp source block, evaluate it.
395   If it contains an image, view it in a split buffer
396   Else, focus on that buffer.
397   Hide all drawers."
398   (interactive)
399   (setq org-show-presentation-file (expand-file-name (buffer-name)))
400   (delete-other-windows)  
401
402   ;; make sure nothing is folded. This seems to be necessary to
403   ;; prevent an error on narrowing then trying to make latex fragments
404   ;; I think.
405   (org-cycle '(64))
406
407   (org-narrow-to-subtree)
408   (visual-line-mode 1)
409   (let ((heading-text (nth 4 (org-heading-components)))
410         (org-format-latex-options (plist-put org-format-latex-options :scale org-show-latex-scale)))
411
412     (set-frame-name (format "%-180s%15s%s" heading-text "slide " (cdr (assoc heading-text org-show-slide-titles))))
413
414     ;; preview equations in the current subtree
415     (org-preview-latex-fragment '(4))
416     (message "") ; clear minibuffer
417     (cond
418
419      ;; view images if there is one. WE only do this this for the first one.
420      ((and (goto-char (point-min))
421            (re-search-forward "\\[\\[\\(.*\\.\\(jpg\\|gif\\|png\\)\\)" nil t))
422       
423       (unless (file-exists-p "org-show-images")
424         (make-directory "org-show-images"))
425       
426       (let* ((png-file (match-string 1))
427              (temp-png (expand-file-name (concat "org-show-images/" (secure-hash 'sha1
428                                             (with-temp-buffer
429                                               (insert-file-contents png-file)
430                                               (buffer-string))) ".png"))))
431
432         (add-to-list 'org-show-temp-images temp-png)
433         (unless (file-exists-p temp-png)
434           (copy-file png-file temp-png t))
435       
436         (split-window-right)      
437       
438         (other-window 1)
439         (find-file temp-png)
440         (when org-show-mogrify-p
441           (eimp-fit-image-width-to-window nil)))
442                   
443       (other-window 1) ; back to slide
444       (goto-char (point-min))
445       (text-scale-set org-show-text-scale)
446       (org-remove-inline-images)
447       (org-cycle-hide-drawers t)
448       (org-show-subtree))
449
450      ;; find and execute source code blocks.
451      ;; you can either have images, or code. Not both.
452      ;; Only code blocks of type emacs-lisp-slide are used.
453      ((and (goto-char (point-min))
454            (re-search-forward "#\\+begin_src emacs-lisp-slide" nil t))
455       (let ((info (org-babel-get-src-block-info)))
456         (unwind-protect
457             (eval (read (concat "(progn " (nth 1 info) ")"))))))
458
459      ;; plain text slides
460      (t
461       (switch-to-buffer (current-buffer))
462       (text-scale-set org-show-text-scale)
463       (org-show-subtree)
464       (org-cycle-hide-drawers t)
465       (org-display-inline-images)
466       (delete-other-windows)))))
467 #+END_SRC
468
469 ** Next and previous slides
470
471 #+BEGIN_SRC emacs-lisp :tangle org-show.el
472 (defun org-show-next-slide ()
473   "Goto next slide in presentation"
474   (interactive)
475   (find-file org-show-presentation-file)
476   (widen)
477   (if (<= (+ org-show-current-slide-number 1) (length org-show-slide-titles))
478       (progn
479         (setq org-show-current-slide-number (+ org-show-current-slide-number 1))
480         (org-show-goto-slide org-show-current-slide-number))
481     (org-show-goto-slide org-show-current-slide-number)
482     (message "This is the end. My only friend the end.  Jim Morrison.")))
483 #+END_SRC
484
485 #+BEGIN_SRC emacs-lisp :tangle org-show.el
486 (defun org-show-previous-slide ()
487   "Goto previous slide in the list"
488   (interactive)
489   (find-file org-show-presentation-file)
490   (widen)
491   (if (> (- org-show-current-slide-number 1) 0)
492       (progn
493         (setq org-show-current-slide-number (- org-show-current-slide-number 1))
494         (org-show-goto-slide org-show-current-slide-number))
495     (org-show-goto-slide org-show-current-slide-number)
496     (message "Once upon a time...")))
497 #+END_SRC
498
499 ** Open this slide
500
501 #+BEGIN_SRC emacs-lisp :tangle org-show.el
502 (defun org-show-open-slide ()
503  "Start show at this slide"
504  (setq org-show-presentation-file (expand-file-name (buffer-name))) 
505  (org-show-initialize)
506  (let ((n (cdr (assoc (nth 4 (org-heading-components)) org-show-slide-titles))))
507    (setq org-show-current-slide-number n)
508    (org-show-goto-slide n)))
509 #+END_SRC
510
511 ** Starting the show
512 We need some functions for convenient starting and stopping.
513
514 On starting, we want to map the slides so we can get slide numbers for navigation and to display them on the frame. We also make the slide tags invisible. We set some temporary key bindings. These need to be global because sometimes we navigate out of the slideshow buffer, and we want page up and down to go to the next slides no matter where we are.
515
516
517 #+BEGIN_SRC emacs-lisp :tangle org-show.el
518 (defvar org-show-slide-list '() "List of slide numbers and markers to each slide")
519 (defvar org-show-slide-titles '() "List of titles and slide numbers for each slide")
520
521 (defun org-show-initialize ()
522   ;; make slide lists for future navigation. rerun this if you change slide order
523   (setq  org-show-slide-titles '()
524          org-show-temp-images '()
525          org-show-slide-list '())
526      
527   (let ((n 0))
528     (org-map-entries
529      (lambda ()
530        (when (string-match-p ":slide:" (or (nth 5 (org-heading-components)) ""))
531          (setq n (+ n 1))
532          
533          (add-to-list 'org-show-slide-titles 
534                       (cons (nth 4 (org-heading-components)) n) t)
535
536          (add-to-list 'org-show-slide-list 
537                       (cons n (set-marker (make-marker) (point))) t))))))
538
539 (defun org-show-start-slideshow ()
540   "Start the slide show, at the beginning"
541   (interactive)
542     
543   (setq *org-show-running* t)
544   (setq org-show-presentation-file (expand-file-name (buffer-name)))
545   (beginning-of-buffer)
546   (setq org-tags-column org-show-tags-column)
547   (org-set-tags-command '(4) t)
548
549   (org-show-initialize)
550   ;; hide slide tags
551   (save-excursion
552     (while (re-search-forward ":slide:" nil t)
553       (overlay-put
554        (make-overlay (match-beginning 0)(match-end 0))
555        'invisible 'slide)))
556   (add-to-invisibility-spec 'slide)
557   (beginning-of-buffer)
558   (delete-other-windows)
559   (org-show-mode 1)
560   (setq org-show-current-slide-number 1)
561   (org-show-goto-slide 1))
562 #+END_SRC
563
564 ** Stop the show
565
566 #+BEGIN_SRC emacs-lisp :tangle org-show.el
567 (defun org-show-stop-slideshow ()
568   (interactive)
569   ;; make slide tag visible again
570   (remove-from-invisibility-spec 'slide)
571
572   ;; Redisplay inline images
573   (org-display-inline-images)
574
575   ;; reset latex scale
576   (plist-put org-format-latex-options :scale org-show-original-latex-scale)
577
578   ;; clean up temp images
579   (mapcar (lambda (x)
580             (let ((bname (file-name-nondirectory x)))
581               (when (get-buffer bname)
582                 (set-buffer bname) 
583                 (save-buffer)
584                 (kill-buffer bname)))
585
586             (when (file-exists-p x)
587               (delete-file x)))
588           org-show-temp-images)
589   (setq org-show-temp-images '())
590
591   ;; ;; clean up miscellaneous buffers
592   (when (get-buffer "*Animation*") (kill-buffer "*Animation*"))
593
594   (when org-show-presentation-file (find-file org-show-presentation-file))
595   (widen)
596   (text-scale-set 0)
597   (delete-other-windows)
598   (setq org-show-presentation-file nil)
599   (setq org-show-current-slide-number 1)
600   (set-frame-name (if (buffer-file-name)
601                    (abbreviate-file-name (buffer-file-name))))
602   (setq org-tags-column org-show-original-tags-column)
603   (org-set-tags-command '(4) t)
604   (setq *org-show-running* nil)
605   (org-show-mode -1))
606
607 (defalias 'stop 'org-show-stop-slideshow)
608 #+END_SRC
609
610 ** Goto a slide
611 #+BEGIN_SRC emacs-lisp :tangle org-show.el
612 (defun org-show-goto-slide (n)
613  "Goto slide N"
614  (interactive "nSlide number: ")
615  (message "Going to slide %s" n)
616  (find-file org-show-presentation-file)
617  (setq org-show-current-slide-number n)
618  (widen)
619  (goto-char (cdr (assoc n org-show-slide-list)))
620  (org-show-execute-slide))
621 #+END_SRC
622
623 ** Table of contents
624 #+BEGIN_SRC emacs-lisp :tangle org-show.el
625 (defun org-show-toc ()
626   (interactive)
627   (let ((links) (c-b (buffer-name)) (n))
628     (save-excursion
629       (widen)
630       (mapcar
631        (lambda (x)
632          (setq n (car x))
633          (goto-char (cdr x))
634          (add-to-list
635           'links
636           (format " [[elisp:(progn (switch-to-buffer \"%s\")(goto-char %s)(org-show-execute-slide))][%2s %s]]\n\n"
637                   (marker-buffer (cdr x))
638                   (marker-position (cdr x))
639                   (car x)
640                   (nth 4 (org-heading-components))) t))
641               org-show-slide-list))
642     
643     (switch-to-buffer "*List of Slides*")
644     (org-mode)
645     (erase-buffer)
646     
647     (insert (mapconcat 'identity links ""))
648   
649     ;(setq buffer-read-only t)
650     (use-local-map (copy-keymap org-mode-map))
651     (local-set-key "q" #'(lambda () (interactive) (kill-buffer)))))
652 #+END_SRC
653
654 ** Utilities
655 It seems like we might animate enough to have a function
656 #+BEGIN_SRC emacs-lisp :tangle org-show.el
657 (require 'animate)
658
659 (defun org-show-animate (strings)
660   "Animate STRINGS in an *Animation* buffer"
661   (switch-to-buffer (get-buffer-create
662                      (or animation-buffer-name
663                          "*Animation*")))
664   (erase-buffer)
665   (text-scale-set 6)
666   (let* ((vpos (/ (- 20
667                      1 ;; For the mode-line
668                      (1- (length strings)) 
669                      (length strings))
670                   2))
671          (width 43)
672          hpos)
673     (while strings
674       (setq hpos (/ (- width (length (car strings))) 2))
675       (when (> 0 hpos) (setq hpos 0))
676       (when (> 0 vpos) (setq vpos 0))
677       (animate-string (car strings) vpos hpos)
678       (setq vpos (1+ vpos))
679       (setq strings (cdr strings)))))
680 #+END_SRC
681
682 dynamic rescalling of text size
683
684 #+BEGIN_SRC emacs-lisp :tangle org-show.el
685 (defun org-show-increase-text-size (&optional arg)
686   "Increase text size. Bound to \\[org-show-increase-text-size].
687
688 With prefix ARG, set `org-show-text-scale' so subsquent slides are the same text size."
689   (interactive "P")
690   (text-scale-increase 1.5)
691   (when arg
692     (setq org-show-text-scale (* org-show-text-scale 1.5))))
693
694 (defun org-show-decrease-text-size (&optional arg)
695   "Increase text size. Bound to \\[org-show-decrease-text-size].
696
697 With prefix ARG, set `org-show-text-scale' so subsquent slides are the same text size."
698   (interactive "P")
699   (text-scale-decrease 1.5)
700   (when arg
701     (setq org-show-text-scale (/ org-show-text-scale 1.5)))
702 )
703 #+END_SRC
704
705 ** End
706 #+BEGIN_SRC emacs-lisp  :tangle org-show.el
707 (provide 'org-show)
708 #+END_SRC
709 * build
710 [[elisp:(org-babel-load-file "org-show.org")]]