From mboxrd@z Thu Jan  1 00:00:00 1970
Path: news.gmane.org!.POSTED!not-for-mail
From: Andy Wingo <wingo@pobox.com>
Newsgroups: gmane.lisp.guile.user
Subject: Re: Example usages of guile-cairo
Date: Thu, 04 Aug 2016 23:06:13 +0200
Message-ID: <87mvkss0qi.fsf@pobox.com>
References: <CAMFYt2aWLCOJxVqUtx-dj4P8oOPzUpD0R=QMfztPgy6Cn9P48w@mail.gmail.com>
NNTP-Posting-Host: blaine.gmane.org
Mime-Version: 1.0
Content-Type: multipart/mixed; boundary="=-=-="
X-Trace: blaine.gmane.org 1470344834 27534 195.159.176.226 (4 Aug 2016 21:07:14 GMT)
X-Complaints-To: usenet@blaine.gmane.org
NNTP-Posting-Date: Thu, 4 Aug 2016 21:07:14 +0000 (UTC)
User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/24.5 (gnu/linux)
Cc: "guile-user@gnu.org" <guile-user@gnu.org>
To: Panicz Maciej Godek <godek.maciek@gmail.com>
Original-X-From: guile-user-bounces+guile-user=m.gmane.org@gnu.org Thu Aug 04 23:07:09 2016
Return-path: <guile-user-bounces+guile-user=m.gmane.org@gnu.org>
Envelope-to: guile-user@m.gmane.org
Original-Received: from lists.gnu.org ([208.118.235.17])
	by blaine.gmane.org with esmtp (Exim 4.84_2)
	(envelope-from <guile-user-bounces+guile-user=m.gmane.org@gnu.org>)
	id 1bVPr7-0005lb-9K
	for guile-user@m.gmane.org; Thu, 04 Aug 2016 23:07:05 +0200
Original-Received: from localhost ([::1]:41757 helo=lists.gnu.org)
	by lists.gnu.org with esmtp (Exim 4.71)
	(envelope-from <guile-user-bounces+guile-user=m.gmane.org@gnu.org>)
	id 1bVPr3-0000qR-W4
	for guile-user@m.gmane.org; Thu, 04 Aug 2016 17:07:02 -0400
Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:55518)
	by lists.gnu.org with esmtp (Exim 4.71)
	(envelope-from <wingo@pobox.com>) id 1bVPqZ-0000qJ-M2
	for guile-user@gnu.org; Thu, 04 Aug 2016 17:06:34 -0400
Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71)
	(envelope-from <wingo@pobox.com>) id 1bVPqU-00028i-Il
	for guile-user@gnu.org; Thu, 04 Aug 2016 17:06:30 -0400
Original-Received: from pb-sasl1.pobox.com ([64.147.108.66]:53061
	helo=sasl.smtp.pobox.com) by eggs.gnu.org with esmtp (Exim 4.71)
	(envelope-from <wingo@pobox.com>) id 1bVPqU-00027y-C0
	for guile-user@gnu.org; Thu, 04 Aug 2016 17:06:26 -0400
Original-Received: from sasl.smtp.pobox.com (unknown [127.0.0.1])
	by pb-sasl1.pobox.com (Postfix) with ESMTP id 159FD2EC39;
	Thu,  4 Aug 2016 17:06:22 -0400 (EDT)
DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=pobox.com; h=from:to:cc
	:subject:references:date:in-reply-to:message-id:mime-version
	:content-type; s=sasl; bh=xAQmNSCqPsdVyeZmXS4pc0XAfxA=; b=VM8r4T
	pixczIy+lWId9ByGM1Kh7MFzCpFbeJ9/hxX/EPvFyKQfc8dPXCtjw0qhdPShnDB1
	aIaNL/MKBoeIL1ZeyV0DlZKDhGcWNXc/SavKMtaGIrixuNT5tcDkvY57APZBIWbT
	ZUsMjuYavb0yQDjz6bDLHWkuqeb8UmTrmrpcE=
