impress / impress.js

It's a presentation framework based on the power of CSS3 transforms and transitions in modern browsers and inspired by the idea behind prezi.com.
http://impress.js.org
MIT License
37.62k stars 6.68k forks source link

PDF export #141

Closed eroullit closed 12 years ago

eroullit commented 12 years ago

I tried to export an impress.js presentation to a PDF file. Unfortunately, the output file is not usable. It is expected that all animations are gone after this but would it be possible to export the "fallback" presentation properly?

Thanks in advance!

bartaz commented 12 years ago

How are you exporting it? By printing from the browser?

3D positioning is added in the browser, so it wouldn't be easy to detect print mode and disable these styles. Probably some switch between impress.js and fallback mode would be needed.

eroullit commented 12 years ago

By exporting it, I meant using File -> Print -> Print to file

bartaz commented 12 years ago

OK. I get that, so it's about print styles. I'll think what I can do.

Thanks for your feedback!

derkoe commented 12 years ago

Could you add a deinit() method similar to jmpress so that the page can be printed?

Then one could add print stylesheet and a print button deiniting impress.js and then calling window.print

damienalexandre commented 12 years ago

I'm achieving a quite good PDF export with those steps:

Adding this CSS:


/* ==|== print styles =======================================================
   Print styles.
   Inlined to avoid required HTTP connection: h5bp.com/r
   ========================================================================== */

@media print {
  * { background: transparent !important; color: black !important; box-shadow:none !important; text-shadow: none !important; } /* Black prints faster: h5bp.com/s */
  a, a:visited { text-decoration: underline; }
  a[href]:after { content: " (" attr(href) ")"; }
  abbr[title]:after { content: " (" attr(title) ")"; }
  .ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after { content: ""; } /* Don't show links for images, or javascript/internal links */
  thead { display: table-header-group; } /* h5bp.com/t */
  tr, img { page-break-inside: avoid; }
  img { max-width: 100% !important; }
  @page { margin: 0.5cm; }
  p, h2, h3 { orphans: 3; widows: 3; }
  h2, h3 { page-break-after: avoid; }

  /* Impress.js specific */
  .fallback-message { display: none; }
  @page land {size: landscape;}
  .step { page-break-inside: avoid; page-break-after: always; border: none; margin-top: 100px; }
  .step img { max-height: 100%; }

  /* plus some more step specific styles of your own
  #htop2 { display: none }
   */
}

Then I switch off Javascript, reload the page and print into a PDF from Firefox. In the print options I'm checking "borderless" and some others stuffs.

There is one downside I can't fix atm, it's the display of the page title/url at the top and the page number at the bottom: I don't know how I can remove them.

There is a lot of CSS3 options here for the @page tag: http://www.w3.org/TR/css3-page/

The point of this is obviously to be able to print a decent copy of the presentation, but also ease the sharing and the offline consultation.

bartaz commented 12 years ago

With properly developed CSS styles impress.js presentation should nicely fallback to linear way when it detects unsupported browser or when it is not initialized.

This can be very easily used to support printing. Unfortunately because of inline 3D transforms on the elements you cant just print initialized presentation, but you can write the code that will enable 'print mode' when specific parameter will be given in the url.

For example if your presentation is at http://example.com/my-awesome-impressjs-prez/ you can make a print link for example as http://example.com/myawesome-impressjs-prez/?print.

Now a little bit of code in HTML to detect {{print}} in url and only initialize the presentation if it is not available - simply wrap the init script into:

if ( !window.location.search.match(/print/) ) {
    impress().init();
}

You can even add a print link somewhere <a href="?print">Print me</a>.

And of course add some print styles to make your steps nicely print.

I will this code into my demo when I have a moment to add some print styles. But basically I treat this issue as solved as this can be done without any changes in impress.js itself.

mweimerskirch commented 11 years ago

This works pretty well, removing transforms and positioning (using "!important") and adding page breaks:

