]> git.donarmstrong.com Git - lilypond.git/blob - scm/output-svg.scm
* scm/output-svg.scm: Font fixes. Sodipodi now groks svg
[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--2004 Jan Nieuwenhuizen <janneke@gnu.org>
6
7 ;;;; http://www.w3.org/TR/SVG11
8
9 ;;; FIXME
10
11 ;;; * sodipodi gets confuseed by dashes in font names.
12 ;;;
13 ;;;   removing feta-nummer*.pfa (LilyPond-feta-nummer),
14 ;;;   feta-braces*.pfa (LilyPond-feta-braces), feta-din*.pfa
15 ;;;   (LilyPond-feta-din) from font path shows feta fonts in sodipodi.
16 ;;;
17 ;;; * inkscape fails to map Feta fonts to private use area (PUA) E000
18 ;;;   (sodipodi is fine).
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
29 ;; GLobals
30 ;; FIXME: 2?
31 (define output-scale (* 2 scale-to-unit))
32 (define line-thickness 0)
33
34 (define (stderr string . rest)
35   (apply format (cons (current-error-port) (cons string rest)))
36   (force-output (current-error-port)))
37
38 (define (debugf string . rest)
39   (if #f
40       (apply stderr (cons string rest))))
41
42
43 (define (dispatch expr)
44   (let ((keyword (car expr)))
45     (cond
46      ((eq? keyword 'some-func) "")
47      ;;((eq? keyword 'placebox) (dispatch (cadddr expr)))
48      (else
49       (if (module-defined? this-module keyword)
50           (apply (eval keyword this-module) (cdr expr))
51           (begin
52             (display
53              (string-append "undefined: " (symbol->string keyword) "\n"))
54             ""))))))
55   
56 ;; Helper functions
57 (define (tagify tag string . attribute-alist)
58   (string-append
59    "<"
60    tag
61    (apply string-append
62           (map (lambda (x)
63                  (string-append " " (symbol->string (car x)) "='" (cdr x) "'"))
64                attribute-alist))
65    ">"
66    string "</" tag ">\n"))
67
68 (define (control->list c)
69   (list (car c) (cdr c)))
70
71 (define (control->string c)
72   (string-append
73    (number->string (car c)) ","
74    ;; lose the -1
75    (number->string (* -1 (cdr c))) " "))
76
77 (define (control-flip-y c)
78   (cons (car c) (* -1 (cdr c))))
79
80 (define (ly:numbers->string l)
81   (string-append
82    (number->string (car l))
83    (if (null? (cdr l))
84        ""
85        (string-append "," (ly:numbers->string (cdr l))))))
86
87 (define (svg-bezier l close)
88   (let* ((c0 (car (list-tail l 3)))
89          (c123 (list-head l 3)))
90     (string-append
91      (if (not close) "M " "L ")
92      (control->string c0)
93      "C " (apply string-append (map control->string c123))
94      (if (not close) "" (string-append
95                          "L " (control->string close))))));; " Z")))))
96
97
98 (define (sqr x)
99   (* x x))
100
101 (define (fontify font expr)
102    (tagify "text" expr (cons 'style (svg-font font))))
103 ;;         (cons 'unicode-range "U+EE00-EEFF"))))
104
105 (define (font-family font)
106   (let ((name (ly:font-name font)))
107     (if name
108         (regexp-substitute/global #f "^GNU-(.*)-[.0-9]*$" name 'pre 1 'post)
109         (begin
110           (stderr "font-name: ~S\n" (ly:font-name font))
111           ;; TODO s/filename/file-name/
112           (stderr "font-filename: ~S\n" (ly:font-filename font))
113           (stderr "font-size: ~S\n" (font-size font))
114           "ecrm12"))))
115
116 (define (font-size font)
117   (let* ((designsize (ly:font-design-size font))
118          (magnification (* (ly:font-magnification font)))
119          (ops 2)
120          (scaling (* ops magnification designsize)))
121     (debugf "scaling:~S\n" scaling)
122     (debugf "magnification:~S\n" magnification)
123     (debugf "design:~S\n" designsize)
124     scaling))
125
126 (define (integer->entity i)
127   (format #f "&#x~x;" i))
128
129 (define (char->entity font c)
130   (define font-name-base-alist
131     `(("LilyPond-feta" . ,(- #xe000 #x20))
132       ("LilyPond-feta-braces-a" . ,(- #xe000 #x40))
133       ("LilyPond-feta-braces-b" . ,(- #xe000 #x40))
134       ("LilyPond-feta-braces-c" . ,(- #xe000 #x40))
135       ("LilyPond-feta-braces-d" . ,(- #xe000 #x40))
136       ("LilyPond-feta-braces-d" . ,(- #xe000 #x40))
137       ("LilyPond-feta-braces-e" . ,(- #xe000 #x40))
138       ("LilyPond-feta-braces-f" . ,(- #xe000 #x40))
139       ("LilyPond-feta-braces-g" . ,(- #xe000 #x40))
140       ("LilyPond-feta-braces-h" . ,(- #xe000 #x40))
141       ("LilyPond-feta-braces-i" . ,(- #xe000 #x40))
142       ("LilyPond-parmesan" . ,(- #xe000 #x20))))
143
144   (integer->entity (+ (assoc-get (font-family font) font-name-base-alist 0)
145                       (char->integer c))))
146
147 (define (string->entities font string)
148   (apply string-append
149          (map (lambda (x) (char->entity font x)) (string->list string))))
150
151 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
152
153
154 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
155 ;;; stencil outputters
156 ;;;
157
158 ;;; catch-all for missing stuff
159 ;;; comment this out to see find out what functions you miss :-)
160 (define (dummy . foo) "")
161 (map (lambda (x) (module-define! this-module x dummy))
162      (append
163       (ly:all-stencil-expressions)
164       (ly:all-output-backend-commands)))
165
166 (define (beam width slope thick blot)
167   (let* ((x width)
168          (y (* slope width))
169          (z (sqrt (+ (sqr x) (sqr y)))))
170     (tagify "rect" ""
171             `(style . ,(format "fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-opacity:1;stroke-width:~f;stroke-linejoin:round;stroke-linecap:round;" line-thickness))
172             `(x . "0")
173             `(y . ,(number->string (* output-scale (- 0 (/ thick 2)))))
174             `(width . ,(number->string (* output-scale width)))
175             `(height . ,(number->string (* output-scale thick)))
176             ;;`(ry . ,(number->string (* output-scale half-lt)))
177             `(ry . ,(number->string (* output-scale (/ line-thickness 2))))
178             `(transform .
179                         ,(format #f "matrix (~f, ~f, 0, 1, 0, 0) scale (~f, ~f)"
180                                  (/ x z)
181                                  (* -1 (/ y z))
182                                  1 1)))))
183
184 (define (bezier-sandwich l thick)
185   (let* (;;(l (eval urg-l this-module))
186          (first (list-tail l 4))
187          (first-c0 (car (list-tail first 3)))
188          (second (list-head l 4)))
189     (tagify "path" ""
190             `(stroke . "#000000")
191             `(stroke-width . ,(number->string line-thickness))
192             `(transform . ,(format #f "scale (~f, ~f)"
193                                    output-scale output-scale))
194             `(d . ,(string-append (svg-bezier first #f)
195                                   (svg-bezier second first-c0))))))
196
197 (define (char font i)
198   (dispatch
199    `(fontify ,font ,(tagify "tspan" (char->entity font (integer->char i))))))
200
201 (define (comment s)
202   (string-append "<!-- " s " !-->\n"))
203
204 (define (filledbox breapth width depth height)
205   (round-filled-box breapth width depth height line-thickness))
206
207 (define (lily-def key val)
208   (cond
209    ((equal? key "lilypondpaperoutputscale")
210     ;; ugr
211     ;; If we just use transform scale (output-scale),
212     ;; all fonts come out scaled too (ie, much too big)
213     ;; So, we manually scale all other stuff.
214     (set! output-scale (* scale-to-unit (string->number val))))
215    ((equal? key "lilypondpaperlinethickness")
216     (set! line-thickness (* scale-to-unit (string->number val)))))
217   "")
218
219 (define (placebox x y expr)
220   (tagify "g"
221           ;; FIXME -- JCN
222           ;;(dispatch expr)
223           expr
224           `(transform . ,(format #f "translate (~f, ~f)"
225                                  (* output-scale x)
226                                  (- 0 (* output-scale y))))))
227
228 (define (round-filled-box breapth width depth height blot-diameter)
229   (tagify "rect" ""
230             `(style . ,(format "fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-opacity:1;stroke-width:~f;stroke-linejoin:miter;stroke-linecap:butt;" line-thickness))
231           `(x . ,(number->string (* output-scale (- 0 breapth))))
232           `(y . ,(number->string (* output-scale (- 0 height))))
233           `(width . ,(number->string (* output-scale (+ breapth width))))
234           `(height . ,(number->string (* output-scale (+ depth height))))
235           ;;`(ry . ,(number->string (* output-scale half-lt)))
236           `(ry . ,(number->string (/ blot-diameter 2)))))
237
238 (define (svg-font font)
239    (format #f "font-family:~a;font-size:~a;fill:black;text-anchor:start;"
240            (font-family font) (font-size font)))
241
242 (define (text font string)
243   (dispatch `(fontify ,font ,(tagify "tspan" (string->entities font string)))))
244
245 ;; WTF is this in every backend?
246 (define (horizontal-line x1 x2 th)
247   (filledbox (- x1) (- x2 x1) (* .5 th) (* .5 th)))