From d283f7e661e14d6ae1881fe803e5b4f1ed0689ff Mon Sep 17 00:00:00 2001 From: David Thompson Date: Mon, 24 Jun 2024 13:49:08 -0400 Subject: Add 2024 Guix social talk. --- .../reveal.js/js/controllers/printview.js | 239 +++++++++++++++++++++ 1 file changed, 239 insertions(+) create mode 100644 2024-06-18-guix-social/reveal.js/js/controllers/printview.js (limited to '2024-06-18-guix-social/reveal.js/js/controllers/printview.js') diff --git a/2024-06-18-guix-social/reveal.js/js/controllers/printview.js b/2024-06-18-guix-social/reveal.js/js/controllers/printview.js new file mode 100644 index 0000000..b335462 --- /dev/null +++ b/2024-06-18-guix-social/reveal.js/js/controllers/printview.js @@ -0,0 +1,239 @@ +import { SLIDES_SELECTOR } from '../utils/constants.js' +import { queryAll, createStyleSheet } from '../utils/util.js' + +/** + * Setups up our presentation for printing/exporting to PDF. + */ +export default class PrintView { + + constructor( Reveal ) { + + this.Reveal = Reveal; + + } + + /** + * Configures the presentation for printing to a static + * PDF. + */ + async activate() { + + const config = this.Reveal.getConfig(); + const slides = queryAll( this.Reveal.getRevealElement(), SLIDES_SELECTOR ) + + // Compute slide numbers now, before we start duplicating slides + const injectPageNumbers = config.slideNumber && /all|print/i.test( config.showSlideNumber ); + + const slideSize = this.Reveal.getComputedSlideSize( window.innerWidth, window.innerHeight ); + + // Dimensions of the PDF pages + const pageWidth = Math.floor( slideSize.width * ( 1 + config.margin ) ), + pageHeight = Math.floor( slideSize.height * ( 1 + config.margin ) ); + + // Dimensions of slides within the pages + const slideWidth = slideSize.width, + slideHeight = slideSize.height; + + await new Promise( requestAnimationFrame ); + + // Let the browser know what page size we want to print + createStyleSheet( '@page{size:'+ pageWidth +'px '+ pageHeight +'px; margin: 0px;}' ); + + // Limit the size of certain elements to the dimensions of the slide + createStyleSheet( '.reveal section>img, .reveal section>video, .reveal section>iframe{max-width: '+ slideWidth +'px; max-height:'+ slideHeight +'px}' ); + + document.documentElement.classList.add( 'reveal-print', 'print-pdf' ); + document.body.style.width = pageWidth + 'px'; + document.body.style.height = pageHeight + 'px'; + + const viewportElement = this.Reveal.getViewportElement(); + let presentationBackground; + if( viewportElement ) { + const viewportStyles = window.getComputedStyle( viewportElement ); + if( viewportStyles && viewportStyles.background ) { + presentationBackground = viewportStyles.background; + } + } + + // Make sure stretch elements fit on slide + await new Promise( requestAnimationFrame ); + this.Reveal.layoutSlideContents( slideWidth, slideHeight ); + + // Batch scrollHeight access to prevent layout thrashing + await new Promise( requestAnimationFrame ); + + const slideScrollHeights = slides.map( slide => slide.scrollHeight ); + + const pages = []; + const pageContainer = slides[0].parentNode; + let slideNumber = 1; + + // Slide and slide background layout + slides.forEach( function( slide, index ) { + + // Vertical stacks are not centred since their section + // children will be + if( slide.classList.contains( 'stack' ) === false ) { + // Center the slide inside of the page, giving the slide some margin + let left = ( pageWidth - slideWidth ) / 2; + let top = ( pageHeight - slideHeight ) / 2; + + const contentHeight = slideScrollHeights[ index ]; + let numberOfPages = Math.max( Math.ceil( contentHeight / pageHeight ), 1 ); + + // Adhere to configured pages per slide limit + numberOfPages = Math.min( numberOfPages, config.pdfMaxPagesPerSlide ); + + // Center slides vertically + if( numberOfPages === 1 && config.center || slide.classList.contains( 'center' ) ) { + top = Math.max( ( pageHeight - contentHeight ) / 2, 0 ); + } + + // Wrap the slide in a page element and hide its overflow + // so that no page ever flows onto another + const page = document.createElement( 'div' ); + pages.push( page ); + + page.className = 'pdf-page'; + page.style.height = ( ( pageHeight + config.pdfPageHeightOffset ) * numberOfPages ) + 'px'; + + // Copy the presentation-wide background to each individual + // page when printing + if( presentationBackground ) { + page.style.background = presentationBackground; + } + + page.appendChild( slide ); + + // Position the slide inside of the page + slide.style.left = left + 'px'; + slide.style.top = top + 'px'; + slide.style.width = slideWidth + 'px'; + + this.Reveal.slideContent.layout( slide ); + + if( slide.slideBackgroundElement ) { + page.insertBefore( slide.slideBackgroundElement, slide ); + } + + // Inject notes if `showNotes` is enabled + if( config.showNotes ) { + + // Are there notes for this slide? + const notes = this.Reveal.getSlideNotes( slide ); + if( notes ) { + + const notesSpacing = 8; + const notesLayout = typeof config.showNotes === 'string' ? config.showNotes : 'inline'; + const notesElement = document.createElement( 'div' ); + notesElement.classList.add( 'speaker-notes' ); + notesElement.classList.add( 'speaker-notes-pdf' ); + notesElement.setAttribute( 'data-layout', notesLayout ); + notesElement.innerHTML = notes; + + if( notesLayout === 'separate-page' ) { + pages.push( notesElement ); + } + else { + notesElement.style.left = notesSpacing + 'px'; + notesElement.style.bottom = notesSpacing + 'px'; + notesElement.style.width = ( pageWidth - notesSpacing*2 ) + 'px'; + page.appendChild( notesElement ); + } + + } + + } + + // Inject page numbers if `slideNumbers` are enabled + if( injectPageNumbers ) { + const numberElement = document.createElement( 'div' ); + numberElement.classList.add( 'slide-number' ); + numberElement.classList.add( 'slide-number-pdf' ); + numberElement.innerHTML = slideNumber++; + page.appendChild( numberElement ); + } + + // Copy page and show fragments one after another + if( config.pdfSeparateFragments ) { + + // Each fragment 'group' is an array containing one or more + // fragments. Multiple fragments that appear at the same time + // are part of the same group. + const fragmentGroups = this.Reveal.fragments.sort( page.querySelectorAll( '.fragment' ), true ); + + let previousFragmentStep; + + fragmentGroups.forEach( function( fragments, index ) { + + // Remove 'current-fragment' from the previous group + if( previousFragmentStep ) { + previousFragmentStep.forEach( function( fragment ) { + fragment.classList.remove( 'current-fragment' ); + } ); + } + + // Show the fragments for the current index + fragments.forEach( function( fragment ) { + fragment.classList.add( 'visible', 'current-fragment' ); + }, this ); + + // Create a separate page for the current fragment state + const clonedPage = page.cloneNode( true ); + + // Inject unique page numbers for fragments + if( injectPageNumbers ) { + const numberElement = clonedPage.querySelector( '.slide-number-pdf' ); + const fragmentNumber = index + 1; + numberElement.innerHTML += '.' + fragmentNumber; + } + + pages.push( clonedPage ); + + previousFragmentStep = fragments; + + }, this ); + + // Reset the first/original page so that all fragments are hidden + fragmentGroups.forEach( function( fragments ) { + fragments.forEach( function( fragment ) { + fragment.classList.remove( 'visible', 'current-fragment' ); + } ); + } ); + + } + // Show all fragments + else { + queryAll( page, '.fragment:not(.fade-out)' ).forEach( function( fragment ) { + fragment.classList.add( 'visible' ); + } ); + } + + } + + }, this ); + + await new Promise( requestAnimationFrame ); + + pages.forEach( page => pageContainer.appendChild( page ) ); + + // Re-run JS-based content layout after the slide is added to page DOM + this.Reveal.slideContent.layout( this.Reveal.getSlidesElement() ); + + // Notify subscribers that the PDF layout is good to go + this.Reveal.dispatchEvent({ type: 'pdf-ready' }); + + viewportElement.classList.remove( 'loading-scroll-mode' ); + + } + + /** + * Checks if the print mode is/should be activated. + */ + isActive() { + + return this.Reveal.getConfig().view === 'print'; + + } + +} \ No newline at end of file -- cgit v1.2.3