.fallback-message { display: none; }
@page land {size: landscape;}
.step { border: 0; opacity: 1 !important; page-break-inside: avoid; page-break-after: always; position: relative !important; -webkit-transform: none !important;  }
.step img { max-height: 100%; }
body { height: auto !important; overflow: visible !important; }
#impress { position: relative !important; -webkit-transform-origin: none !important; top: 0 !important; left: 0 !important; -webkit-transform: none !important; }

And yes it works even with initialized slideshows.

regebro commented 11 years ago

For some types of slides this isn't going to work, as the zooming is used to zoom into different bits, so one slide might be completely empty and just zoom into something that is actually in another slide.

I realize this is kinda unusual, but I would appreciate ideas on how to solve that. Essentially, I think we need to get a browser to actually render the viewport to something printable and do this once per slide. I have no clue if that is even doable.

ogrisel commented 11 years ago

Deck.js has a script to generate png rendering assembled into a PDF using the phantom.js programmatic browser.

bartaz commented 11 years ago

For me it looks like overengineering.

If you create your presentation content in accessible way - so it works fine for browsers not supporting impress.js, displaying slide contents in linear fashion, there is no problem with printing it to PDF with a little bit of styling added to separate pages, etc. (Assuming that in 'print mode' impress.js will be disabled - it's a simple if statement)

regebro commented 11 years ago

If impress.js is disabled, then there is nothing to print. I guess that means that slide content is not displayed in a "linear fashion". Look for example at this impress.js presentation, especially at around the 5-6 minute mark.

http://pyvideo.org/video/1690/things-to-make-writing-tests-easier

phantom.js seems to me to be the first suggestion for a usable solution.

bartaz commented 11 years ago

@regebro yes, it looks like quite a special case - question is, do you really need to have these steps (differents parts of code selected) printed as separate slides, or maybe whole block of code printed would be fine.

It really depends what do you want to print and how. Making content of the presentation accessible in printable way - it's a matter of styling and disabling impress.js. When you want to generate PDF with actual slides (to upload it to slideshare or something) you may need more complicated tool (like phantom for screenshots).

For me, solving such problems is quite out of the scope for impress.js (or any other presentation framework - it could easily be done as universal script for deck, reveal, impress or anything else), but it's nice to hear discussions around such topics.

regebro commented 11 years ago

It is indeed a good question, but printing from a browser will not do either case. Printing from a browser will give you a big code block on one page, and then a couple of pages with red squares. So something else needs to be done. The only idea I have that is generic to impress.js is to print each viewport.

I could in Hovercraft! probably add some special features to do something else, but it would still require rendering viewports, but perhaps with something clever done to print different viewports or skip slides.

It doens't need to be a part of impress.js, it could easily be a separate script. I don't know if it could be generic, I do know that the one for deck doesn't work with impress, but that might be fixable.

aigamedev commented 11 years ago

The phantom.js solution didn't work out for us, since CSS3D is not supported as it requires a hardware-based rendering backend rather than the software solution that phantom.js ships with. We've ended up with a Chrome Extension that uses chrome.tabs.captureVisibleTab(). Unfortunately, Chrome's security model makes it a pain to save the resulting PNG files out, but it's possible indirectly.

regebro commented 11 years ago

Interesting. I'm wondering if Chromium Embedded Framework could be useful here. http://code.google.com/p/chromiumembedded/

I'm planning to look into using http://code.google.com/p/cefpython/ for use with Hovercraft, but I doubt I will get time for that this year, I have other features that I actually need that I'll do first.

luzfcb commented 9 years ago

https://github.com/melix/deck2pdf

alexanderklein commented 9 years ago

deck2pdf works very well for me as long as i don't go crazy with animations and when i avoid having steps overlapp. It might also help to increase the time out (defaults to 1s) in deck2pdf to ensure all animations are done when steps are being printed/exported.