Skip to content

Commit

Permalink
Merge pull request #16 from Anaconda-Server/pdf
Browse files Browse the repository at this point in the history
pdf generation, mostly deferred to nbbrowserpdf
  • Loading branch information
bollwyvl committed Nov 28, 2015
2 parents 730e36d + bd79f8d commit 6c78f6f
Show file tree
Hide file tree
Showing 38 changed files with 916 additions and 243 deletions.
3 changes: 3 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"presets": ["es2015"]
}
2 changes: 1 addition & 1 deletion .binstar.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ engine:
- python=3.5

script:
- conda build -c javascript conda.recipe
- conda build conda.recipe -c javascript -c nbcio

build_targets: conda
2 changes: 1 addition & 1 deletion bin/browserify-notebook.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ browserify \
--external d3 \
--external html2canvas \
--external baobab \
--external node-uuid \
--external uuid \
--external nbpresent-deps \
--transform [ babelify --sourceMapRelative . ] \
${EXTRA} \
Expand Down
48 changes: 0 additions & 48 deletions bin/rasterize.js

This file was deleted.

5 changes: 0 additions & 5 deletions conda.recipe/meta.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,13 @@ requirements:
build:
- nodejs
- python
- notebook
- jupyter
- funcsigs
run:
- python
- notebook
- jupyter
- funcsigs

test:
imports:
- notebook
- nbpresent

about:
Expand Down
81 changes: 66 additions & 15 deletions environment.yml
Original file line number Diff line number Diff line change
@@ -1,28 +1,79 @@
name: nbindex
name: nbpresent
dependencies:
- anaconda-client=1.0.2=py35_0
- clyent=0.4.0=py35_0
- freetype=2.5.2=2
- anaconda-client=1.1.2=py35_0
- bokeh=0.10.0=py35_0
- clyent=1.0.0=py35_0
- cycler=0.9.0=py35_0
- decorator=4.0.4=py35_0
- flask=0.10.1=py35_1
- fontconfig=2.11.1=5
- freetype=2.5.5=0
- ipykernel=4.1.1=py35_0
- ipython=4.0.0=py35_0
- ipython_genutils=0.1.0=py35_0
- ipywidgets=4.1.0=py35_0
- itsdangerous=0.24=py35_0
- jbig=2.1=0
- jinja2=2.8=py35_0
- jpeg=8d=0
- jsonschema=2.4.0=py35_0
- jupyter_client=4.1.1=py35_0
- jupyter_core=4.0.6=py35_0
- libgfortran=1.0=0
- libpng=1.6.17=0
- libtiff=4.0.2=1
- openssl=1.0.1k=1
- pillow=2.9.0=py35_0
- libsodium=1.0.3=0
- libtiff=4.0.6=1
- libxml2=2.9.2=0
- markupsafe=0.23=py35_0
- matplotlib=1.5.0=np110py35_0
- mistune=0.7.1=py35_0
- nbconvert=4.0.0=py35_0
- nbformat=4.0.1=py35_0
- nodejs=4.2.0=1
- notebook=4.0.6=py35_0
- numpy=1.10.1=py35_0
- openblas=0.2.14=3
- openssl=1.0.2d=0
- pandas=0.17.0=np110py35_0
- path.py=8.1.2=py35_0
- pexpect=3.3=py35_0
- pickleshare=0.5=py35_0
- pillow=3.0.0=py35_1
- pip=7.1.2=py35_0
- python=3.5.0=0
- ptyprocess=0.5=py35_0
- pygments=2.0.2=py35_0
- pyparsing=2.0.3=py35_0
- pyqt=4.11.4=py35_0
- python=3.5.0=1
- python-dateutil=2.4.2=py35_0
- pytz=2015.6=py35_0
- pytz=2015.7=py35_0
- pyyaml=3.11=py35_1
- pyzmq=14.7.0=py35_1
- qt=4.8.7=1
- readline=6.2=2
- requests=2.7.0=py35_0
- setuptools=18.1=py35_0
- six=1.9.0=py35_0
- requests=2.8.1=py35_0
- setuptools=18.4=py35_0
- simplegeneric=0.8.1=py35_0
- sip=4.16.9=py35_0
- six=1.10.0=py35_0
- sqlite=3.8.4.1=1
- terminado=0.5=py35_1
- tk=8.5.18=0
- wheel=0.26.0=py35_0
- tornado=4.2.1=py35_1
- traitlets=4.0.0=py35_0
- werkzeug=0.11.2=py35_0
- wheel=0.26.0=py35_1
- xz=5.0.5=0
- yaml=0.1.6=0
- zeromq=4.1.3=0
- zlib=1.2.8=0
- pip:
- elasticsearch==1.7.0
- urllib3==1.12
- bqplot==0.4.2
- ghost.py==0.2.3
- ipython-genutils==0.1.0
- jupyter-client==4.1.1
- jupyter-core==4.0.6
- nbpresent (/home/weg/Documents/projects/nbpresent)==0.3.0.dev0
- pypdf2==1.25.1
- qgrid==0.2.0

22 changes: 20 additions & 2 deletions nbpresent/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,23 @@

def load_jupyter_server_extension(nbapp):
from nbconvert.exporters.export import exporter_map
from .exporter import PresentExporter
exporter_map["nbpresent"] = PresentExporter
from .exporters.html import PresentExporter

