summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBehdad Esfahbod <behdad@behdad.org>2014-09-16 19:07:36 +0200
committerBehdad Esfahbod <behdad@behdad.org>2014-09-16 19:31:40 +0200
commit155410efbd93813e8f444f6b9eaf31917159cc28 (patch)
treed41d28cc640ec338a85eb5cdb8000b97e73c9938
parent3b3ec3a18a08285dc82d22ef30473cb0048a684a (diff)
[fonttools] Start slides
-rw-r--r--fonttools/Makefile12
-rw-r--r--fonttools/behdad.pngbin0 -> 11176 bytes
-rwxr-xr-xfonttools/fonttools_slides.py198
-rw-r--r--fonttools/fonttools_theme.py142
l---------fonttools/pangopygments.py1
-rw-r--r--fonttools/pillars.jpgbin0 -> 833230 bytes
l---------fonttools/slippy.py1
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
new file mode 100644
index 0000000..b9ed80f
--- /dev/null
+++ b/fonttools/behdad.png
Binary files differ
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 &amp;\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("&", "&amp;").replace("<", "&lt;")
+ 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("&", "&amp;").replace("<", "&lt;")
+ 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
new file mode 100644
index 0000000..f529f18
--- /dev/null
+++ b/fonttools/pillars.jpg
Binary files differ
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