DomainKey-Signature: a=rsa-sha1; c=nofws; d=pobox.com; h=from:to:cc
	:subject:references:date:in-reply-to:message-id:mime-version
	:content-type; q=dns; s=sasl; b=Rbnt22Uuf3kPAxXZGFSS1lzc93mPA8Z1
	518dhTz8j8bt3SCCtAStLUyJiv5IChFuW/lajYXlKOGLMAnn44Md8KLkxNTk82Ul
	RRNi91bPZfCLhZ0IiQE5JE7hyBxQpztjRbw5ri+0sQRurc/R3EYUtw9ZpCj5f7L0
	Hb5OCi5TvXw=
Original-Received: from pb-sasl1.nyi.icgroup.com (unknown [127.0.0.1])
	by pb-sasl1.pobox.com (Postfix) with ESMTP id 0DA772EC38;
	Thu,  4 Aug 2016 17:06:22 -0400 (EDT)
Original-Received: from clucks (unknown [88.160.190.192])
	(using TLSv1 with cipher ECDHE-RSA-AES256-SHA (256/256 bits))
	(No client certificate requested)
	by pb-sasl1.pobox.com (Postfix) with ESMTPSA id AC66D2EC37;
	Thu,  4 Aug 2016 17:06:20 -0400 (EDT)
In-Reply-To: <CAMFYt2aWLCOJxVqUtx-dj4P8oOPzUpD0R=QMfztPgy6Cn9P48w@mail.gmail.com>
	(Panicz Maciej Godek's message of "Wed, 6 Jul 2016 22:01:52 +0200")
X-Pobox-Relay-ID: 4B17978C-5A87-11E6-9017-C1836462E9F6-02397024!pb-sasl1.pobox.com
X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic]
	[fuzzy]
X-Received-From: 64.147.108.66
X-BeenThere: guile-user@gnu.org
X-Mailman-Version: 2.1.21
Precedence: list
List-Id: General Guile related discussions <guile-user.gnu.org>
List-Unsubscribe: <https://lists.gnu.org/mailman/options/guile-user>,
	<mailto:guile-user-request@gnu.org?subject=unsubscribe>
List-Archive: <http://lists.gnu.org/archive/html/guile-user/>
List-Post: <mailto:guile-user@gnu.org>
List-Help: <mailto:guile-user-request@gnu.org?subject=help>
List-Subscribe: <https://lists.gnu.org/mailman/listinfo/guile-user>,
	<mailto:guile-user-request@gnu.org?subject=subscribe>
Errors-To: guile-user-bounces+guile-user=m.gmane.org@gnu.org
Original-Sender: "guile-user" <guile-user-bounces+guile-user=m.gmane.org@gnu.org>
Xref: news.gmane.org gmane.lisp.guile.user:12819
Archived-At: <http://permalink.gmane.org/gmane.lisp.guile.user/12819>

--=-=-=
Content-Type: text/plain

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


--=-=-=
Content-Type: text/plain; charset=utf-8
Content-Disposition: attachment; filename=cairo.scm
Content-Transfer-Encoding: quoted-printable

;; guile-present
;; Copyright (C) 2007, 2009, 2010, 2011, 2012, 2014 Andy Wingo <wingo at po=
box 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)
  =3D> (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)
  =3D> (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)) =3D> 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)
  (=3D (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*
  '(("``" . "=E2=80=9C")
    ("''" . "=E2=80=9D")
    ("`" . "=E2=80=98")
    ("'" . "=E2=80=99")
    ("---" . "=E2=80=94")
    ("--" . "=E2=80=93")))

(define (prettify-punctuation word)
  (let lp ((word word) (punctuation *punctuation*))
    (match punctuation
      (() word)
      (((needle . replacement) . punctuation)
       (cond
        ((string-contains word needle)
         =3D> (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=3D> (string-index-right word *subword-charset* 0 tail)
                1+))))

(define* (next-subword cr word available #:optional (end (string-length wor=
d)))
  (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 wid=
th
                      margin-right font-weight font-slant pretty-punctuatio=
n?
                      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-widt=
h)))
         ;; 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-ri=
ght))
                     (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 subwor=
d))))
                            (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)=20
  (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-hei=
ght)))
      (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 shei=
ght)))
            (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-scalin=
g)
    (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 "=E2=9D=A7")
    (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))))

--=-=-=--