nbapp.log.info("Enabling nbpresent HTML export")
exporter_map.update(
nbpresent=PresentExporter,
)

try:
from .exporters.pdf import PDFPresentExporter
except Exception as err:
nbapp.log.warn(
"nbbrowserpdf not available, PDF generation disabled: {}"
.format(err)
)
return

nbapp.log.info("Enabling nbpresent PDF export")
exporter_map.update(
nbpresent_pdf=PDFPresentExporter
)
5 changes: 5 additions & 0 deletions nbpresent/exporters/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# flake8: noqa
from .base import (
ASSETS,
APP_ROOT
)
10 changes: 10 additions & 0 deletions nbpresent/exporters/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import os

from notebook import DEFAULT_STATIC_FILES_PATH

APP_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
ASSETS = os.path.join(APP_ROOT, "static", "nbpresent")
NB_ASSETS = [os.path.join(DEFAULT_STATIC_FILES_PATH, *bits) for bits in [
["components", "requirejs", "require.js"],
["components", "jquery", "jquery.min.js"]
]]
17 changes: 13 additions & 4 deletions nbpresent/exporter.py → nbpresent/exporters/html.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@

from nbconvert.exporters.html import HTMLExporter

APP_ROOT = os.path.abspath(os.path.dirname(__file__))
ASSETS = os.path.join(APP_ROOT, "static", "nbpresent")
from .base import (
APP_ROOT,
ASSETS,
NB_ASSETS
)


class PresentExporter(HTMLExporter):
Expand All @@ -18,6 +21,8 @@ def __init__(self, *args, **kwargs):
super(PresentExporter, self).__init__(*args, **kwargs)

def from_notebook_node(self, nb, resources=None, **kw):
bin_ext = ["woff", "ttf"]

resources = self._init_resources(resources)
resources.update(
nbpresent={
Expand All @@ -26,8 +31,12 @@ def from_notebook_node(self, nb, resources=None, **kw):
sort_keys=True)
},
outputs={
filename: open(filename).read()
for filename in glob(os.path.join(ASSETS, "*.*"))
filename: open(
filename,
"rb" if filename.split(".")[-1] in bin_ext else "r"
).read()
for filename
in list(glob(os.path.join(ASSETS, "*.*"))) + NB_ASSETS
}
)

Expand Down
11 changes: 11 additions & 0 deletions nbpresent/exporters/pdf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from .html import PresentExporter

from nbbrowserpdf.exporters.pdf import BrowserPDFExporter


class PDFPresentExporter(PresentExporter, BrowserPDFExporter):
def pdf_capture_args(self):
return [
"--capture-server-class",
"nbpresent.exporters.pdf_capture:SlideCaptureServer"
]
83 changes: 83 additions & 0 deletions nbpresent/exporters/pdf_capture.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import time

from ghost.bindings import (
QPainter,
QPrinter,
QtCore,
)

from PyPDF2 import (
PdfFileReader,
PdfFileMerger,
)

from nbbrowserpdf.exporters.pdf_capture import CaptureServer

VIEWPORT = (1920, 1080)


class SlideCaptureServer(CaptureServer):
""" CaptureServer to generate multi-page PDF based on nbpresent metadata
"""
def init_session(self):
""" create a session with a some tweaked settings
"""
return self.ghost.start(
# display=True,
# TODO: read this off config
viewport_size=VIEWPORT,
show_scrollbars=False,
)

def page_ready(self):
""" Wait until nbpresent-css gets created
"""
self.session.wait_for_page_loaded()
self.session.wait_for_selector("#nbpresent-css")
time.sleep(1)

def print_to_pdf(self, path):
""" Custom print based on metadata: generate one per slide
"""
merger = PdfFileMerger()

for i, slide in enumerate(self.notebook.metadata.nbpresent.slides):
print("\n\n\nprinting slide", i, slide)
# science help you if you have 9999 slides
filename = self.in_static("notebook-{0:04d}.pdf".format(i))
# this is weird, but it seems to always need it
self.session.show()
self.screenshot(filename)
merger.append(PdfFileReader(filename, "rb"))

# advance the slides
result, resources = self.session.evaluate(
"""
console.log(window.nbpresent);
console.log(window.nbpresent.mode.presenter.speaker.advance());
""")

# always seem to get some weirdness... perhaps could inttegrate
# ioloop...
time.sleep(1)

# all done!
merger.write(path)

def screenshot(self, filename):
""" do an individual slide screenshot
big thanks to https://gist.github.com/jmaupetit/4217925
"""

printer = QPrinter(mode=QPrinter.ScreenResolution)
printer.setOutputFormat(QPrinter.PdfFormat)
printer.setPaperSize(QtCore.QSizeF(*reversed(VIEWPORT)),
QPrinter.DevicePixel)
printer.setOrientation(QPrinter.Landscape)
printer.setOutputFileName(filename)
printer.setPageMargins(0, 0, 0, 0, QPrinter.DevicePixel)

painter = QPainter(printer)
painter.scale(1.45, 1.45)
self.session.main_frame.render(painter)
painter.end()
Loading

0 comments on commit 6c78f6f

Please sign in to comment.