March, 2015 |
Currently (SVN 8900) exporting documents in beamer style to PDF is not always quite what one would like. Folds, unrolls & co. are all flattened out before export happens, which is great when one wants to distribute a presentation, but no so much if one needs a PDF to actually do the presentation. Also, page numbering does not work and nested switches fail to export correctly. In this short document we set to fix this.
The implementation is easy (though not exactly optimized). We assume
that a buffer is open which contains a
Create a new buffer
with a root node
1. This is needed in order for the slide numbers to be
correctly computed, since the routines doing it rely on
the existence of a
For each child of
Are there any more steps in the current screen?
If not, then stop (and process the next screen), otherwise go to ?.
If we came from
The reason why we go screen by screen is to produce a new document
with a
First of all a couple of handy macros:
(define-public-macro (nnull-with var val . body)
; assign @val to @var and execute @body only if @val is not
a null list
‘(with ,var ,val
(if (nnull? ,var) (begin ,@body) #f)))
Scheme]
(define (list->tree label l)
(tree-insert (tree label) 0 l))
Scheme]
Scheme]
Although not strictly necessary, we prune hidden children of
(define (keep-shown! t)
(if (alternative-context? t)
(map ; Prune hidden children
(lambda (i) (if (tm-func? (tree-ref t i) 'hidden)
(tree-remove! t i 1)))
(reverse (.. 0 (tm-arity t)))))
(if (and (tm-compound? t) (> (tm-arity t) 0))
(for-each keep-shown! (tree-children t))))
Scheme]
Scheme]
How do we know when we are done “clicking”
There isn't much to say here which we hadn't said already. As usual, start reading at the last function.
(define (create-slide scr)
(if (not (tree? scr)) (tree 'slide '(document ""))
; just in case
(with t (tree-copy scr)
(keep-shown! t)
(tree-assign-node! t 'slide)
t)))
Scheme]
(define (process-screen scr)
(cons (create-slide scr)
(begin
(dynamic-traverse-buffer :next)
(if (tm-func? scr 'hidden) '()
(process-screen scr)))))
Scheme]
(define (screens->slides t)
(if (not (tm-func? t 'screens)) (tree 'document
"")
(with f (lambda (scr) (list->tree 'document
(process-screen scr)))
(system-wait "Generating slides"
"please wait")
; Insert fake screen at the end
(tree-insert! t (tree-arity t)
(list (tree 'hidden '(document
""))))
(dynamic-operate-on-buffer :first)
; Notice that we don't process the last (fake) screen
(list->tree 'screens (map f (cDr (tree-children
t)))))))
Scheme]
Scheme]
Recall that we intend to create a new buffer with the slides. In order
to preserve the preamble and other attributes of the original document
we copy all of it, then edit out what we don't want. The copying is
done in
(use-modules (utils library cursor)) ; defines with-buffer
Scheme]
(define (buffer-copy buf)
"Creates a copy of @u and returns the new url."
(with-buffer buf
(let* ((u (buffer-new))
(styles (get-style-list))
(init (get-all-inits))
(body (tree-copy (buffer-get-body buf))))
(view-new u) ; needed by buffer-focus, used in
with-buffer
(buffer-set-body u body)
(with-buffer u
(set-style-list styles)
(init-env "global-title"
(buffer-get-metadata buf "title"))
(init-env "global-author"
(buffer-get-metadata buf "author"))
(init-env "global-subject"
(buffer-get-metadata buf "subject"))
(for-each
(lambda (t)
(if (tree-func? t 'associate)
(with (var val) (list (tree-ref t 0) (tree-ref
t 1))
(init-env-tree (tree->string var) val))))
(tree-children init)))
u)))
Scheme]
(tm-define (create-slides buf)
(nnull-with l (select (buffer->tree buf) '(screens))
(let* ((scrns (car l))
(new-buf (buffer-copy buf))
(saved (tree-copy (buffer-get-body buf)))
(slides (screens->slides scrns))) ; modifies buf
(buffer-set-body buf saved) ; restore original buf
(with t (car (select (buffer->tree new-buf)
'(screens)))
(tree-set! t slides)) ; Replace body with new
content
(switch-to-buffer new-buf)
(init-env "page-medium" "paper")
(add-style-package "slides"))))
Scheme]
Scheme]
Before you can use
<document|<assign|screens-bar|<macro|body|>>|<assign|page-odd-footer|>|<assign|page-even-footer|>|<assign|page-odd-header|>|<assign|page-even-header|>>
Second, we need to skip some hardcoded behaviour. Currently, there's
(define disable-dynamic-make-slides #t)
Scheme]
(tm-define (dynamic-make-slides)
(:require disable-dynamic-make-slides)
(noop))
Scheme]
Scheme]
If you want the old functionality back, just set
There are a few things which might be cool to do:
A table of contents for the generated PDF would be great.
Fine grained control of what environments should be flattened out, if any.
Right now images linked in the original document using relative paths will not display properly in the new buffer unless it is saved in the same folder as the original. We could fix this either including the images or pre-saving the buffer in the right folder.