merge #2843 with minor tweaks

edit
Hakim El Hattab 2021-05-05 11:02:02 +02:00
commit 892c752a4b
6 changed files with 76 additions and 56 deletions

View File

@ -458,6 +458,9 @@ Reveal.on( 'customevent', function() {
<script src="plugin/highlight/highlight.js"></script>
<script>
console.time( 'print' )
Reveal.addEventListener( 'pdf-ready', () => console.timeEnd( 'print' ) );
// Also available as an ES module, see:
// https://revealjs.com/initialization/
Reveal.initialize({

2
dist/reveal.esm.js vendored

File diff suppressed because one or more lines are too long

2
dist/reveal.js vendored

File diff suppressed because one or more lines are too long

View File

@ -27,8 +27,6 @@ export default class Backgrounds {
*/
create() {
let printMode = this.Reveal.isPrintingPDF();
// Clear prior backgrounds
this.element.innerHTML = '';
this.element.classList.add( 'no-transition' );
@ -114,9 +112,24 @@ export default class Backgrounds {
*/
sync( slide ) {
let element = slide.slideBackgroundElement,
const element = slide.slideBackgroundElement,
contentElement = slide.slideBackgroundContentElement;
const data = {
background: slide.getAttribute( 'data-background' ),
backgroundSize: slide.getAttribute( 'data-background-size' ),
backgroundImage: slide.getAttribute( 'data-background-image' ),
backgroundVideo: slide.getAttribute( 'data-background-video' ),
backgroundIframe: slide.getAttribute( 'data-background-iframe' ),
backgroundColor: slide.getAttribute( 'data-background-color' ),
backgroundRepeat: slide.getAttribute( 'data-background-repeat' ),
backgroundPosition: slide.getAttribute( 'data-background-position' ),
backgroundTransition: slide.getAttribute( 'data-background-transition' ),
backgroundOpacity: slide.getAttribute( 'data-background-opacity' ),
};
const dataPreload = slide.hasAttribute( 'data-preload' );
// Reset the prior background state in case this is not the
// initial sync
slide.classList.remove( 'has-dark-background' );
@ -135,19 +148,6 @@ export default class Backgrounds {
contentElement.style.opacity = '';
contentElement.innerHTML = '';
let data = {
background: slide.getAttribute( 'data-background' ),
backgroundSize: slide.getAttribute( 'data-background-size' ),
backgroundImage: slide.getAttribute( 'data-background-image' ),
backgroundVideo: slide.getAttribute( 'data-background-video' ),
backgroundIframe: slide.getAttribute( 'data-background-iframe' ),
backgroundColor: slide.getAttribute( 'data-background-color' ),
backgroundRepeat: slide.getAttribute( 'data-background-repeat' ),
backgroundPosition: slide.getAttribute( 'data-background-position' ),
backgroundTransition: slide.getAttribute( 'data-background-transition' ),
backgroundOpacity: slide.getAttribute( 'data-background-opacity' )
};
if( data.background ) {
// Auto-wrap image urls in url(...)
if( /^(http|file|\/\/)/gi.test( data.background ) || /\.(svg|png|jpg|jpeg|gif|bmp)([?#\s]|$)/gi.test( data.background ) ) {
@ -179,7 +179,7 @@ export default class Backgrounds {
if( data.backgroundColor ) element.style.backgroundColor = data.backgroundColor;
if( data.backgroundTransition ) element.setAttribute( 'data-background-transition', data.backgroundTransition );
if( slide.hasAttribute( 'data-preload' ) ) element.setAttribute( 'data-preload', '' );
if( dataPreload ) element.setAttribute( 'data-preload', '' );
// Background image options are set on the content wrapper
if( data.backgroundSize ) contentElement.style.backgroundSize = data.backgroundSize;
@ -201,7 +201,7 @@ export default class Backgrounds {
}
if( contrastColor ) {
let rgb = colorToRgb( contrastColor );
const rgb = colorToRgb( contrastColor );
// Ignore fully transparent backgrounds. Some browsers return
// rgba(0,0,0,0) when reading the computed background color of
@ -394,4 +394,4 @@ export default class Backgrounds {
}
}
}

View File

@ -16,20 +16,26 @@ export default class Print {
* Configures the presentation for printing to a static
* PDF.
*/
setupPDF() {
async setupPDF() {
let config = this.Reveal.getConfig();
const config = this.Reveal.getConfig();
const slides = queryAll( this.Reveal.getRevealElement(), SLIDES_SELECTOR )
let slideSize = this.Reveal.getComputedSlideSize( window.innerWidth, window.innerHeight );
// Compute slide numbers now, before we start duplicating slides
const doingSlideNumbers = config.slideNumber && /all|print/i.test( config.showSlideNumber );
const slideSize = this.Reveal.getComputedSlideSize( window.innerWidth, window.innerHeight );
// Dimensions of the PDF pages
let pageWidth = Math.floor( slideSize.width * ( 1 + config.margin ) ),
const pageWidth = Math.floor( slideSize.width * ( 1 + config.margin ) ),
pageHeight = Math.floor( slideSize.height * ( 1 + config.margin ) );
// Dimensions of slides within the pages
let slideWidth = slideSize.width,
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;}' );
@ -41,29 +47,32 @@ export default class Print {
document.body.style.height = pageHeight + 'px';
// Make sure stretch elements fit on slide
await new Promise( requestAnimationFrame );
this.Reveal.layoutSlideContents( slideWidth, slideHeight );
// Compute slide numbers now, before we start duplicating slides
let doingSlideNumbers = config.slideNumber && /all|print/i.test( config.showSlideNumber );
queryAll( this.Reveal.getRevealElement(), SLIDES_SELECTOR ).forEach( function( slide ) {
slide.setAttribute( 'data-slide-number', this.Reveal.slideNumber.getSlideNumber( slide ) );
}, this );
// Re-run the slide layout so that r-fit-text is applied based on
// the printed slide size
slides.forEach( slide => this.Reveal.slideContent.layout( slide ) );
// 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;
// Slide and slide background layout
queryAll( this.Reveal.getRevealElement(), SLIDES_SELECTOR ).forEach( function( slide ) {
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,
top = ( pageHeight - slideHeight ) / 2;
let left = ( pageWidth - slideWidth ) / 2;
let top = ( pageHeight - slideHeight ) / 2;
// Re-run the slide layout so that r-fit-text is applied based on
// the printed slide size
this.Reveal.slideContent.layout( slide );
let contentHeight = slide.scrollHeight;
const contentHeight = slideScrollHeights[ index ];
let numberOfPages = Math.max( Math.ceil( contentHeight / pageHeight ), 1 );
// Adhere to configured pages per slide limit
@ -76,10 +85,11 @@ export default class Print {
// Wrap the slide in a page element and hide its overflow
// so that no page ever flows onto another
let page = document.createElement( 'div' );
const page = document.createElement( 'div' );
pages.push( page );
page.className = 'pdf-page';
page.style.height = ( ( pageHeight + config.pdfPageHeightOffset ) * numberOfPages ) + 'px';
slide.parentNode.insertBefore( page, slide );
page.appendChild( slide );
// Position the slide inside of the page
@ -95,19 +105,19 @@ export default class Print {
if( config.showNotes ) {
// Are there notes for this slide?
let notes = this.Reveal.getSlideNotes( slide );
const notes = this.Reveal.getSlideNotes( slide );
if( notes ) {
let notesSpacing = 8;
let notesLayout = typeof config.showNotes === 'string' ? config.showNotes : 'inline';
let notesElement = document.createElement( 'div' );
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' ) {
page.parentNode.insertBefore( notesElement, page.nextSibling );
pages.push( notesElement );
}
else {
notesElement.style.left = notesSpacing + 'px';
@ -122,10 +132,11 @@ export default class Print {
// Inject slide numbers if `slideNumbers` are enabled
if( doingSlideNumbers ) {
let numberElement = document.createElement( 'div' );
const slideNumber = index + 1;
const numberElement = document.createElement( 'div' );
numberElement.classList.add( 'slide-number' );
numberElement.classList.add( 'slide-number-pdf' );
numberElement.innerHTML = slide.getAttribute( 'data-slide-number' );
numberElement.innerHTML = slideNumber;
page.appendChild( numberElement );
}
@ -135,10 +146,9 @@ export default class Print {
// 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.
let fragmentGroups = this.Reveal.fragments.sort( page.querySelectorAll( '.fragment' ), true );
const fragmentGroups = this.Reveal.fragments.sort( page.querySelectorAll( '.fragment' ), true );
let previousFragmentStep;
let previousPage;
fragmentGroups.forEach( function( fragments ) {
@ -155,11 +165,10 @@ export default class Print {
}, this );
// Create a separate page for the current fragment state
let clonedPage = page.cloneNode( true );
page.parentNode.insertBefore( clonedPage, ( previousPage || page ).nextSibling );
const clonedPage = page.cloneNode( true );
pages.push( clonedPage );
previousFragmentStep = fragments;
previousPage = clonedPage;
}, this );
@ -182,6 +191,10 @@ export default class Print {
}, this );
await new Promise( requestAnimationFrame );
pages.forEach( page => pageContainer.appendChild( page ) );
// Notify subscribers that the PDF layout is good to go
this.Reveal.dispatchEvent({ type: 'pdf-ready' });
@ -196,4 +209,4 @@ export default class Print {
}
}
}

View File

@ -1335,7 +1335,11 @@ export default function( revealElement, options ) {
}
// Announce the current slide contents to screen readers
announceStatus( getStatusText( currentSlide ) );
// Use animation frame to prevent getComputedStyle in getStatusText
// from triggering layout mid-frame
requestAnimationFrame( () => {
announceStatus( getStatusText( currentSlide ) );
});
progress.update();
controls.update();
@ -2290,7 +2294,7 @@ export default function( revealElement, options ) {
// When looping is enabled `routes.down` is always available
// so we need a separate check for when we've reached the
// end of a stack and should move horizontally
if( routes.down && routes.right && config.loop && isLastVerticalSlide( currentSlide ) ) {
if( routes.down && routes.right && config.loop && isLastVerticalSlide() ) {
routes.down = false;
}