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