* Re: Example usages of guile-cairo
2016-07-06 20:01 Example usages of guile-cairo Panicz Maciej Godek
2016-07-15 20:19 ` Vladimir Zhbanov
@ 2016-08-04 21:06 ` Andy Wingo
1 sibling, 0 replies; 5+ messages in thread
From: Andy Wingo @ 2016-08-04 21:06 UTC (permalink / raw)
To: Panicz Maciej Godek; +Cc: guile-user@gnu.org
[-- Attachment #1: Type: text/plain, Size: 461 bytes --]
On Wed 06 Jul 2016 22:01, Panicz Maciej Godek <godek.maciek@gmail.com> writes:
> I've been trying to cooperate with guile-cairo. However, the documentation
> serves as a reference rather than a tutorial, and I've been trying to agree
> the tutorial from the Cairo website with guile bindings, to no avail.
Attached is a part of guile-present that uses cairo to render slides. I
think I need to find guile-present a new home, as gitorious went away...
Andy
[-- Attachment #2: cairo.scm --]
[-- Type: text/plain, Size: 24058 bytes --]
;; guile-present
;; Copyright (C) 2007, 2009, 2010, 2011, 2012, 2014 Andy Wingo <wingo at pobox dot com>
;; This library is free software; you can redistribute it and/or
;; modify it under the terms of the GNU Lesser General Public
;; License as published by the Free Software Foundation; either
;; version 3 of the License, or (at your option) any later version.
;;
;; This library is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
;; Lesser General Public License for more details.
;;
;; You should have received a copy of the GNU Lesser General Public
;; License along with this library; if not, see
;; <http://www.gnu.org/licenses/>.
;;; Commentary:
;;
;; Routines to render SXML documents from the presentation vocabulary
;; using the Cairo graphics library.
;;
;;; Code:
(define-module (present cairo)
#:use-module (cairo)
#:use-module (present fold)
#:use-module (ice-9 match)
#:export (presentation-render-cairo))
(define rsvg-handle-render-cairo #f)
(define rsvg-handle-new-from-file #f)
(define rsvg-handle-get-dimensions #f)
(cond
((resolve-module '(rsvg) #:ensure #f)
=> (lambda (mod)
(set! rsvg-handle-render-cairo
(module-ref mod 'rsvg-handle-render-cairo))
(set! rsvg-handle-new-from-file
(module-ref mod 'rsvg-handle-new-from-file))
(set! rsvg-handle-get-dimensions
(module-ref mod 'rsvg-handle-get-dimensions))))
(else
(let ()
(define (no-rsvg . args)
(warn "SVG support not available. Install guile-rsvg."))
(set! rsvg-handle-render-cairo no-rsvg)
(set! rsvg-handle-new-from-file no-rsvg)
(set! rsvg-handle-get-dimensions no-rsvg))))
(define jpeg-dimensions #f)
(define jpeg->rgb #f)
(define interleaved-image-buffer #f)
(cond
((resolve-module '(jpeg) #:ensure #f)
=> (lambda (mod)
(set! jpeg-dimensions (eval 'jpeg-dimensions mod))
(set! jpeg->rgb (eval 'jpeg->rgb mod))
(set! interleaved-image-buffer (eval 'interleaved-image-buffer mod))))
(else
(let ()
(define (no-jpeg . args)
(warn "JPEG support not available. Install guile-jpeg."))
(set! jpeg-dimensions no-jpeg)
(set! jpeg->rgb no-jpeg)
(set! interleaved-image-buffer no-jpeg))))
(define (fold-cairo cr tree bindings params layout stylesheet)
(define (munge-bindings bindings)
(define (munge-post-proc p)
(lambda (tag params layout klayout kids)
(values (p cr tag params layout klayout)
(if (null? (car params))
(cons tag kids)
(cons* tag (cons '@ (car params)) kids)))))
(define (munge-pre-layout-proc p)
(lambda (tag params layout)
(p cr tag params layout)))
(define (munge-text-proc p)
(lambda (text params layout)
(values (p cr text params layout)
text)))
(define (munge-handler h)
(case (car h)
((post) (cons 'post (munge-post-proc (cdr h))))
((pre-layout) (cons 'pre-layout (munge-pre-layout-proc (cdr h))))
((bindings) (cons 'bindings (munge-bindings (cdr h))))
(else h)))
(define (munge-binding b)
(case (car b)
((*text*) (cons '*text* (munge-text-proc (cdr b))))
(else (cons (car b) (map munge-handler (cdr b))))))
(map munge-binding bindings))
(call-with-values
(lambda () (fold-layout tree (munge-bindings bindings)
params layout stylesheet))
(lambda (ret layout)
layout)))
;; Probably, more of this could be public.
(define* (lookup params key
#:optional (default-thunk
(lambda ()
(error "unbound param" key))))
(cond ((null? params) (default-thunk))
((assq key (car params)) => cadr)
(else (lookup (cdr params) key default-thunk))))
(define-syntax let-params
(syntax-rules ()
((_ (param-exp ...) (k ...) b b* ...)
(let ((p (param-exp ...)))
(let-params p (k ...) b b* ...)))
((_ params () b b* ...)
(begin b b* ...))
((_ params ((k default) k* ...) b b* ...)
(let ((k (lookup params 'k (lambda () default))))
(let-params params (k* ...) b b* ...)))
((_ params (k k* ...) b b* ...)
(let ((k (lookup params 'k)))
(let-params params (k* ...) b b* ...)))))
(define* (make-layout left top x line-height #:optional (space 0))
(vector left top x line-height space))
(define (layout-left layout)
(vector-ref layout 0))
(define (layout-top layout)
(vector-ref layout 1))
(define (layout-x layout)
(vector-ref layout 2))
(define (layout-line-height layout)
(vector-ref layout 3))
(define (layout-space layout)
(vector-ref layout 4))
(define (layout-beginning-of-line? layout)
(= (layout-x layout) (layout-left layout)))
(define (layout-indent layout indent)
(make-layout (+ (layout-left layout) indent) (layout-top layout)
(+ (layout-left layout) indent) (layout-line-height layout)))
(define (layout-scroll layout scroll)
(make-layout (layout-left layout) (+ (layout-top layout) scroll)
(layout-left layout) (layout-line-height layout)))
(define (layout-next-line layout line-spacing)
(layout-scroll layout (* (layout-line-height layout) line-spacing)))
(define (layout-enter layout line-height)
(make-layout (layout-left layout) (layout-top layout)
(layout-left layout) line-height))
(define (layout-return layout block-spacing line-spacing)
(if (layout-beginning-of-line? layout)
layout
(layout-next-line layout (* block-spacing line-spacing))))
(define* (layout-advance layout dx #:optional (space 0))
(make-layout (layout-left layout) (layout-top layout)
(+ (layout-x layout) dx) (layout-line-height layout)
space))
(define (layout-ensure-space layout space)
(if (layout-beginning-of-line? layout)
layout
(layout-advance layout 0 (max (layout-space layout) space))))
(define (layout-apply-space layout)
(layout-advance layout (layout-space layout)))
(define (default-pre-layout cr tree params layout)
(let-params params (inline? absolute? margin-left margin-top
text-height text-scaling)
(cond
(inline? layout)
(absolute? (make-layout margin-left margin-top margin-left
(* text-height text-scaling)))
(else (and layout (layout-enter layout (* text-height text-scaling)))))))
(define (default-post cr tag params old-layout layout)
(let-params params (inline? absolute? block-spacing line-spacing)
(cond
(inline? layout)
(absolute? old-layout)
(else (layout-return layout block-spacing line-spacing)))))
(define (specialize-param params k v)
(cons (list (list k v)) params))
(define (text-x-advance cr text)
(cairo-text-extents:x-advance (cairo-text-extents cr text)))
(define *punctuation*
'(("``" . "“")
("''" . "”")
("`" . "‘")
("'" . "’")
("---" . "—")
("--" . "–")))
(define (prettify-punctuation word)
(let lp ((word word) (punctuation *punctuation*))
(match punctuation
(() word)
(((needle . replacement) . punctuation)
(cond
((string-contains word needle)
=> (lambda (pos)
(lp (string-append
(substring word 0 pos)
replacement
(substring word (+ pos (string-length needle))))
punctuation)))
(else (lp word punctuation)))))))
(define *subword-charset* (char-set #\/ #\- ))
(define (find-subword-right word end)
(let ((tail (string-index-right word
(char-set-complement *subword-charset*)
0 end)))
(and tail
(and=> (string-index-right word *subword-charset* 0 tail)
1+))))
(define* (next-subword cr word available #:optional (end (string-length word)))
(let* ((subword-end (find-subword-right word end))
(end (or subword-end end)))
(let* ((subword (substring word 0 end))
(width (text-x-advance cr subword)))
(if (or (< width available) (not subword-end))
(values subword width)
(next-subword cr word available end)))))
(define (terminal? word)
(case (string-ref word (1- (string-length word)))
((#\. #\! #\?) #t)
(else #f)))
(define (flow-text cr params layout text)
(let-params params (text-height text-scaling font-family line-spacing width
margin-right font-weight font-slant pretty-punctuation?
can-end-sentence?)
(define (maybe-prettify word)
(if pretty-punctuation?
(prettify-punctuation word)
word))
(cairo-select-font-face cr font-family font-slant font-weight)
(cairo-set-font-size cr (* text-height text-scaling))
(let ((space-width (text-x-advance cr " ")))
(let render ((text text)
(layout layout))
(cond
((string-null? text)
;; Done.
layout)
((char-whitespace? (string-ref text 0))
;; Interword space.
(render (substring text 1) (layout-ensure-space layout space-width)))
;; Print a word at a time. Ignore whitespace; interword spacing
;; is uniform, except a little bit more after a terminator (.?!).
(else
(let* ((word-end (or (string-index text char-whitespace?)
(string-length text)))
(word (maybe-prettify (substring text 0 word-end))))
(let retry ((word word)
(subword? #f)
(text (substring text word-end))
(word-width (text-x-advance cr word))
(layout (layout-apply-space layout))
(force? #f))
(cond
((and (> (+ (layout-x layout) word-width) (- width margin-right))
(not force?))
;; Word doesn't fit.
(let ((available (- width margin-right (layout-x layout))))
(call-with-values (lambda ()
(next-subword cr word available))
(lambda (subword subword-width)
(if (or (layout-beginning-of-line? layout)
(< subword-width available)
(string-every char-set:punctuation word))
(let ((tail (substring word (string-length subword))))
(retry subword #t (string-append tail text)
subword-width layout #t))
(retry word subword? text word-width
(layout-next-line layout line-spacing)
#f))))))
(else
(cairo-move-to cr (layout-x layout)
(+ (layout-top layout)
(layout-line-height layout)))
(cairo-show-text cr word)
(render text
(layout-advance layout
word-width
(cond
((or subword? (string-null? text)) 0)
((and can-end-sentence?
(terminal? word))
(* 1.5 space-width))
(else space-width))))))))))))))
(define (verbatim-text cr params layout text)
(let-params params (text-height text-scaling font-family line-spacing
font-weight font-slant)
(cairo-select-font-face cr font-family font-slant font-weight)
(cairo-set-font-size cr (* text-height text-scaling))
(let lp ((lines (string-split text #\newline)) (layout layout))
(cairo-move-to cr (layout-x layout)
(+ (layout-top layout)
(layout-line-height layout)))
(cairo-show-text cr (car lines))
(if (null? (cdr lines))
(layout-advance layout (text-x-advance cr (car lines)))
(lp (cdr lines) (layout-next-line layout line-spacing))))))
(define (text-handler cr text params layout)
(let-params params (verbatim?)
((if verbatim? verbatim-text flow-text)
cr params layout text)))
(define (ul-pre-layout cr tree params layout)
(let-params params (indent-width text-height text-scaling)
(layout-enter (layout-indent layout indent-width)
(* text-height text-scaling))))
(define (ul-post cr tag params old-layout layout)
(let-params params (indent-width block-spacing line-spacing)
(layout-return (layout-indent layout (- indent-width))
block-spacing line-spacing)))
(define (make-bullet cr params layout)
(let-params params (bullet-string bullet-font-family)
(flow-text cr (specialize-param params 'font-family bullet-font-family)
layout bullet-string)))
(define (li-post cr tag params old-layout layout)
(let-params params (indent-width)
(make-bullet cr params (layout-indent old-layout (- indent-width)))
(default-post cr tag params old-layout layout)))
(define (title-pre-layout cr tree params layout)
(let-params params (margin-top text-height text-scaling)
;; go to the middle of the page?
(layout-enter (layout-scroll layout margin-top)
(* text-height text-scaling))))
(define (image-width-for-height filename target-height)
(cond
((string-suffix? ".svg" (string-downcase filename))
(call-with-values
(lambda ()
(rsvg-handle-get-dimensions (rsvg-handle-new-from-file
filename)))
(lambda (width height em ex)
(* target-height 1.0 (/ width height)))))
((string-suffix? ".png" (string-downcase filename))
(let ((surf (cairo-image-surface-create-from-png filename)))
(* target-height 1.0 (/ (cairo-image-surface-get-width surf)
(cairo-image-surface-get-height surf)))))
((or (string-suffix? ".jpg" (string-downcase filename))
(string-suffix? ".jpeg" (string-downcase filename)))
(call-with-values (lambda () (jpeg-dimensions filename))
(lambda (width height)
(* target-height 1.0 (/ width height)))))
(else
(error "unknown image type" filename))))
(define (create-image-surface filename target-width target-height)
(cond
((string-suffix? ".svg" (string-downcase filename))
(let ((surf (cairo-image-surface-create 'argb32 target-width target-height)))
(let ((cr (cairo-create surf)))
(rsvg-handle-render-cairo
(rsvg-handle-new-from-file filename) cr)
(cairo-surface-flush surf)
(values surf target-width target-height))))
((string-suffix? ".png" (string-downcase filename))
(let ((surf (cairo-image-surface-create-from-png filename)))
(values surf
(cairo-image-surface-get-width surf)
(cairo-image-surface-get-height surf))))
((or (string-suffix? ".jpg" (string-downcase filename))
(string-suffix? ".jpeg" (string-downcase filename)))
(call-with-values (lambda () (jpeg-dimensions filename))
(lambda (width height)
(let* ((stride (cairo-format-stride-for-width 'rgb24 width))
(rgb (jpeg->rgb filename #:argb? #t
#:stride-for-width (lambda (_) stride))))
(values (cairo-image-surface-create-for-data
(interleaved-image-buffer rgb)
'rgb24 width height stride)
width
height)))))
(else
(warn "unknown image type" filename)
(values #f #f #f))))
(define* (paint-image cr filename x0 y0 width height #:key stretch?
(center-horizontally? #t) (center-vertically? #t))
(define (scale-dimensions aspect)
(cond
(stretch?
(values x0 y0 width height))
((< aspect (/ width height))
;; Tall source.
(let ((new-width (* height aspect)))
(values (if center-horizontally?
(+ x0 (/ (- width new-width) 2))
x0)
y0
new-width height)))
(else
;; Wide source.
(let ((new-height (/ width aspect)))
(values x0
(if center-vertically?
(+ y0 (/ (- height new-height) 2))
y0)
width new-height)))))
(call-with-values (lambda () (create-image-surface filename width height))
(lambda (surface swidth sheight)
(if surface
(call-with-values (lambda () (scale-dimensions (/ swidth 1.0 sheight)))
(lambda (x0 y0 width height)
(cairo-save cr)
(cairo-translate cr x0 y0)
(cairo-scale cr (/ width swidth) (/ height sheight))
(cairo-set-source-surface cr surface 0 0)
(cairo-pattern-set-filter (cairo-get-source cr) 'best)
(cairo-rectangle cr 0 0 swidth sheight)
(cairo-fill cr)
(cairo-restore cr)
(values width height)))
(values 0 0)))))
(define (image-post cr tag params old-layout layout)
(let-params params (image-filename (image-width #f) (image-height #f)
height width margin-right margin-bottom inline?
text-height text-scaling line-spacing)
(cond
(inline?
;; An image in a text block: restrict to the line height.
(let* ((layout (layout-apply-space layout))
(avail-x (- width margin-right (layout-x layout)))
(width-for-height
(image-width-for-height image-filename
(* text-height text-scaling)))
(layout (if (< avail-x width-for-height)
(layout-next-line layout line-spacing)
layout)))
(paint-image cr image-filename
(layout-x layout) (layout-top layout)
width-for-height (* text-height text-scaling)
#:stretch? #t)
(layout-advance layout width-for-height)))
(else
;; An image at the top level: horizontally center in available
;; space.
(let* ((avail-x (- width margin-right (layout-x layout)))
(avail-y (- height (layout-top layout) margin-bottom))
(max-width (or image-width avail-x))
(max-height (or image-height avail-y))
(x0 (+ (layout-x layout) (/ (- avail-x max-width) 2.0))))
(call-with-values
(lambda ()
(paint-image cr image-filename
x0 (layout-top layout)
max-width max-height
#:center-vertically? #f))
(lambda (width height)
(layout-scroll layout height))))))))
(define (set-source-rgb cr rgb)
(cairo-set-source-rgb cr
(/ (logand (ash rgb -16) #xff) 255.0)
(/ (logand (ash rgb -8) #xff) 255.0)
(/ (logand (ash rgb 0) #xff) 255.0)))
(define (slide-pre-layout cr tree params layout)
(let-params params (margin-left margin-top background background-color
foreground-color width height text-height text-scaling)
(set-source-rgb cr background-color)
(cairo-rectangle cr 0 0 width height)
(cairo-fill cr)
(when background
(paint-image cr background 0 0 width height))
(set-source-rgb cr foreground-color)
(make-layout margin-left margin-top margin-left
(* text-height text-scaling))))
(define (slide-post cr tag params old-layout layout)
(cairo-show-page cr)
old-layout)
(define (presentation-post cr tag params old-layout layout)
old-layout)
(define *presentation->svg-rules*
`((presentation
(post . ,presentation-post))
(slide
(pre-layout . ,slide-pre-layout)
(post . ,slide-post))
(title
(pre-layout . ,title-pre-layout))
(image
(post . ,image-post))
(ul
(pre-layout . ,ul-pre-layout)
(post . ,ul-post))
(li
(post . ,li-post))
(*text* . ,text-handler)
(*default*
(pre-layout . ,default-pre-layout)
(post . ,default-post))))
(define *default-params*
'((indent-width 64)
(margin-left 64)
(margin-right 64)
(margin-top 64)
(margin-bottom 64)
(line-spacing 1.1)
(inline? #f)
(absolute? #f)
(can-end-sentence? #t)
(verbatim? #f)
(pretty-punctuation? #t)
(block-spacing 1.4)
(font-family "Serif")
(font-slant normal)
(font-weight normal)
(bullet-string "❧")
(bullet-font-family "Sans")
(text-height 42)
(text-scaling 1/1)
(background #f)
(background-color #xFFFFFF)
(foreground-color #x000000)
(width 1024)
(height 768)))
(define *null-layout* #f)
(define *presentation-stylesheet*
'((title (text-height 96))
(header (text-height 64))
(tt (font-family "Monospace") (inline? #t)
(can-end-sentence? #f) (pretty-punctuation? #f) (text-height 36))
(pre (font-family "Monospace") (verbatim? #t) (pretty-punctuation? #f) (text-height 36) (line-spacing 1.2))
(span (inline? #t))
(image (inline? #t))
(i (font-slant italic) (inline? #t))
(b (font-weight bold) (inline? #t))))
(define (adjoin-stylesheets presentation stylesheet)
(define (adjoin tag key value stylesheet)
(match stylesheet
(() `((,tag (,key ,value))))
(((t . params) . stylesheet)
(if (eq? t tag)
`((,t (,key ,value) . ,params) . ,stylesheet)
(acons t params (adjoin tag key value stylesheet))))))
(match presentation
(('presentation ('@ . params) . body)
(let lp ((in params) (out '()) (stylesheet stylesheet))
(match in
(() (values `(presentation (@ ,@(reverse out)) . ,body)
stylesheet))
(((and param (tag key value)) . in)
(lp in out (adjoin tag key value stylesheet)))
((param . in)
(lp in (cons param out) stylesheet)))))
(_ (values presentation stylesheet))))
(define (presentation-render-cairo presentation cr)
"Convert an SXML document in the @code{presentation} vocabulary to a
multi-layered SVG.
The result will still be a document in SXML format, so if you want to
write it to disk, use @code{sxml->xml}. @xref{sxml simple,(sxml
simple),(sxml simple),guile-library,Guile Library}, for more
information.
The resulting SVG will be written with annotations readable by Inkscape,
a vector graphics editor, which help to make the SVG easily editable. If
your toolchain does not understand namespaces, you might want to filter
out elements that start with @samp{sodipodi:}, @samp{xmlns:}, and
@samp{inkscape:}.
"
(call-with-values (lambda ()
(adjoin-stylesheets presentation
*presentation-stylesheet*))
(lambda (presentation stylesheet)
(fold-cairo cr presentation *presentation->svg-rules* *default-params*
*null-layout* stylesheet))))
^ permalink raw reply [flat|nested] 5+ messages in thread