Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[wip] pdf generation #16

Merged
merged 23 commits into from
Nov 28, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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