merge #2843 with minor tweaks
commit
892c752a4b
|
@ -458,6 +458,9 @@ Reveal.on( 'customevent', function() {
|
||||||
<script src="plugin/highlight/highlight.js"></script>
|
<script src="plugin/highlight/highlight.js"></script>
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
|
console.time( 'print' )
|
||||||
|
Reveal.addEventListener( 'pdf-ready', () => console.timeEnd( 'print' ) );
|
||||||
|
|
||||||
// Also available as an ES module, see:
|
// Also available as an ES module, see:
|
||||||
// https://revealjs.com/initialization/
|
// https://revealjs.com/initialization/
|
||||||
Reveal.initialize({
|
Reveal.initialize({
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -27,8 +27,6 @@ export default class Backgrounds {
|
||||||
*/
|
*/
|
||||||
create() {
|
create() {
|
||||||
|
|
||||||
let printMode = this.Reveal.isPrintingPDF();
|
|
||||||
|
|
||||||
// Clear prior backgrounds
|
// Clear prior backgrounds
|
||||||
this.element.innerHTML = '';
|
this.element.innerHTML = '';
|
||||||
this.element.classList.add( 'no-transition' );
|
this.element.classList.add( 'no-transition' );
|
||||||
|
@ -114,9 +112,24 @@ export default class Backgrounds {
|
||||||
*/
|
*/
|
||||||
sync( slide ) {
|
sync( slide ) {
|
||||||
|
|
||||||
let element = slide.slideBackgroundElement,
|
const element = slide.slideBackgroundElement,
|
||||||
contentElement = slide.slideBackgroundContentElement;
|
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
|
// Reset the prior background state in case this is not the
|
||||||
// initial sync
|
// initial sync
|
||||||
slide.classList.remove( 'has-dark-background' );
|
slide.classList.remove( 'has-dark-background' );
|
||||||
|
@ -135,19 +148,6 @@ export default class Backgrounds {
|
||||||
contentElement.style.opacity = '';
|
contentElement.style.opacity = '';
|
||||||
contentElement.innerHTML = '';
|
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 ) {
|
if( data.background ) {
|
||||||
// Auto-wrap image urls in url(...)
|
// Auto-wrap image urls in url(...)
|
||||||
if( /^(http|file|\/\/)/gi.test( data.background ) || /\.(svg|png|jpg|jpeg|gif|bmp)([?#\s]|$)/gi.test( data.background ) ) {
|
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.backgroundColor ) element.style.backgroundColor = data.backgroundColor;
|
||||||
if( data.backgroundTransition ) element.setAttribute( 'data-background-transition', data.backgroundTransition );
|
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
|
// Background image options are set on the content wrapper
|
||||||
if( data.backgroundSize ) contentElement.style.backgroundSize = data.backgroundSize;
|
if( data.backgroundSize ) contentElement.style.backgroundSize = data.backgroundSize;
|
||||||
|
@ -201,7 +201,7 @@ export default class Backgrounds {
|
||||||
}
|
}
|
||||||
|
|
||||||
if( contrastColor ) {
|
if( contrastColor ) {
|
||||||
let rgb = colorToRgb( contrastColor );
|
const rgb = colorToRgb( contrastColor );
|
||||||
|
|
||||||
// Ignore fully transparent backgrounds. Some browsers return
|
// Ignore fully transparent backgrounds. Some browsers return
|
||||||
// rgba(0,0,0,0) when reading the computed background color of
|
// rgba(0,0,0,0) when reading the computed background color of
|
||||||
|
|
|
@ -16,20 +16,26 @@ export default class Print {
|
||||||
* Configures the presentation for printing to a static
|
* Configures the presentation for printing to a static
|
||||||
* PDF.
|
* 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
|
// 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 ) );
|
pageHeight = Math.floor( slideSize.height * ( 1 + config.margin ) );
|
||||||
|
|
||||||
// Dimensions of slides within the pages
|
// Dimensions of slides within the pages
|
||||||
let slideWidth = slideSize.width,
|
const slideWidth = slideSize.width,
|
||||||
slideHeight = slideSize.height;
|
slideHeight = slideSize.height;
|
||||||
|
|
||||||
|
await new Promise( requestAnimationFrame );
|
||||||
|
|
||||||
// Let the browser know what page size we want to print
|
// Let the browser know what page size we want to print
|
||||||
createStyleSheet( '@page{size:'+ pageWidth +'px '+ pageHeight +'px; margin: 0px;}' );
|
createStyleSheet( '@page{size:'+ pageWidth +'px '+ pageHeight +'px; margin: 0px;}' );
|
||||||
|
|
||||||
|
@ -41,29 +47,32 @@ export default class Print {
|
||||||
document.body.style.height = pageHeight + 'px';
|
document.body.style.height = pageHeight + 'px';
|
||||||
|
|
||||||
// Make sure stretch elements fit on slide
|
// Make sure stretch elements fit on slide
|
||||||
|
await new Promise( requestAnimationFrame );
|
||||||
this.Reveal.layoutSlideContents( slideWidth, slideHeight );
|
this.Reveal.layoutSlideContents( slideWidth, slideHeight );
|
||||||
|
|
||||||
// Compute slide numbers now, before we start duplicating slides
|
// Re-run the slide layout so that r-fit-text is applied based on
|
||||||
let doingSlideNumbers = config.slideNumber && /all|print/i.test( config.showSlideNumber );
|
// the printed slide size
|
||||||
queryAll( this.Reveal.getRevealElement(), SLIDES_SELECTOR ).forEach( function( slide ) {
|
slides.forEach( slide => this.Reveal.slideContent.layout( slide ) );
|
||||||
slide.setAttribute( 'data-slide-number', this.Reveal.slideNumber.getSlideNumber( slide ) );
|
|
||||||
}, this );
|
// 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
|
// 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
|
// Vertical stacks are not centred since their section
|
||||||
// children will be
|
// children will be
|
||||||
if( slide.classList.contains( 'stack' ) === false ) {
|
if( slide.classList.contains( 'stack' ) === false ) {
|
||||||
// Center the slide inside of the page, giving the slide some margin
|
// Center the slide inside of the page, giving the slide some margin
|
||||||
let left = ( pageWidth - slideWidth ) / 2,
|
let left = ( pageWidth - slideWidth ) / 2;
|
||||||
top = ( pageHeight - slideHeight ) / 2;
|
let top = ( pageHeight - slideHeight ) / 2;
|
||||||
|
|
||||||
// Re-run the slide layout so that r-fit-text is applied based on
|
const contentHeight = slideScrollHeights[ index ];
|
||||||
// the printed slide size
|
|
||||||
this.Reveal.slideContent.layout( slide );
|
|
||||||
|
|
||||||
let contentHeight = slide.scrollHeight;
|
|
||||||
let numberOfPages = Math.max( Math.ceil( contentHeight / pageHeight ), 1 );
|
let numberOfPages = Math.max( Math.ceil( contentHeight / pageHeight ), 1 );
|
||||||
|
|
||||||
// Adhere to configured pages per slide limit
|
// 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
|
// Wrap the slide in a page element and hide its overflow
|
||||||
// so that no page ever flows onto another
|
// 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.className = 'pdf-page';
|
||||||
page.style.height = ( ( pageHeight + config.pdfPageHeightOffset ) * numberOfPages ) + 'px';
|
page.style.height = ( ( pageHeight + config.pdfPageHeightOffset ) * numberOfPages ) + 'px';
|
||||||
slide.parentNode.insertBefore( page, slide );
|
|
||||||
page.appendChild( slide );
|
page.appendChild( slide );
|
||||||
|
|
||||||
// Position the slide inside of the page
|
// Position the slide inside of the page
|
||||||
|
@ -95,19 +105,19 @@ export default class Print {
|
||||||
if( config.showNotes ) {
|
if( config.showNotes ) {
|
||||||
|
|
||||||
// Are there notes for this slide?
|
// Are there notes for this slide?
|
||||||
let notes = this.Reveal.getSlideNotes( slide );
|
const notes = this.Reveal.getSlideNotes( slide );
|
||||||
if( notes ) {
|
if( notes ) {
|
||||||
|
|
||||||
let notesSpacing = 8;
|
const notesSpacing = 8;
|
||||||
let notesLayout = typeof config.showNotes === 'string' ? config.showNotes : 'inline';
|
const notesLayout = typeof config.showNotes === 'string' ? config.showNotes : 'inline';
|
||||||
let notesElement = document.createElement( 'div' );
|
const notesElement = document.createElement( 'div' );
|
||||||
notesElement.classList.add( 'speaker-notes' );
|
notesElement.classList.add( 'speaker-notes' );
|
||||||
notesElement.classList.add( 'speaker-notes-pdf' );
|
notesElement.classList.add( 'speaker-notes-pdf' );
|
||||||
notesElement.setAttribute( 'data-layout', notesLayout );
|
notesElement.setAttribute( 'data-layout', notesLayout );
|
||||||
notesElement.innerHTML = notes;
|
notesElement.innerHTML = notes;
|
||||||
|
|
||||||
if( notesLayout === 'separate-page' ) {
|
if( notesLayout === 'separate-page' ) {
|
||||||
page.parentNode.insertBefore( notesElement, page.nextSibling );
|
pages.push( notesElement );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
notesElement.style.left = notesSpacing + 'px';
|
notesElement.style.left = notesSpacing + 'px';
|
||||||
|
@ -122,10 +132,11 @@ export default class Print {
|
||||||
|
|
||||||
// Inject slide numbers if `slideNumbers` are enabled
|
// Inject slide numbers if `slideNumbers` are enabled
|
||||||
if( doingSlideNumbers ) {
|
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' );
|
||||||
numberElement.classList.add( 'slide-number-pdf' );
|
numberElement.classList.add( 'slide-number-pdf' );
|
||||||
numberElement.innerHTML = slide.getAttribute( 'data-slide-number' );
|
numberElement.innerHTML = slideNumber;
|
||||||
page.appendChild( numberElement );
|
page.appendChild( numberElement );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,10 +146,9 @@ export default class Print {
|
||||||
// Each fragment 'group' is an array containing one or more
|
// Each fragment 'group' is an array containing one or more
|
||||||
// fragments. Multiple fragments that appear at the same time
|
// fragments. Multiple fragments that appear at the same time
|
||||||
// are part of the same group.
|
// 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 previousFragmentStep;
|
||||||
let previousPage;
|
|
||||||
|
|
||||||
fragmentGroups.forEach( function( fragments ) {
|
fragmentGroups.forEach( function( fragments ) {
|
||||||
|
|
||||||
|
@ -155,11 +165,10 @@ export default class Print {
|
||||||
}, this );
|
}, this );
|
||||||
|
|
||||||
// Create a separate page for the current fragment state
|
// Create a separate page for the current fragment state
|
||||||
let clonedPage = page.cloneNode( true );
|
const clonedPage = page.cloneNode( true );
|
||||||
page.parentNode.insertBefore( clonedPage, ( previousPage || page ).nextSibling );
|
pages.push( clonedPage );
|
||||||
|
|
||||||
previousFragmentStep = fragments;
|
previousFragmentStep = fragments;
|
||||||
previousPage = clonedPage;
|
|
||||||
|
|
||||||
}, this );
|
}, this );
|
||||||
|
|
||||||
|
@ -182,6 +191,10 @@ export default class Print {
|
||||||
|
|
||||||
}, this );
|
}, this );
|
||||||
|
|
||||||
|
await new Promise( requestAnimationFrame );
|
||||||
|
|
||||||
|
pages.forEach( page => pageContainer.appendChild( page ) );
|
||||||
|
|
||||||
// Notify subscribers that the PDF layout is good to go
|
// Notify subscribers that the PDF layout is good to go
|
||||||
this.Reveal.dispatchEvent({ type: 'pdf-ready' });
|
this.Reveal.dispatchEvent({ type: 'pdf-ready' });
|
||||||
|
|
||||||
|
|
|
@ -1335,7 +1335,11 @@ export default function( revealElement, options ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Announce the current slide contents to screen readers
|
// Announce the current slide contents to screen readers
|
||||||
|
// Use animation frame to prevent getComputedStyle in getStatusText
|
||||||
|
// from triggering layout mid-frame
|
||||||
|
requestAnimationFrame( () => {
|
||||||
announceStatus( getStatusText( currentSlide ) );
|
announceStatus( getStatusText( currentSlide ) );
|
||||||
|
});
|
||||||
|
|
||||||
progress.update();
|
progress.update();
|
||||||
controls.update();
|
controls.update();
|
||||||
|
@ -2290,7 +2294,7 @@ export default function( revealElement, options ) {
|
||||||
// When looping is enabled `routes.down` is always available
|
// When looping is enabled `routes.down` is always available
|
||||||
// so we need a separate check for when we've reached the
|
// so we need a separate check for when we've reached the
|
||||||
// end of a stack and should move horizontally
|
// 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;
|
routes.down = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue