问题
My aim is write a document in markdown
(rmarkdown
, more specifically), that can be compiled both to a regular PDF (or other) file and a beamer presentation at the same time, from the same source. (Using knitr
.) Scenario: the document includes, in addition to the regular text, one sentence summaries for each paragraph and these should go to the presentation as the bullet points.
I know that I can compile a document to several different output formats at the same time with knitr
, but the problem here is something else: the content of the document. How to include those sentences...? I must mark them somehow, and achieve that they're not compiled into the regular PDF, and at the same time only they should be compiled into the beamer presentation!
What's the solution here?
(I'm planning to do this with bookdown
, but I have the feeling that it doesn't matter.)
回答1:
You could use the beamerarticle
package to build an article class document from the beamer sources. I couldn't convince rmarkdown to build both documents at the same time, but the following works with alternating the header between the lines for the beamer output and the article document and renaming the output file in between:
---
output:
beamer_presentation:
keep_tex: true
# pdf_document:
# includes:
# in_header: preamble.tex
---
sentence in both documents
``` {=latex}
\only<article>{
sentence only in the article
}
```
``` {=latex}
\only<presentation>{
sentence only in the presentation
}
```
With preamble.tex
:
\usepackage{beamerarticle}
Complete rstudio project: https://rstudio.cloud/project/725309
回答2:
I finally managed to put together an approach which is working - I believe - perfectly, although it has some not-really-elegant solutions, and therefore there is very likely (a lot of) room for improvement.
The basic idea is to include margin notes, both in HTML and PDF, and then these margin notes are those that will be displayed in the presentation. So they serve two purpose: a quick summary of the paragraph (somewhat Tufte-style) and the basis for creating the presentation.
The presentation comes only in one format, there is no incremental version, where bullet points appear one-by-one. (Actually, there are no more bullet points at all, texts are simply displayed as sentences in different paragraphs.)
To achieve this, I used the custom block function of bookdown
:
Text
```{block, type="handout"}
Margin note
```
Text
Here are the details:
- When compiling to HTML, the custom block conveniently compiles into a
div
, with appropriatetype
, so all we have to do is the format it in the CSS file:
p {
text-align: justify;
width: 80%;
margin-left: 0;
margin-right: 20%;
}
li:not(.chapter) {
text-align: justify;
width: 80%;
margin-left: 0;
margin-right: 20%;
}
.handout {
float: right;
clear: right;
width: 18%;
margin-top: 0.5rem;
margin-bottom: 1rem;
font-size: 1.1rem;
line-height: 1.3;
vertical-align: baseline;
position: relative;
}
.handout p {
font-size:100%;
line-height:1.3;
-webkit-hyphens: auto;
-ms-hyphens: auto;
hyphens: auto;
}
- When compiling into PDF, we unfortunately get an environment, but we can use the
environ
package to turn it to a single -marginnote
- command:
\NewEnviron{handout}{\marginnote{\footnotesize \BODY}[0.23cm]}
When compiling to
beamer
, things get a bit trickier. I found a similar solution which uses preprocessor. Instead of that, I've chosen to use Pandoc's filters. Luckily, we even have an example that is quite close to what we want! We will use a lua filter, however, a few things should be tuned...First,
documentclass
is set "globally" (inindex.Rmd
) tobook
which won't work with beamer. Thus, in both beamer formats, we have to reset it:
function Meta(m)
if FORMAT=="beamer" then m.documentclass="beamer" end
return m
end
- Next, we should crop out everything, except the marginnotes and the figures. The former is recognized by looking for
RawBlock
type withtex
format which contains the texthandout
, the latter needs some tweaking. Albeit it is a completely unrelated issue, here are the details: the part of the filter that recognizes the Image part should also be modified to work with Rmarkdown generated files:(el.t == "Para" and el.c[1].t == "Image") or
. More importantly, even this won't work if we usefig.align
or something like that as it changes the generated markdown's format from![]()
to direct LaTeX code. So we have to add another condition:(el.t == "RawBlock" and el.format == "tex" and string.match( el.text, "includegraphics" ) ) or
. Overall, here is the second part of the lua filter:
function Pandoc(doc)
if FORMAT=="beamer" then
local hblocks = {}
for i,el in pairs(doc.blocks) do
if (el.t == "Div" and el.classes[1] == "handout") or
(el.t == "BlockQuote") or
(el.t == "RawBlock" and el.format == "tex" and string.match( el.text, "includegraphics" ) ) or
(el.t == "RawBlock" and el.format == "tex" and string.match( el.text, "handout" ) ) or
(el.t == "OrderedList" and el.style == "Example") or
(el.t == "Para" and el.c[1].t == "Image") or
(el.t == "Header") then
table.insert(hblocks, el)
end
end
return pandoc.Pandoc(hblocks, doc.meta)
end
end
- The fact that we don't cut out the
handout
environment means that we have to do something with it, we simply translate it to text:
\NewEnviron{handout}{\BODY}
- We still have to be careful to break long slides. Luckily, there is an
allowframebreaks
option in beamer (which is considered evil, but I think here it is completely justified, or rather, we don't have any better solution); the only problem is that we can't add it to each slide, as we have no direct control on the LaTeX code for the frames. Luckily, there's a solution to modify the option in header to make it default, and we can easily do it inpreamble.tex
. I combine this solution with a more elegant numbering scheme:
\let\oldframe\frame
\renewcommand\frame[1][allowframebreaks]{\oldframe[#1]}
\makeatletter
\defbeamertemplate*{frametitle continuation}{only if multiple}{%
\ifnum \numexpr \beamer@endpageofframe+1-\beamer@startpageofframe\relax > 1
\insertcontinuationcount.%
\fi
}
\makeatother
The most non-elegant part is that we can't include the
beamer_presentation
output to twice, and with different names, or at least I don't know a solution for this so we have to manually compile it withbookdown::render_book
and don't forget to rename (and move) the resulting compiled file afterwards.This also means that we have to give up using the
Build Book
button, unfortunately. We rather have to create a script to do all what the button would (and I hope that I made no mistake, and it is indeed doing the same as the button...):
bookdown::render_book( "index.Rmd", "bookdown::pdf_book" )
bookdown::render_book( "index.Rmd", "bookdown::gitbook" )
bookdown::render_book( "index.Rmd", "bookdown::epub_book" )
bookdown::render_book( "index.Rmd", "beamer_presentation" )
file.rename( "FerenciTamas_ValszamEsStatAlapvonalai.pdf", "./docs/FerenciTamas_ValszamEsStatAlapvonalai_handout.pdf" )
Finally, we also need a custom Pandoc template, as for the presentation it is easily possible that we need a short title (which is not currently supported by Pandoc). So I changed
\title{$title$$if(thanks)$\thanks{$thanks$}$endif$}
to\title[$if(short-title)$$short-title$$endif$]{$title$$if(thanks)$\thanks{$thanks$}$endif$}
(adding ashort-title
element toindex.Rmd
).It's again an unrelated issue, but I also changed the
\frame{\sectionpage}
line in\AtBeginSection
to
\begin{frame}{$toc-title$}
\tableofcontents[currentsection]
\end{frame}
which is of course mostly a matter of taste, but an objective reason is that it works in non-English languages as well (the original template would display "Section 1" even if non-English language is selected).
And that's it!
You can find everything put together, in a completely realized project here: https://github.com/tamas-ferenci/FerenciTamas_ValszamEsStatAlapvonalai.
Of course, I really welcome any feedback, criticism or suggestion on improvement.
来源:https://stackoverflow.com/questions/58885889/embedding-a-beamer-presentation-in-a-regular-document-with-rmarkdown