diff options
author | Behdad Esfahbod <behdad@behdad.org> | 2014-09-16 19:07:36 +0200 |
---|---|---|
committer | Behdad Esfahbod <behdad@behdad.org> | 2014-09-16 19:31:40 +0200 |
commit | 155410efbd93813e8f444f6b9eaf31917159cc28 (patch) | |
tree | d41d28cc640ec338a85eb5cdb8000b97e73c9938 | |
parent | 3b3ec3a18a08285dc82d22ef30473cb0048a684a (diff) |
[fonttools] Start slides
-rw-r--r-- | fonttools/Makefile | 12 | ||||
-rw-r--r-- | fonttools/behdad.png | bin | 0 -> 11176 bytes | |||
-rwxr-xr-x | fonttools/fonttools_slides.py | 198 | ||||
-rw-r--r-- | fonttools/fonttools_theme.py | 142 | ||||
l--------- | fonttools/pangopygments.py | 1 | ||||
-rw-r--r-- | fonttools/pillars.jpg | bin | 0 -> 833230 bytes | |||
l--------- | fonttools/slippy.py | 1 |
7 files changed, 354 insertions, 0 deletions
diff --git a/fonttools/Makefile b/fonttools/Makefile new file mode 100644 index 0000000..039da8b --- /dev/null +++ b/fonttools/Makefile @@ -0,0 +1,12 @@ +name = fonttools + +all: $(name)_slides.pdf + +view: + python slippy.py $(name)_slides.py -t $(name)_theme.py + +%_slides.pdf: slippy.py %_slides.py %_theme.py + python slippy.py $*_slides.py -t $*_theme.py -o $@ + +clean: + $(RM) $(name)_slides.pdf *.pyc diff --git a/fonttools/behdad.png b/fonttools/behdad.png Binary files differnew file mode 100644 index 0000000..b9ed80f --- /dev/null +++ b/fonttools/behdad.png diff --git a/fonttools/fonttools_slides.py b/fonttools/fonttools_slides.py new file mode 100755 index 0000000..a95d6de --- /dev/null +++ b/fonttools/fonttools_slides.py @@ -0,0 +1,198 @@ +#!/usr/bin/python +# -*- coding:utf8 -*- + +# Copyright 2014 Behdad Esfahbod <behdad@google.com> + +# A slides file should populate the variable slides with +# a list of tuples. Each tuple should have: +# +# - Slide content +# - User data +# - Canvas width +# - Canvas height +# +# Slide content can be a string, a list of strings, +# a function returning one of those, or a generator +# yielding strings. The user data should be a dictionary or +# None, and is both used to communicate options to the +# renderer and to pass extra options to the theme functions. +# +# A function-based slide content will be passed a renderer object. +# Renderer is an object similar to a cairo.Context and +# pangocairo.CairoContext but has its own methods too. +# The more useful of them here are put_text, put_image, and +# set_allocation. See their pydocs. + +slides = [] +def slide_add(f, data=None, width=800, height=600): + #slides[:0] = [(f, data, width, height)] + slides.append ((f, data, width, height)) + return f + +import pango, pangocairo, cairo, os, signal +from pangopygments import highlight + +# We use slide data to tell the theme who's speaking. +# That is, which side the bubble should point to. +behdad = -1 +whois = 0 +def who(name): + global whois + whois = name +# And convenience functions to add a slide. Can be +# used as a function decorator, or called directly. +def slide_who(f, who, data=None): + if data: + data = dict (data) + else: + data = {} + data['who'] = who + return slide_add (f, data) +def slide(f, data=None): + return slide_who (f, whois, data=data) +def slide_noone(f, data=None): + if data and 'who' in data: + return slide_who (f, data['who'], data=data) + else: + return slide_who (f, 0, data=data) + +# +# Slides start here +# + +who (behdad) + +@slide +def title_slide (r): + r.move_to (400, 250) + r.put_text ("<b>FontTools</b>\n"+ + "<span font_desc='30'>"+ + "reviving an Open Source project &\n"+ + "<span strikethrough='true'>re</span>building a thriving community"+ + "</span>", valign=0, halign=0, desc="100") + + r.move_to (400, 550) + r.put_text ("""Behdad Esfahbod\n<span font_desc="monospace 16">behdad@google.com\nhttp://behdad.org</span>""", + desc="20", halign=0, valign=-1) + +def list_slide (l, data=None): + def s (r): + return '\n'.join (l) + #yield l[0] + #for i in l[1:]: + # yield '\n'+i + s.__name__ = l[0] + slide (s, data) + +slide_noone("History") +slide_noone("Where") + +def image_slide (f, width=600, height=600, imgwidth=0, imgheight=0, xoffset=0, yoffset=0, data=None): + def s (r): + r.move_to (400+xoffset, 300+yoffset) + r.put_image (f, width=imgwidth, height=imgheight) + x = (800 - width) * .5 + y = (600 - height) * .5 + r.set_allocation (x, y, width, height) + s.__name__ = f + slide (s, data) + return s + +def draw_image (r, f, width=600, height=600, imgwidth=0, imgheight=0, xoffset=0, yoffset=0, data=None): + r.move_to (400+xoffset, 300+yoffset) + r.put_image (f, width=imgwidth, height=imgheight) + x = (800 - width) * .5 + y = (600 - height) * .5 + r.set_allocation (x, y, width, height) + +slide_noone("<b>Demo\ntime!</b>") + +# Demo! + +slide_noone("<b>Limitations</b>") +slide("Memory\nfootprint") +slide("Speed+memory\nfont-dependent") + +slide_noone("<b>Advantages</b>") +slide("Memory\nfootprint") +slide("Subpixel\npositioning") +slide("Pinch-to-zoom") + +list_slide ([ + "<b>Challenges</b>", + "• Shader size / complexity", + "• Pixel cost", + "• Conditionals", + "• Dependent texture lookups", + "• Variable loop iterations", + "• Interpolation accuracy", + ], data={'align': pango.ALIGN_LEFT}) + +def source_slide(s, lang="python"): + s = highlight(s, lang) + s = "<span font_desc='monospace'>" + s + "</span>" + slide_noone (s, data={'align': pango.ALIGN_LEFT}) + +def patch_slide(s): + lines = s.split ("\n") + new_lines = [] + for s in lines: + s = s.replace("&", "&").replace("<", "<") + if not s: s = ' ' + if s[0] == '-': + s = "<span fgcolor='#d00'>%s</span>" % s + elif s[0] == '+': + s = "<span fgcolor='#0a0'>%s</span>" % s + elif s[0] != ' ': + s = "<b>%s</b>" % s + + new_lines.append (s) + + s = '\n'.join (new_lines) + s = "<span font_desc='monospace'>" + s + "</span>" + slide_noone (s, data={'align': pango.ALIGN_LEFT}) + +def commit_slide(s, who=None): + lines = s.split ("\n") + new_lines = [] + for s in lines: + s = s.replace("&", "&").replace("<", "<") + if not s: s = ' ' + if s[0] != ' ': + s = "<b>%s</b>" % s + + new_lines.append (s) + + s = '\n'.join (new_lines) + s = "<span font_desc='monospace'>" + s + "</span>" + if who: + slide_noone (s, data={'align': pango.ALIGN_LEFT, 'who': who}) + else: + slide_noone (s, data={'align': pango.ALIGN_LEFT}) + +list_slide ([ + "<b>Code: libglyphy</b>", + "• ~400 lines *.h", + "• ~2500 lines *.cc *.hh", + "• ~370 lines *.glsl", + "• No dependencies", + ], data={'align': pango.ALIGN_LEFT}) +list_slide ([ + "<b>Code: glyphy-demo</b>", + "• ~2800 lines *.cc *.h", + "• ~150 lines *.glsl", + "• FreeType, GLUT", + ], data={'align': pango.ALIGN_LEFT}) +list_slide ([ + "<b>More work</b>", + "• Subpixel-rendering", + "• Anisotropic-antialiasing", + ], data={'align': pango.ALIGN_LEFT}) + +slide("<b>Gallery!</b>") +slide("<b>Q?</b>") + +if __name__ == "__main__": + import slippy + import fonttools_theme + slippy.main (slides, fonttools_theme) diff --git a/fonttools/fonttools_theme.py b/fonttools/fonttools_theme.py new file mode 100644 index 0000000..5c5bbf3 --- /dev/null +++ b/fonttools/fonttools_theme.py @@ -0,0 +1,142 @@ +# vim: set fileencoding=utf-8 : +# Written by Behdad Esfahbod, 2014 +# Not copyrighted, in public domain. + +# A theme file should define two functions: +# +# - prepare_page(renderer): should draw any background and return a tuple of +# x,y,w,h that is the area to use for slide canvas. +# +# - draw_bubble(renderer, x, y, w, h, data=None): should setup canvas for the +# slide to run. Can draw a speaking-bubble for example. x,y,w,h is the +# actual extents that the slide will consume. Data will be the user-data +# dictionary from the slide. +# +# Renderer is an object similar to a cairo.Context and pangocairo.CairoContext +# but has its own methods too. The more useful of them here are put_text and +# put_image. See their pydocs. + +import cairo + +avatar_margin = .10 +logo_margin = .01 +footer_margin = .04 +padding = .000 +bubble_rad = .25 + +def bubble (cr, x0, y0, x, y, w, h): + + r = min (w, h) * (bubble_rad / (1 - 2./8*bubble_rad)) + + p = r / 7. + x, y, w, h, r = x - p, y - p, w + 2*p, h + 2*p, r + p + + x1, y1, x2, y2 = x, y, x + w, y + h + + cr.move_to (x1+r, y1) + cr.line_to (x2-r, y1) + cr.curve_to (x2, y1, x2, y1, x2, y1+r) + cr.line_to (x2, y2-r) + cr.curve_to (x2, y2, x2, y2, x2-r, y2) + cr.line_to (x1+r, y2) + cr.curve_to (x1, y2, x1, y2, x1, y2-r) + cr.line_to (x1, y1+r) + cr.curve_to (x1, y1, x1, y1, x1+r, y1) + cr.close_path () + + xc, yc = .5 * (x1 + x2), .5 * (y1 + y2) + cr.move_to (xc+r, yc) + cr.curve_to (xc+r, y0, .5 * (xc+r+x0), (yc+y0*2)/3, x0, y0) + cr.curve_to (.5 * (xc-r+x0), (yc+y0*2)/3, xc-r, y0, xc-r, yc) + + +def prepare_page (renderer): + cr = renderer.cr + width = renderer.width + height = renderer.height + + a = avatar_margin * width + s = (logo_margin + footer_margin) * .5 * width + l = logo_margin * height + f = footer_margin * height + p = padding * min (width, height) + p2 = 2 * p + + cr.paint () + sky = cairo.LinearGradient (0, 0, 0, height) + sky.add_color_stop_rgba (0, 0x02/255., 0x23/255., 0x80/255., 1.) + sky.add_color_stop_rgba (1, 0x9b/255., 0x84/255., 0x66/255., 1.) + cr.set_source (sky) + cr.paint () + + # Background image + cr.move_to (width / 2., height / 2.) + renderer.put_image ("pillars.jpg", height = height, width = width) + + cr.move_to (.5 * width, height-p2) + b = 150. + cr.set_source_rgb (0x9b/b, 0x84/b, 0x66/b) + renderer.put_text ("ATypI Barcelona, 17 September 2014", height=f-p2, valign=-1, desc="bold") + + # Cartoon icons for speakers + who = renderer.data.get ('who', None) + if who: + if who < 0: + cr.move_to (p, height-p) + renderer.put_image ("behdad.png", width = a-p2, valign=-1, halign=+1) + else: + cr.move_to (width-p, height-p) + renderer.put_image (who, width = a-p2, valign=-1, halign=-1) + + # Compute rectangle available for slide content + w = width - s - s - p * 2 + x = s + p + h = height - l - f - p * 2 + y = l + p + + # Adjust for bubble padding. the 8 comes from bezier calculations + d = min (w, h) * bubble_rad / 8. + x, y, w, h = x + d, y + d, w - d*2, h - d*2 + + return x, y, w, h + +def draw_bubble (renderer, x, y, w, h, data={}): + # Fancy speech bubble! + cr = renderer.cr + width = renderer.width + height = renderer.height + + s = avatar_margin * width + p = padding * min (width, height) + + cr.save() + x, y = cr.user_to_device (x, y) + w, h = cr.user_to_device_distance (w, h) + cr.identity_matrix () + + who = data.get ('who', None) + if not who: + xc, yc = x + w*.5, y + h*.5 + elif who < 0: + xc, yc = s * .9, height - .7 * s + else: + xc, yc = width - s * .9, height - .7 * s + + bubble (cr, xc, yc, x, y, w, h) + cr.rectangle (width, 0, -width, height) + cr.clip () + + bubble (cr, xc, yc, x, y, w, h) + #cr.set_source_rgb (0, 0, 0) + #cr.set_line_width (p) + #cr.set_miter_limit (20) + #cr.stroke_preserve () + + cr.restore () + + cr.clip () + cr.set_source_rgba (1, 1, 1, .95) + cr.paint () + + b = 700. + cr.set_source_rgb (0xbe/b, 0xa3/b, 0x89/b) diff --git a/fonttools/pangopygments.py b/fonttools/pangopygments.py new file mode 120000 index 0000000..3a5bcdd --- /dev/null +++ b/fonttools/pangopygments.py @@ -0,0 +1 @@ +../pangopygments.py
\ No newline at end of file diff --git a/fonttools/pillars.jpg b/fonttools/pillars.jpg Binary files differnew file mode 100644 index 0000000..f529f18 --- /dev/null +++ b/fonttools/pillars.jpg diff --git a/fonttools/slippy.py b/fonttools/slippy.py new file mode 120000 index 0000000..8eb5363 --- /dev/null +++ b/fonttools/slippy.py @@ -0,0 +1 @@ +../slippy.py
\ No newline at end of file |