]> git.donarmstrong.com Git - lilypond.git/blob - scm/output-ps.scm
69f277203934985a01979476b56df1803c5a1bae
[lilypond.git] / scm / output-ps.scm
1 ;;;; This file is part of LilyPond, the GNU music typesetter.
2 ;;;;
3 ;;;; Copyright (C) 1998--2015 Jan Nieuwenhuizen <janneke@gnu.org>
4 ;;;;                 Han-Wen Nienhuys <hanwen@xs4all.nl>
5 ;;;;
6 ;;;; LilyPond is free software: you can redistribute it and/or modify
7 ;;;; it under the terms of the GNU General Public License as published by
8 ;;;; the Free Software Foundation, either version 3 of the License, or
9 ;;;; (at your option) any later version.
10 ;;;;
11 ;;;; LilyPond is distributed in the hope that it will be useful,
12 ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 ;;;; GNU General Public License for more details.
15 ;;;;
16 ;;;; You should have received a copy of the GNU General Public License
17 ;;;; along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
18
19 ;;;; Note: currently misused as testbed for titles with markup, see
20 ;;;;       input/test/title-markup.ly
21 ;;;;
22 ;;;; TODO:
23 ;;;;   * %% Papersize in (header ...)
24 ;;;;   * text setting, kerning.
25 ;;;;   * document output-interface
26
27 (define-module (scm output-ps)
28   #:re-export (quote))
29
30 (use-modules (guile)
31              (ice-9 regex)
32              (ice-9 optargs)
33              (srfi srfi-1)
34              (srfi srfi-13)
35              (scm framework-ps)
36              (lily))
37
38 ;;; helper functions, not part of output interface
39 ;;;
40
41
42 ;; ice-9 format uses a lot of memory
43 ;; using simple-format almost halves lilypond cell usage
44
45 (define (str4 num)
46   (if (or (nan? num) (inf? num))
47       (begin
48         (ly:warning (_ "Found infinity or nan in output.  Substituting 0.0"))
49         (if (ly:get-option 'strict-infinity-checking)
50             (exit 1))
51         "0.0")
52       (ly:number->string num)))
53
54 (define (number-pair->string4 numpair)
55   (ly:format "~4l" numpair))
56
57 ;;;
58 ;;; Lily output interface, PostScript implementation --- cleanup and docme
59 ;;;
60
61 (define (char font i)
62   (ly:format "~a (\\~a) show"
63              (ps-font-command font)
64              (ly:inexact->string i 8)))
65
66 (define (circle radius thick fill)
67   (ly:format
68    "~a ~4f ~4f draw_circle"
69    (if fill
70        "true"
71        "false")
72    radius thick))
73
74 (define (start-enclosing-id-node s)
75   "")
76
77 (define (end-enclosing-id-node)
78   "")
79
80 (define (dashed-line thick on off dx dy phase)
81   (ly:format "~4f ~4f ~4f [ ~4f ~4f ] ~4f draw_dashed_line"
82              dx
83              dy
84              thick
85              on
86              off
87              phase))
88
89 (define (draw-line thick x1 y1 x2 y2)
90   (ly:format "~4f ~4f ~4f ~4f ~4f draw_line"
91              (- x2 x1) (- y2 y1)
92              x1 y1 thick))
93
94 (define (partial-ellipse x-radius y-radius start-angle end-angle thick connect fill)
95   (ly:format "~a ~a ~4f ~4f ~4f ~4f ~4f draw_partial_ellipse"
96              (if fill "true" "false")
97              (if connect "true" "false")
98              x-radius
99              y-radius
100              start-angle
101              end-angle
102              thick))
103
104 (define (ellipse x-radius y-radius thick fill)
105   (ly:format
106    "~a ~4f ~4f ~4f draw_ellipse"
107    (if fill
108        "true"
109        "false")
110    x-radius y-radius thick))
111
112 (define (embedded-ps string)
113   string)
114
115 (define (glyph-string pango-font
116                       postscript-font-name
117                       size
118                       cid?
119                       w-x-y-named-glyphs)
120   (define (glyph-spec w h x y g) ; h not used
121     (let ((prefix (if (string? g) "/" "")))
122       (ly:format "~4f ~4f ~4f ~a~a" w x y prefix g)))
123   (define (emglyph-spec w h x y g) ; h not used
124     (if (and (= x 0) (= y 0))
125         (ly:format "currentpoint ~a moveto ~4f 0 rmoveto" g w)
126         (ly:format "currentpoint ~4f ~4f rmoveto ~a moveto ~4f 0 rmoveto" x y g w)))
127   (if cid?
128       (ly:format
129        "/~a /CIDFont findresource ~a output-scale div scalefont setfont\n~a\n~a print_glyphs"
130        postscript-font-name size
131        (string-join (map (lambda (x) (apply glyph-spec x))
132                          (reverse w-x-y-named-glyphs)) "\n")
133        (length w-x-y-named-glyphs))
134       (if (and (ly:bigpdfs) (string-startswith postscript-font-name "Emmentaler"))
135           (ly:format "/~a-O ~a output-scale div selectfont\n~a"
136                      postscript-font-name size
137                      (string-join (map (lambda (x) (apply emglyph-spec x))
138                                        w-x-y-named-glyphs) "\n"))
139           (ly:format "/~a ~a output-scale div selectfont\n~a\n~a print_glyphs"
140                      postscript-font-name size
141                      (string-join (map (lambda (x) (apply glyph-spec x))
142                                        (reverse w-x-y-named-glyphs)) "\n")
143                      (length w-x-y-named-glyphs)))))
144
145 (define (grob-cause offset grob)
146   (if (ly:get-option 'point-and-click)
147       (let* ((cause (ly:grob-property grob 'cause))
148              (music-origin (if (ly:stream-event? cause)
149                                (ly:event-property cause 'origin)))
150              (point-and-click (ly:get-option 'point-and-click)))
151         (if (and
152              (ly:input-location? music-origin)
153              (cond ((boolean? point-and-click) point-and-click)
154                    ((symbol? point-and-click)
155                     (ly:in-event-class? cause point-and-click))
156                    (else (any (lambda (t)
157                                 (ly:in-event-class? cause t))
158                               point-and-click))))
159             (let* ((location (ly:input-file-line-char-column music-origin))
160                    (x-ext (ly:grob-extent grob grob X))
161                    (y-ext (ly:grob-extent grob grob Y)))
162
163               (if (and (< 0 (interval-length x-ext))
164                        (< 0 (interval-length y-ext)))
165                   (ly:format "~4f ~4f ~4f ~4f (textedit://~a:~a:~a:~a) mark_URI\n"
166                              (+ (car offset) (car x-ext))
167                              (+ (cdr offset) (car y-ext))
168                              (+ (car offset) (cdr x-ext))
169                              (+ (cdr offset) (cdr y-ext))
170
171                              ;; Backslashes are not valid
172                              ;; file URI path separators.
173                              (ly:string-percent-encode
174                               (ly:string-substitute "\\" "/" (car location)))
175
176                              (cadr location)
177                              (caddr location)
178                              (1+ (cadddr location)))
179                   ""))
180             ""))
181       ""))
182
183 (define (named-glyph font glyph)
184   (if (and (ly:bigpdfs) (string-startswith (ly:font-file-name font) "emmentaler"))
185       (if (string-endswith (ly:font-file-name font)"-brace")
186           (if (or (string-startswith glyph "brace1") (string-startswith glyph "brace2"))
187               (ly:format "~a ~a" (string-append (ps-font-command font) "-N" ) glyph)
188               (if (or (string-startswith glyph "brace3") (string-startswith glyph "brace4"))
189                   (ly:format "~a ~a" (string-append (ps-font-command font) "-S" ) glyph)
190                   (ly:format "~a ~a" (string-append (ps-font-command font) "-O" ) glyph)))
191           (if (string-startswith glyph "noteheads")
192               (ly:format "~a ~a" (string-append (ps-font-command font) "-N" ) glyph)
193               (if (or (string-startswith glyph "scripts") (string-startswith glyph "clefs"))
194                   (ly:format "~a ~a" (string-append (ps-font-command font) "-S" ) glyph)
195                   (ly:format "~a ~a" (string-append (ps-font-command font) "-O" ) glyph))))
196       (ly:format "~a /~a glyphshow" (ps-font-command font) glyph)))
197
198 (define (no-origin)
199   "")
200
201 (define (placebox x y s)
202   (if (not (string-null? s))
203       (ly:format "~4f ~4f moveto ~a\n" x y s)
204       ""))
205
206 (define (polygon points blot-diameter filled?)
207   (ly:format "~a ~4l ~a ~4f draw_polygon"
208              (if filled? "true" "false")
209              points
210              (- (/ (length points) 2) 1)
211              blot-diameter))
212
213 (define (round-filled-box left right bottom top blotdiam)
214   (let* ((halfblot (/ blotdiam 2))
215          (x (- halfblot left))
216          (width (- right (+ halfblot x)))
217          (y (- halfblot bottom))
218          (height (- top (+ halfblot y))))
219     (ly:format  "~4l draw_round_box"
220                 (list width height x y blotdiam))))
221
222 ;; save current color on stack and set new color
223 (define (setcolor r g b)
224   (ly:format "gsave ~4l setrgbcolor\n"
225              (list r g b)))
226
227 ;; restore color from stack
228 (define (resetcolor) "grestore\n")
229
230 ;; rotation around given point
231 (define (setrotation ang x y)
232   (ly:format "gsave ~4l translate ~a rotate ~4l translate\n"
233              (list x y)
234              ang
235              (list (* -1 x) (* -1 y))))
236
237 (define (resetrotation ang x y)
238   "grestore  ")
239
240 (define (unknown)
241   "\n unknown\n")
242
243 (define (url-link url x y)
244   (ly:format "~a ~a currentpoint vector_add  ~a ~a currentpoint vector_add (~a) mark_URI"
245              (car x)
246              (car y)
247              (cdr x)
248              (cdr y)
249              url))
250
251 (define (page-link page-no x y)
252   (if (number? page-no)
253       (ly:format "~a ~a currentpoint vector_add  ~a ~a currentpoint vector_add ~a mark_page_link"
254                  (car x)
255                  (car y)
256                  (cdr x)
257                  (cdr y)
258                  page-no)
259       ""))
260
261 (define* (path thickness exps #:optional (cap 'round) (join 'round) (fill? #f))
262   (define (convert-path-exps exps)
263     (if (pair? exps)
264         (let*
265             ((head (car exps))
266              (rest (cdr exps))
267              (arity
268               (cond
269                ((memq head '(rmoveto rlineto lineto moveto)) 2)
270                ((memq head '(rcurveto curveto)) 6)
271                ((eq? head 'closepath) 0)
272                (else 1)))
273              (args (take rest arity))
274              )
275
276           ;; WARNING: this is a vulnerability: a user can output arbitrary PS code here.
277           (cons (ly:format
278                  "~l ~a "
279                  args
280                  head)
281                 (convert-path-exps (drop rest arity))))
282         '()))
283
284   (let ((cap-numeric (case cap ((butt) 0) ((round) 1) ((square) 2)
285                            (else (begin
286                                    (ly:warning (_ "unknown line-cap-style: ~S")
287                                                (symbol->string cap))
288                                    1))))
289         (join-numeric (case join ((miter) 0) ((round) 1) ((bevel) 2)
290                             (else (begin
291                                     (ly:warning (_ "unknown line-join-style: ~S")
292                                                 (symbol->string join))
293                                     1)))))
294      (ly:format
295       "gsave currentpoint translate
296 ~a setlinecap ~a setlinejoin ~a setlinewidth
297 ~l ~a grestore"
298       cap-numeric
299       join-numeric
300       thickness
301       (convert-path-exps exps)
302       ;; print outline contour only if there is no fill or if
303       ;; contour is explicitly requested with a thickness > 0
304       (cond ((not fill?) "stroke")
305             ((positive? thickness) "gsave stroke grestore fill")
306             (else "fill")))))
307
308
309 (define (setscale x y)
310   (ly:format "gsave ~4l scale\n"
311              (list x y)))
312
313 (define (resetscale)
314   "grestore\n")