]> git.donarmstrong.com Git - lilypond.git/blob - scm/output-svg.scm
release commit
[lilypond.git] / scm / output-svg.scm
1 ;;;; output-svg.scm -- implement Scheme output routines for SVG1
2 ;;;;
3 ;;;;  source file of the GNU LilyPond music typesetter
4 ;;;;
5 ;;;; (c) 2002--2005 Jan Nieuwenhuizen <janneke@gnu.org>
6
7 ;;;; http://www.w3.org/TR/SVG11
8 ;;;; http://www.w3.org/TR/SVG12/ -- page, pageSet in draft
9
10 ;;;; TODO:
11 ;;;;  * .cff MUST NOT be in fc's fontpath.
12 ;;;;    - workaround: remove mf/out from ~/.fonts.conf,
13 ;;;;      instead add ~/.fonts and symlink all /mf/out/*otf there.
14 ;;;;    - bug in fontconfig/freetype/pango?
15
16 ;;;;  * inkscape page/pageSet support
17 ;;;;  * inkscape SVG-font support
18 ;;;;    - use fontconfig/fc-cache for now, see output-gnome.scm
19
20 (debug-enable 'backtrace)
21 (define-module (scm output-svg))
22 (define this-module (current-module))
23
24 (use-modules
25  (guile)
26  (ice-9 regex)
27  (lily)
28  (srfi srfi-13))
29
30
31 (define lily-unit-length 1.75)
32
33 (define (dispatch expr)
34   (let ((keyword (car expr)))
35     (cond
36      ((eq? keyword 'some-func) "")
37      ;;((eq? keyword 'placebox) (dispatch (cadddr expr)))
38      (else
39       (if (module-defined? this-module keyword)
40           (apply (eval keyword this-module) (cdr expr))
41           (begin
42             (display
43              (string-append "undefined: " (symbol->string keyword) "\n"))
44             ""))))))
45
46 ;; Helper functions
47 (define-public (attributes attributes-alist)
48   (apply string-append
49          (map (lambda (x) (format #f " ~s=\"~a\"" (car x) (cdr x)))
50               attributes-alist)))
51
52 (define-public (eo entity . attributes-alist)
53   (format #f "<~S~a>\n" entity (attributes attributes-alist)))
54
55 (define-public (eoc entity . attributes-alist)
56   (format #f "<~S~a/>\n" entity (attributes attributes-alist)))
57
58 (define-public (ec entity)
59   (format #f "</~S>\n" entity))
60
61 (define-public (entity entity string . attributes-alist)
62   (if (equal? string "")
63       (apply eoc entity attributes-alist)
64       (string-append
65        (apply eo (cons entity attributes-alist)) string (ec entity))))
66
67 (define (offset->point o)
68   (format #f " ~S,~S" (car o) (cdr o)))
69
70 (define (svg-bezier lst close)
71   (let* ((c0 (car (list-tail lst 3)))
72          (c123 (list-head lst 3)))
73     (string-append
74      (if (not close) "M " "L ")
75      (offset->point c0)
76      "C " (apply string-append (map offset->point c123))
77      (if (not close) "" (string-append
78                          "L " (offset->point close))))))
79
80 (define (sqr x)
81   (* x x))
82
83 (define (integer->entity integer)
84   (format #f "&#x~x;" integer))
85
86 (define (char->entity char)
87   (integer->entity (char->integer char)))
88
89 (define (string->entities string)
90   (apply string-append
91          (map (lambda (x) (char->entity x)) (string->list string))))
92
93 (define pango-description-regexp
94   (make-regexp "^([^,]+)+, ?([-a-zA-Z_]*) ([0-9.]+)$"))
95
96 (define (pango-description-to-svg-font str)
97   (let*
98       ((size 4.0)
99        (family "Helvetica")
100        (style #f)
101        (match (regexp-exec pango-description-regexp str)))
102
103     (if (regexp-match? match)
104         (begin
105           (set! family (match:substring match 1))
106           (if (< 0 (string-length (match:substring match 2)))
107               (set! style (match:substring match 2)))
108           (set! size
109                 (string->number (match:substring match 3))))
110
111         (display (format "Cannot decypher Pango description:  ~a\n" str)))
112
113     (set! style
114           (if (string? style)
115               (format "font-style:~a;" style)
116               ""))
117     
118     (format "font-family:~a;~afont-size:~a;text-anchor:west"
119             family
120             style
121             (/ size lily-unit-length))
122     ))
123
124 ;;; FONT may be font smob, or pango font string
125 (define (svg-font font)
126   (if (string? font)
127       (pango-description-to-svg-font font)
128       (let ((name-style (font-name-style font))
129             (size (modified-font-metric-font-scaling font))
130             (anchor "west"))
131
132         (format #f "font-family:~a;font-style:~a;font-size:~a;text-anchor:~a;"
133                 (car name-style) (cadr name-style)
134                 size anchor))))
135
136 (define (fontify font expr)
137   (entity 'text expr (cons 'style (svg-font font))))
138
139 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
140 ;;; stencil outputters
141 ;;;
142
143 ;;; catch-all for missing stuff
144 ;;; comment this out to see find out what functions you miss :-)
145 (define (dummy . foo) "")
146 (map (lambda (x) (module-define! this-module x dummy))
147      (append
148       (ly:all-stencil-expressions)
149       (ly:all-output-backend-commands)))
150
151 (define (rect-beam width slope thick blot-diameter)
152   (let* ((x width)
153          (y (* slope width))
154          (z (/ y x)))
155     (entity 'rect ""
156             ;; The stroke will stick out.  To use stroke,
157             ;; the stroke-width must be subtracted from all other dimensions.
158             ;;'(stroke-linejoin . "round")
159             ;;'(stroke-linecap . "round")
160             ;;`(stroke-width . ,blot-diameter)
161             ;;'(stroke . "red")
162             ;;'(fill . "orange")
163
164             `(x . 0)
165             `(y . ,(- (/ thick 2)))
166             `(width . ,width)
167             `(height . ,(+ thick (* (abs z) (/ thick 2))))
168             `(rx . ,(/ blot-diameter 2))
169             `(transform . ,(format #f "matrix (1, ~f, 0, 1, 0, 0)" (- z))
170                            ))))
171
172 (define (beam width slope thick blot-diameter)
173   (let* ((b blot-diameter)
174          (t (- thick b))
175          (w (- width b))
176          (h (* w slope)))
177     (entity 'polygon ""
178             '(stroke-linejoin . "round")
179             '(stroke-linecap . "round")
180             `(stroke-width . ,blot-diameter)
181             '(stroke . "black")
182             '(fill . "black")
183             `(points . ,(string-join
184                          (map offset->point
185                               (list (cons (/ b 2) (/ t 2))
186                                     (cons (+ w (/ b 2)) (+ h (/ t 2)))
187                                     (cons (+ w (/ b 2)) (+ h (- (/ t 2))))
188                                     (cons (/ b 2) (- (/ t 2)))))))
189             )))
190
191 (define (path-beam width slope thick blot-diameter)
192   (let* ((b blot-diameter)
193          (t (- thick b))
194          (w (- width b))
195          (h (* w slope)))
196     (entity 'path ""
197             '(stroke-linejoin . "round")
198             '(stroke-linecap . "round")
199             `(stroke-width . ,blot-diameter)
200             '(stroke . "black")
201             '(fill . "black")
202             `(d . ,(format #f "M ~S,~S l ~S,~S l ~S,~S l ~S,~S l ~S,~S"
203                            (/ b 2) (/ t 2)
204                            w (- h)
205                            0 (- t)
206                            (- w) h
207                            0 t))
208           )))
209
210 (define (bezier-sandwich lst thick)
211   (let* ((first (list-tail lst 4))
212          (first-c0 (car (list-tail first 3)))
213          (second (list-head lst 4)))
214     (entity 'path ""
215             '(stroke-linejoin . "round")
216             '(stroke-linecap . "round")
217             `(stroke-width . ,thick)
218             '(stroke . "black")
219             '(fill . "black")
220             `(d . ,(string-append (svg-bezier first #f)
221                                   (svg-bezier second first-c0)))
222           )))
223
224 (define (char font i)
225   (dispatch
226    `(fontify ,font ,(entity 'tspan (char->entity (integer->char i))))))
227
228 (define-public (comment s)
229   (string-append "<!-- " s " !-->\n"))
230
231 (define (dashed-line thick on off dx dy)
232   (draw-line thick 0 0 dx dy))
233
234 (define (draw-line thick x1 y1 x2 y2)
235   (entity 'line ""
236           '(stroke-linejoin . "round")
237           '(stroke-linecap . "round")
238           `(stroke-width . ,thick)
239           '(stroke . "black")
240           ;;'(fill . "black")
241           `(x1 . ,x1)
242           `(y1 . ,y1)
243           `(x2 . ,x2)
244           `(y2 . ,y2)
245           ))
246
247 ;; WTF is this in every backend?
248 (define (horizontal-line x1 x2 th)
249   (filledbox (- x1) (- x2 x1) (* .5 th) (* .5 th)))
250
251 (define (filledbox breapth width depth height)
252   (round-filled-box breapth width depth height 0))
253
254 (define (named-glyph font name)
255   (dispatch
256    `(fontify ,font ,(entity 'tspan
257                             (integer->entity
258                              (ly:font-glyph-name-to-charcode font name))))))
259
260 (define (placebox x y expr)
261   (entity 'g
262           ;; FIXME -- JCN
263           ;;(dispatch expr)
264           expr
265           `(transform . ,(format #f "translate (~f, ~f)"
266                                  x
267                                  (-  y)))))
268
269 (define (polygon coords blot-diameter)
270   (entity 'polygon ""
271           '(stroke-linejoin . "round")
272           '(stroke-linecap . "round")
273           `(stroke-width . ,blot-diameter)
274           '(stroke . "black")
275           ;;'(fill . "black")
276           `(points . ,(string-join
277                        (map offset->point (ly:list->offsets '() coords))))
278   ))
279
280 (define (round-filled-box breapth width depth height blot-diameter)
281   (entity 'rect ""
282           ;; The stroke will stick out.  To use stroke,
283           ;; the stroke-width must be subtracted from all other dimensions.
284           ;;'(stroke-linejoin . "round")
285           ;;'(stroke-linecap . "round")
286           ;;`(stroke-width . ,blot)
287           ;;'(stroke . "red")
288           ;;'(fill . "orange")
289
290           `(x . ,(- breapth))
291           `(y . ,(- height))
292           `(width . ,(+ breapth width))
293           `(height . ,(+ depth height))
294           `(ry . ,(/ blot-diameter 2))
295           ))
296
297 (define (text font string)
298   (dispatch `(fontify ,font ,(entity 'tspan (string->entities string)))))
299
300 (define (utf8-string pango-font-description string)
301   (dispatch `(fontify ,pango-font-description ,(entity 'tspan string))))