gnab / remark

A simple, in-browser, markdown-driven slideshow tool.
http://remarkjs.com
MIT License
12.74k stars 858 forks source link

Hide incremental slides #478

Open trombonehero opened 7 years ago

trombonehero commented 7 years ago

Thanks very much for remark.js: I use it extensively when preparing lecture material for courses that I teach (see, e.g., http://www.engr.mun.ca/~anderson/teaching/3891/lecture).

I like to use incremental builds in the classroom, but when I distribute PDF versions of the lecture notes (slides + presenter notes with CSS to turn key words into blank spaces for students to fill in) I don't want to show all of the incremental steps. My current workflow requires me to manually go through the PDF output from Chrome's "Print to PDF" and delete all of the intermediate pages. This is a labour-intensive, slightly error-prone process. It would be really handy if I could run remark in a mode that only showed the final version of each complete (non-incremental) slide.

benjie commented 6 years ago

I've just hacked out this workaround, sharing in case it's useful to anyone else:

function hidePrintSlides(slideshow) {
  const allSlides = slideshow.getSlides();
  let lastSlide;
  let currentSlide;
  const slidesToHide = [];
  const slidesEl = document.getElementsByClassName("remark-slides-area")[0];
  const slideEls = slidesEl.children;
  for (let i = 0; i < allSlides.length; i++) {
    lastSlide = currentSlide;
    currentSlide = allSlides[i];
    if (lastSlide && (
      String(lastSlide.properties.continued) === "true"
      ||
      String(currentSlide.properties.count) === "false"
    )) {
      const slideToHideIndex = i - 1;
      slidesToHide.push(slideToHideIndex);
      slideEls[slideToHideIndex].className = slideEls[slideToHideIndex].className + ' has-continuation';
    }
  }
}

Basically call this function (passing the slideshow instance) and it'll automatically add has-continuation to slides that are followed by another slide that doesn't increment the slide number (i.e. where count is set false, or where they use a continuation --). Assumes that the option countIncrementalSlides is set to false.

Then you can add a print stylesheet to your CSS to remove the slides:

@media print {
  .has-continuation {
    display: none;
  }
}
ulysses4ever commented 6 years ago

@benjie thanks for looking at it! I tried your recipe, but it didn't help in my case (the presentation is here). Do you actually have a "minimal working example" somewhere?

benjie commented 6 years ago

Make sure it fires after the DOM is ready. I don't have a minimal example right now - sorry 😞

nuest commented 6 years ago

Would be great to have this feature somehow built in.

For the next reader, I took a different approach and added a little R snippet to my presentation to create a copy of the file, use grep to remove all -- lines, and then create the PDF from that (and clean up), see https://zivgitlab.uni-muenster.de/d_nues01/git-digital-humanities/blob/master/git-digital-humanities.Rmd#L15

library("webshot")
system("cat presentation.Rmd | grep -v '^--$' > print.Rmd")
rmarkdown::render('print.Rmd', 'xaringan::moon_reader')
file_name <- paste0("file://", normalizePath("print.html"))
webshot(file_name, "git-digital-humanities.pdf")
system("rm -r print.Rmd print.html print_files")
DavidAntliff commented 6 years ago

Looks like there might have been a change to the way the continued property is applied to slides since @benjie posted his code. In particular, it looks like continued is "false" for the first slide of each "full" slide (perhaps it used to be the last one?). This leads to changing:

String(lastSlide.properties.continued) === "true"

to

String(currentSlide.properties.continued) === "true"

Anyway, the following full example seems to work for me with this change:

<!DOCTYPE html>
<html>
  <head>
    <title>Print Full Slides</title>
    <meta charset="utf-8">
    <style>
      @import url(https://fonts.googleapis.com/css?family=Yanone+Kaffeesatz);
      @import url(https://fonts.googleapis.com/css?family=Droid+Serif:400,700,400italic);
      @import url(https://fonts.googleapis.com/css?family=Ubuntu+Mono:400,700,400italic);

      body { font-family: 'Droid Serif'; }
      h1, h2, h3 {
        font-family: 'Yanone Kaffeesatz';
        font-weight: normal;
      }
      code {
        background: #e7e8e2;
        border-radius: 5px;
      }
      @media print {
          .has-continuation {
              display: none;
          }
      }
      .remark-code, .remark-inline-code { font-family: 'Ubuntu Mono'; }
    </style>
  </head>
  <body>
    <textarea id="source">

class: center, middle

# Remark Issue - Continuation

---
# Agenda
--

1. Topic 1
--

1. Topic 2
--

1. Topic 3
--

1. Topic 4
--

1. Topic 5

---
# Topic 1
--

Some text

--

More text

---
# Topic 2

Single slide text

---
# Topic 3
--

Some text

--

More text

---
# Topic 4

Single slide text

---

# Topic 5

--

Some text

--

More text

    </textarea>
    <script src="https://gnab.github.io/remark/downloads/remark-latest.min.js">
    </script>
    <script>
        // https://github.com/gnab/remark/issues/478#issuecomment-383377112
        function hidePrintSlides(slideshow) {
          const allSlides = slideshow.getSlides();
          let lastSlide;
          let currentSlide;
          const slidesToHide = [];
          const slidesEl = document.getElementsByClassName("remark-slides-area")[0];
          const slideEls = slidesEl.children;
          for (let i = 0; i < allSlides.length; i++) {
            lastSlide = currentSlide;
            currentSlide = allSlides[i];
            if (lastSlide && (
              String(currentSlide.properties.continued) === "true"
              ||
              String(currentSlide.properties.count) === "false"
            )) {
              const slideToHideIndex = i - 1;
              slidesToHide.push(slideToHideIndex);
              slideEls[slideToHideIndex].className = slideEls[slideToHideIndex].className + ' has-continuation';
            }
          }
        }
    </script>
    <script>
      remark.macros.scale = function (percentage) {
          var url = this;
          return '<img src="' + url + '" style="width: ' + percentage + '" />';
      };
      var slideshow = remark.create({
          highlightLanguage: 'python',
          highlightStyle: 'solarized-dark',
          highlightLines: false,
          countIncrementalSlides: false  // needed by hidePrintSlides()
      });
      hidePrintSlides(slideshow);
    </script>
  </body>
</html>

I haven't tested it extensively, but the only quirk I've noticed so far is that you get an extra blank page at the end of the PDF.

I hope that's useful.

ulysses4ever commented 6 years ago

@DavidAntliff thanks for your instructions! Unfortunately, they don't work for me as expected. I have

Chromium
Version 69.0.3497.81 (Official Build) Built on Ubuntu , running on Ubuntu 18.04 (64-bit)

And it prints all the overlayed pages. Also, the size is wrong. Print Full Slides.pdf

DavidAntliff commented 6 years ago

@ulysses4ever strange, I am only able to test on Ubuntu 16.04/Chrome 69 and Mac OSX 10.12.6/Chrome 68, but in both cases it seems to work for me.

Print.Full.Slides-DA.pdf

Only difference seems to be that on my Mac I don't get a blank page at the end.

The size is another issue and might be helped by https://github.com/gnab/remark/issues/50#issuecomment-223887379

yihui commented 5 years ago

I think @DavidAntliff was correct: @benjie's original code contained a little bug.

Here is my "old-school" JS code, which also tries to inject the CSS to <head> (I also found the variables slidesToHide and lastSlide unnecessary):

(function(d) {
  var el = d.getElementsByClassName("remark-slides-area");
  if (!el) return;
  var slide, slides = slideshow.getSlides(), els = el[0].children;
  for (var i = 1; i < slides.length; i++) {
    slide = slides[i];
    if (slide.properties.continued === "true" || slide.properties.count === "false") {
      els[i - 1].className += ' has-continuation';
    }
  }
  var s = d.createElement("style");
  s.type = "text/css";
  s.innerHTML = "@media print { .has-continuation { display: none; } }";
  d.head.appendChild(s);
})(document);

Test: https://slides.yihui.name/xaringan/

andreymoser commented 3 years ago

I used this workaround: sed '/^--$/d' original.md > no_incremental_slides.md

abelards commented 3 years ago

Not sure why this is still open with two workarounds listed here. Should we close the issue?

yihui commented 3 years ago

With remark.js 0.15.0, you no longer need the workarounds, because 295155567e652bfceb83f4356c556a27f52506b0 introduced a CSS class to incremental slides, so you can define CSS rules to hide these slides easily. However, the current "latest" release of remark.js is still 0.14.1 because 0.15.0 also introduced some breaking changes (6e2325d23edbe48178000285dbd8bae6ae95ea21).

ulysses4ever commented 3 years ago

It would be nice to have documentation (for every corresponding release) for this particular use case: when I'm using incremental slides markup (--), how do I convert to PDF so that only the last slide in every series goes to PDF.