]> git.donarmstrong.com Git - lilypond.git/blob - lily/paper-book.cc
(LY_DEFINE): ly:output-formats. New function.
[lilypond.git] / lily / paper-book.cc
1 /*
2   paper-book.cc -- implement Paper_book
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 2004 Jan Nieuwenhuizen <janneke@gnu.org>
7 */
8
9 #include "ly-module.hh"
10 #include "main.hh"
11 #include "page.hh"
12 #include "paper-book.hh"
13 #include "output-def.hh"
14 #include "paper-outputter.hh"
15 #include "paper-line.hh"
16 #include "paper-score.hh"
17 #include "stencil.hh"
18 #include "warn.hh"
19
20
21 /*
22   Ugh. the Function of the Paper_book class is unclear. Trim this
23   file.
24   
25  */
26
27
28 Paper_book::Paper_book ()
29 {
30   pages_ = SCM_EOL;
31   lines_ = SCM_EOL;
32   copyright_ = SCM_EOL;
33   tagline_ = SCM_EOL;
34   header_ = SCM_EOL;
35   
36   bookpaper_ = 0;
37   smobify_self ();
38 }
39
40 Paper_book::~Paper_book ()
41 {
42 }
43
44 #include "ly-smobs.icc"
45
46 IMPLEMENT_DEFAULT_EQUAL_P (Paper_book);
47 IMPLEMENT_SMOBS (Paper_book)
48 IMPLEMENT_TYPE_P (Paper_book, "ly:paper-book?")
49
50 SCM
51 Paper_book::mark_smob (SCM smob)
52 {
53   Paper_book *b = (Paper_book*) SCM_CELL_WORD_1 (smob);
54   for (int i = 0; i < b->score_lines_.size (); i++)
55     b->score_lines_[i].gc_mark ();
56
57   scm_gc_mark (b->copyright_);
58   if (b->bookpaper_)
59     scm_gc_mark (b->bookpaper_->self_scm ());
60   scm_gc_mark (b->header_);
61   scm_gc_mark (b->pages_);
62   scm_gc_mark (b->lines_);
63   return b->tagline_;
64 }
65
66 int
67 Paper_book::print_smob (SCM smob, SCM port, scm_print_state*)
68 {
69   Paper_book *b = (Paper_book*) ly_cdr (smob);
70      
71   scm_puts ("#<", port);
72   scm_puts (classname (b), port);
73   scm_puts (" ", port);
74   //scm_puts (b->, port);
75   scm_puts (">", port);
76   return 1;
77 }
78
79 Array<String>
80 split_string (String s, char c)
81 {
82   Array<String> rv; 
83   while (s.length ())
84     {
85       int i = s.index (c);
86       
87       if (i == 0)
88         {
89           s = s.nomid_string (0, 1);
90           continue;
91         }
92       
93       if (i < 0)
94         i = s.length () ;
95
96       rv.push (s.cut_string (0, i));
97       s = s.nomid_string (0, i);
98     }
99
100   return rv;
101 }
102
103 SCM
104 dump_fields ()
105 {
106   SCM fields = SCM_EOL;
107   for (int i = dump_header_fieldnames_global.size (); i--; )
108     fields
109       = scm_cons (ly_symbol2scm (dump_header_fieldnames_global[i].to_str0 ()),
110                   fields);
111   return fields;
112 }
113
114 LY_DEFINE(ly_output_formats, "ly:output-formats",
115           0, 0, 0, (),
116           "Formats passed to --format as a list of strings, "
117           "used for the output.")
118 {
119   Array<String> output_formats = split_string (output_format_global, ',');
120
121   SCM l = SCM_EOL;
122   for (int i = 0; i < output_formats.size (); i ++)
123     {
124       l = scm_cons (scm_makfrom0str  (output_formats[i].to_str0 ()), l); 
125     }
126
127   return l; 
128 }
129           
130
131
132 /*
133   TODO: there is too much code dup, and the interface is not
134   clear. FIXME.
135  */
136 void
137 Paper_book::output (String outname)
138 {
139   if (!score_lines_.size ())
140     return;
141     
142   /* Generate all stencils to trigger font loads.  */
143   pages ();
144
145   
146   SCM formats = ly_output_formats();
147   for (SCM s = formats; ly_c_pair_p (s); s = ly_cdr (s)) 
148     {
149       
150       String format = ly_scm2string (ly_car (s));
151       
152       Paper_outputter *out = get_paper_outputter (outname + "." + format, format);
153
154   
155       SCM scopes = SCM_EOL;
156       if (ly_c_module_p (header_))
157         scopes = scm_cons (header_, scopes);
158   
159       String func_nm = format;
160       func_nm = "output-framework-" + func_nm;
161         
162       SCM func = ly_scheme_function (func_nm.to_str0 ());
163       scm_apply_0 (func, scm_list_n (out->self_scm (),
164                                      self_scm (),
165                                      scopes,
166                                      dump_fields (),
167                                      scm_makfrom0str (outname.to_str0 ()),
168                                      SCM_UNDEFINED
169                                      )) ;
170
171       scm_gc_unprotect_object (out->self_scm ());
172     }
173 }
174
175
176 void
177 Paper_book::classic_output (String outname)
178 {
179
180   /* Generate all stencils to trigger font loads.  */
181   lines ();
182
183
184   // ugh code dup
185   SCM scopes = SCM_EOL;
186   if (ly_c_module_p (header_))
187     scopes = scm_cons (header_, scopes);
188
189   if (ly_c_module_p (score_lines_[0].header_))
190     scopes = scm_cons (score_lines_[0].header_, scopes);
191   //end ugh
192
193
194   Array<String> output_formats = split_string (output_format_global, ',');
195
196   for (int i = 0; i < output_formats.size (); i++)
197     {
198       String format = output_formats[i];
199       String func_nm = format;
200       func_nm = "output-classic-framework-" + func_nm;
201       
202       SCM func = ly_scheme_function (func_nm.to_str0 ());
203
204       Paper_outputter *out = get_paper_outputter (outname + "." + format, format);
205
206       scm_apply_0 (func, scm_list_n (out->self_scm (),
207                                      self_scm (),
208                                      scopes,
209                                      dump_fields (),
210                                      scm_makfrom0str (outname.to_str0 ()),
211                                      SCM_UNDEFINED
212                                      )) ;
213
214       scm_gc_unprotect_object (out->self_scm ());
215     }
216   
217   progress_indication ("\n");
218 }
219
220
221
222
223 LY_DEFINE(ly_paper_book_pages, "ly:paper-book-pages",
224           1,0,0,
225           (SCM pb),
226           "Return pages in book PB.")
227 {
228   return unsmob_paper_book(pb)->pages ();
229 }
230
231
232 LY_DEFINE(ly_paper_book_lines, "ly:paper-book-lines",
233           1,0,0,
234           (SCM pb),
235           "Return lines in book PB.")
236 {
237   return unsmob_paper_book (pb)->lines ();
238 }
239
240
241 LY_DEFINE(ly_paper_book_book_paper, "ly:paper-book-book-paper",
242           1,0,0,
243           (SCM pb),
244           "Return pages in book PB.")
245 {
246   return unsmob_paper_book(pb)->bookpaper_->self_scm ();
247 }
248
249 /*
250
251 TODO: resurrect more complex user-tweaks for titling .
252
253 */
254 Stencil
255 Paper_book::book_title ()
256 {
257   SCM title_func = bookpaper_->lookup_variable (ly_symbol2scm ("book-title"));
258   Stencil title;
259
260   SCM scopes = SCM_EOL;
261   if (ly_c_module_p (header_))
262     scopes = scm_cons (header_, scopes);
263
264  
265   SCM tit = SCM_EOL;
266   if (ly_c_procedure_p (title_func))
267     tit = scm_call_2 (title_func,
268                      bookpaper_->self_scm (),
269                      scopes);
270
271   if (unsmob_stencil (tit))
272     title = *unsmob_stencil (tit);
273
274   if (!title.is_empty ())
275     title.align_to (Y_AXIS, UP);
276   
277   return title;
278 }
279
280   
281
282 Stencil
283 Paper_book::score_title (int i)
284 {
285   SCM title_func = bookpaper_->lookup_variable (ly_symbol2scm ("score-title"));
286
287   Stencil title;
288
289   // ugh code dup
290   SCM scopes = SCM_EOL;
291   if (ly_c_module_p (header_))
292     scopes = scm_cons (header_, scopes);
293
294   if (ly_c_module_p (score_lines_[i].header_))
295     scopes = scm_cons (score_lines_[i].header_, scopes);
296   //end ugh
297
298   SCM tit = SCM_EOL;
299   if (ly_c_procedure_p (title_func))
300     tit =scm_call_2 (title_func,
301                      bookpaper_->self_scm (),
302                      scopes);
303
304   if (unsmob_stencil (tit))
305     title = *unsmob_stencil (tit);
306
307
308   if (!title.is_empty ())
309     title.align_to (Y_AXIS, UP);
310   
311   return title;
312 }
313
314   
315
316 SCM
317 Paper_book::lines ()
318 {
319   if (ly_c_pair_p (lines_))
320     return lines_;
321
322   Stencil title = book_title ();      
323   if (!title.is_empty ())
324     {
325       Paper_line *pl = new Paper_line (title, -10001, true);
326       
327       lines_ = scm_cons (pl->self_scm (), lines_);
328       scm_gc_unprotect_object (pl->self_scm ());
329     }
330   
331   int score_count = score_lines_.size ();
332   for (int i = 0; i < score_count; i++)
333     {
334       Stencil title = score_title (i);      
335       if (!title.is_empty ())
336         {
337           Paper_line *pl = new Paper_line (title, -10001, true);
338           lines_ = scm_cons (pl->self_scm (), lines_);
339           scm_gc_unprotect_object (pl->self_scm ());
340         }
341       
342       if (scm_vector_p (score_lines_[i].lines_) == SCM_BOOL_T)
343         {
344           SCM line_list = scm_vector_to_list (score_lines_[i].lines_); // guh.
345           lines_ = scm_append (scm_list_2 (scm_reverse (line_list), lines_));
346         }
347     }
348   
349   lines_ = scm_reverse (lines_);
350
351   int i = 0;
352   for (SCM s = lines_; s != SCM_EOL; s = ly_cdr (s))
353     unsmob_paper_line (ly_car (s))->number_ = ++i;
354   return lines_;
355 }
356
357
358 SCM
359 make_tagline (Output_def*paper, SCM scopes)
360 {
361   SCM make_tagline = paper->c_variable ("make-tagline");
362   SCM tagline = scm_call_2 (make_tagline, paper->self_scm (), scopes);
363   return tagline;
364 }
365
366 SCM
367 make_copyright (Output_def *paper, SCM scopes)
368 {
369   SCM make_copyright = paper->c_variable ("make-copyright");
370   SCM  copyright = scm_call_2 (make_copyright, paper->self_scm (), scopes);
371   return copyright;
372 }
373
374 SCM
375 Paper_book::pages ()
376 {
377   if (ly_c_pair_p (pages_))
378     return pages_;
379
380   Output_def *paper = bookpaper_;
381   Page *page = new Page (paper, 1);
382
383   Real text_height = page->text_height ();
384
385   Real copy_height = 0;
386   if (Stencil *s = unsmob_stencil (copyright_))
387     copy_height = s->extent (Y_AXIS).length ();
388
389   Real tag_height = 0;
390   if (Stencil *s = unsmob_stencil (tagline_))
391     tag_height = s->extent (Y_AXIS).length ();
392
393   SCM all = lines ();
394   SCM proc = paper->c_variable ("page-breaking");
395   SCM breaks = scm_apply_0 (proc, scm_list_n (all,
396                                               self_scm (),
397                                               scm_make_real (text_height),
398                                               scm_make_real (-copy_height),
399                                               scm_make_real (-tag_height),
400                                               SCM_UNDEFINED));
401
402
403   /*
404     UGH - move this out of C++.
405    */
406   SCM scopes = SCM_EOL;
407   if (ly_c_module_p (header_))
408     scopes = scm_cons (header_, scopes);
409   
410   tagline_ = make_tagline (bookpaper_, scopes);
411   copyright_ = make_tagline (bookpaper_, scopes);
412
413   int page_count = SCM_VECTOR_LENGTH ((SCM) breaks);
414   int line = 1;
415
416   for (int i = 0; i < page_count; i++)
417     {
418       if (i)
419         page = new Page (paper, i + 1);
420
421       int next = i + 1 < page_count
422         ? ly_scm2int (scm_vector_ref (breaks, scm_int2num (i))) : 0;
423       while ((!next && all != SCM_EOL) || line <= next)
424         {
425           SCM s = ly_car (all);
426           page->lines_ = ly_snoc (s, page->lines_);
427           page->height_ += unsmob_paper_line (s)->dim ()[Y_AXIS];
428           page->line_count_++;
429           all = ly_cdr (all);
430           line++;
431         }
432       if (i == page_count-1)
433         page->is_last_ = true;
434       
435       pages_ = scm_cons (page->self_scm (), pages_);
436     }
437
438   pages_ =  scm_reverse (pages_);
439   return pages_;
440 }
441
442
443
444 static SCM
445 c_ragged_page_breaks (SCM lines,
446                       Paper_book *book,
447                       Real text_height,
448                       Real first, Real last)
449 {
450   int page_number = 0;
451
452   Real book_height =0.;
453   for (SCM s = lines ; ly_c_pair_p (s);  s = ly_cdr (s))
454     {
455       book_height += unsmob_paper_line (ly_car (s))->dim ()[Y_AXIS];
456     }
457
458   /*
459     UGH. following stuff should go out of C++.
460    */
461   SCM scopes = SCM_EOL;
462   if (ly_c_module_p (book->header_))
463     scopes = scm_cons (book->header_, scopes);
464   
465
466   SCM tag = make_tagline (book->bookpaper_, scopes);
467   if (unsmob_stencil (tag))
468     {
469       book_height += unsmob_stencil (tag)->extent (Y_AXIS).length ();
470     }
471
472   SCM cr = make_copyright (book->bookpaper_, scopes);
473   if (unsmob_stencil (cr))
474     {
475       book_height += unsmob_stencil (cr)->extent (Y_AXIS).length ();
476     }
477
478   int page_count = int (book_height / text_height + 0.5); // ceil?
479   SCM breaks = SCM_EOL;
480   Real page_height = text_height + first;
481   Real h = 0;
482   int number = 0;
483   for (SCM s = lines; ly_c_pair_p (s); s = ly_cdr (s))
484     {
485       Paper_line *pl = unsmob_paper_line (ly_car (s));
486       if (!pl->is_title () && h < page_height)
487         number++;
488       h += pl->dim ()[Y_AXIS];
489       if (!pl->is_title () && h > page_height)
490         {
491           breaks = ly_snoc (scm_int2num (number), breaks);
492           page_number++;
493           page_height = text_height + (page_number == page_count) * last;
494           h = 0;
495         }
496       if (ly_cdr (s) == SCM_EOL)
497         breaks = ly_snoc (scm_int2num (pl->number_), breaks);
498     }
499
500   return scm_vector (breaks);
501 }
502
503 LY_DEFINE (ly_ragged_page_breaks, "ly:ragged-page-breaks",
504            5, 0, 0, (SCM lines, SCM book, SCM text, SCM first, SCM last),
505            "Return a vector with line numbers of page breaks.")
506 {
507   Paper_book* b = unsmob_paper_book (book);
508
509   SCM_ASSERT_TYPE (scm_pair_p (lines), lines, SCM_ARG1, __FUNCTION__, "list");
510   SCM_ASSERT_TYPE (b, book, SCM_ARG2, __FUNCTION__, "Paper_book");
511   SCM_ASSERT_TYPE (ly_c_number_p (text), text, SCM_ARG3, __FUNCTION__, "number");
512   SCM_ASSERT_TYPE (ly_c_number_p (first), first, SCM_ARG4, __FUNCTION__, "number");
513   SCM_ASSERT_TYPE (ly_c_number_p (last), last, SCM_ARG5, __FUNCTION__, "number");
514
515   return c_ragged_page_breaks (lines, b,
516                                ly_scm2double (text),
517                                ly_scm2double (first), ly_scm2double (last));
518 }
519
520 /****************************************************************/
521
522 Score_lines::Score_lines ()
523 {
524   lines_ = SCM_EOL;
525   header_ = SCM_EOL;
526 }
527
528 void
529 Score_lines::gc_mark ()
530 {
531   scm_gc_mark (lines_);
532   scm_gc_mark (header_);
533 }
534