slides.py 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. """HTML slide show Exporter class"""
  2. # Copyright (c) Jupyter Development Team.
  3. # Distributed under the terms of the Modified BSD License.
  4. from copy import deepcopy
  5. from warnings import warn
  6. from traitlets import Bool, Unicode, default
  7. from nbconvert.preprocessors.base import Preprocessor
  8. from .html import HTMLExporter
  9. class _RevealMetadataPreprocessor(Preprocessor):
  10. # A custom preprocessor adding convenience metadata to cells
  11. def preprocess(self, nb, resources=None):
  12. nb = deepcopy(nb)
  13. for cell in nb.cells:
  14. # Make sure every cell has a slide_type
  15. try:
  16. slide_type = cell.metadata.get("slideshow", {}).get("slide_type", "-")
  17. except AttributeError:
  18. slide_type = "-"
  19. cell.metadata.slide_type = slide_type
  20. # Find the first visible cell
  21. for index, cell in enumerate(nb.cells):
  22. if cell.metadata.slide_type not in {"notes", "skip"}:
  23. cell.metadata.slide_type = "slide"
  24. cell.metadata.slide_start = True
  25. cell.metadata.subslide_start = True
  26. first_slide_ix = index
  27. break
  28. else:
  29. msg = "All cells are hidden, cannot create slideshow"
  30. raise ValueError(msg)
  31. in_fragment = False
  32. for index, cell in enumerate(nb.cells[first_slide_ix + 1 :], start=(first_slide_ix + 1)):
  33. previous_cell = nb.cells[index - 1]
  34. # Slides are <section> elements in the HTML, subslides (the vertically
  35. # stacked slides) are also <section> elements inside the slides,
  36. # and fragments are <div>s within subslides. Subslide and fragment
  37. # elements can contain content:
  38. # <section>
  39. # <section>
  40. # (content)
  41. # <div class="fragment">(content)</div>
  42. # </section>
  43. # </section>
  44. # Get the slide type. If type is subslide or slide,
  45. # end the last slide/subslide/fragment as applicable.
  46. if cell.metadata.slide_type == "slide":
  47. previous_cell.metadata.slide_end = True
  48. cell.metadata.slide_start = True
  49. if cell.metadata.slide_type in {"subslide", "slide"}:
  50. previous_cell.metadata.fragment_end = in_fragment
  51. previous_cell.metadata.subslide_end = True
  52. cell.metadata.subslide_start = True
  53. in_fragment = False
  54. elif cell.metadata.slide_type == "fragment":
  55. cell.metadata.fragment_start = True
  56. if in_fragment:
  57. previous_cell.metadata.fragment_end = True
  58. else:
  59. in_fragment = True
  60. # The last cell will always be the end of a slide
  61. nb.cells[-1].metadata.fragment_end = in_fragment
  62. nb.cells[-1].metadata.subslide_end = True
  63. nb.cells[-1].metadata.slide_end = True
  64. return nb, resources
  65. class SlidesExporter(HTMLExporter):
  66. """Exports HTML slides with reveal.js"""
  67. # Overrides from HTMLExporter
  68. #################################
  69. export_from_notebook = "Reveal.js slides"
  70. @default("template_name")
  71. def _template_name_default(self):
  72. return "reveal"
  73. @default("file_extension")
  74. def _file_extension_default(self):
  75. return ".slides.html"
  76. @default("template_extension")
  77. def _template_extension_default(self):
  78. return ".html.j2"
  79. # Extra resources
  80. #################################
  81. reveal_url_prefix = Unicode(
  82. help="""The URL prefix for reveal.js (version 3.x).
  83. This defaults to the reveal CDN, but can be any url pointing to a copy
  84. of reveal.js.
  85. For speaker notes to work, this must be a relative path to a local
  86. copy of reveal.js: e.g., "reveal.js".
  87. If a relative path is given, it must be a subdirectory of the
  88. current directory (from which the server is run).
  89. See the usage documentation
  90. (https://nbconvert.readthedocs.io/en/latest/usage.html#reveal-js-html-slideshow)
  91. for more details.
  92. """
  93. ).tag(config=True)
  94. @default("reveal_url_prefix")
  95. def _reveal_url_prefix_default(self):
  96. if "RevealHelpPreprocessor.url_prefix" in self.config:
  97. warn(
  98. "Please update RevealHelpPreprocessor.url_prefix to "
  99. "SlidesExporter.reveal_url_prefix in config files.",
  100. stacklevel=2,
  101. )
  102. return self.config.RevealHelpPreprocessor.url_prefix
  103. return "https://unpkg.com/reveal.js@4.0.2"
  104. reveal_theme = Unicode(
  105. "simple",
  106. help="""
  107. Name of the reveal.js theme to use.
  108. We look for a file with this name under
  109. ``reveal_url_prefix``/css/theme/``reveal_theme``.css.
  110. https://github.com/hakimel/reveal.js/tree/master/css/theme has
  111. list of themes that ship by default with reveal.js.
  112. """,
  113. ).tag(config=True)
  114. reveal_transition = Unicode(
  115. "slide",
  116. help="""
  117. Name of the reveal.js transition to use.
  118. The list of transitions that ships by default with reveal.js are:
  119. none, fade, slide, convex, concave and zoom.
  120. """,
  121. ).tag(config=True)
  122. reveal_scroll = Bool(
  123. False,
  124. help="""
  125. If True, enable scrolling within each slide
  126. """,
  127. ).tag(config=True)
  128. reveal_number = Unicode(
  129. "",
  130. help="""
  131. slide number format (e.g. 'c/t'). Choose from:
  132. 'c': current, 't': total, 'h': horizontal, 'v': vertical
  133. """,
  134. ).tag(config=True)
  135. reveal_width = Unicode(
  136. "",
  137. help="""
  138. width used to determine the aspect ratio of your presentation.
  139. Use the horizontal pixels available on your intended presentation
  140. equipment.
  141. """,
  142. ).tag(config=True)
  143. reveal_height = Unicode(
  144. "",
  145. help="""
  146. height used to determine the aspect ratio of your presentation.
  147. Use the horizontal pixels available on your intended presentation
  148. equipment.
  149. """,
  150. ).tag(config=True)
  151. font_awesome_url = Unicode(
  152. "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.css",
  153. help="""
  154. URL to load font awesome from.
  155. Defaults to loading from cdnjs.
  156. """,
  157. ).tag(config=True)
  158. def _init_resources(self, resources):
  159. resources = super()._init_resources(resources)
  160. if "reveal" not in resources:
  161. resources["reveal"] = {}
  162. resources["reveal"]["url_prefix"] = self.reveal_url_prefix
  163. resources["reveal"]["theme"] = self.reveal_theme
  164. resources["reveal"]["transition"] = self.reveal_transition
  165. resources["reveal"]["scroll"] = self.reveal_scroll
  166. resources["reveal"]["number"] = self.reveal_number
  167. resources["reveal"]["height"] = self.reveal_height
  168. resources["reveal"]["width"] = self.reveal_width
  169. return resources