]> git.donarmstrong.com Git - lilypond.git/blob - lily/paper-book.cc
Merge branch 'master' into lilypond/translation
[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--2009 Jan Nieuwenhuizen <janneke@gnu.org>
7 */
8
9 #include "paper-book.hh"
10
11 #include "grob.hh"
12 #include "international.hh"
13 #include "main.hh"
14 #include "output-def.hh"
15 #include "paper-column.hh"
16 #include "paper-score.hh"
17 #include "paper-system.hh"
18 #include "text-interface.hh"
19 #include "warn.hh"
20 #include "program-option.hh"
21 #include "page-marker.hh"
22
23 #include "ly-smobs.icc"
24
25 Paper_book::Paper_book ()
26 {
27   header_ = SCM_EOL;
28   header_0_ = SCM_EOL;
29   pages_ = SCM_BOOL_F;
30   scores_ = SCM_EOL;
31   bookparts_ = SCM_EOL;
32   performances_ = SCM_EOL;
33   systems_ = SCM_BOOL_F;
34
35   paper_ = 0;
36   parent_ = 0;
37   smobify_self ();
38 }
39
40 Paper_book::~Paper_book ()
41 {
42 }
43
44 IMPLEMENT_DEFAULT_EQUAL_P (Paper_book);
45 IMPLEMENT_SMOBS (Paper_book);
46 IMPLEMENT_TYPE_P (Paper_book, "ly:paper-book?");
47
48 SCM
49 Paper_book::mark_smob (SCM smob)
50 {
51   Paper_book *b = (Paper_book *) SCM_CELL_WORD_1 (smob);
52   if (b->paper_)
53     scm_gc_mark (b->paper_->self_scm ());
54   if (b->parent_)
55     scm_gc_mark (b->parent_->self_scm ());
56   scm_gc_mark (b->header_);
57   scm_gc_mark (b->header_0_);
58   scm_gc_mark (b->pages_);
59   scm_gc_mark (b->performances_);
60   scm_gc_mark (b->scores_);
61   scm_gc_mark (b->bookparts_);
62   return b->systems_;
63 }
64
65 int
66 Paper_book::print_smob (SCM smob, SCM port, scm_print_state *)
67 {
68   Paper_book *b = (Paper_book *) SCM_CELL_WORD_1 (smob);
69   (void)b;
70   scm_puts ("#<Paper_book>", port);
71   return 1;
72 }
73
74 Output_def *
75 Paper_book::top_paper ()
76 {
77   Output_def *paper = paper_;
78   while (paper->parent_)
79     paper = paper->parent_;
80   return paper;
81 }
82
83 SCM
84 dump_fields ()
85 {
86   SCM fields = SCM_EOL;
87   for (vsize i = dump_header_fieldnames_global.size (); i--;)
88     fields
89       = scm_cons (ly_symbol2scm (dump_header_fieldnames_global[i].c_str ()),
90                   fields);
91   return fields;
92 }
93
94 void
95 Paper_book::add_score (SCM s)
96 {
97   scores_ = scm_cons (s, scores_);
98 }
99
100 void
101 Paper_book::add_bookpart (SCM p)
102 {
103   bookparts_ = scm_cons (p, bookparts_);
104 }
105
106 void
107 Paper_book::add_performance (SCM s)
108 {
109   performances_ = scm_cons (s, performances_);
110 }
111
112 int
113 Paper_book::output_aux (SCM output_channel,
114                         bool is_last,
115                         int *first_page_number,
116                         int *first_performance_number)
117 {
118   int page_nb = 0;
119   if (scm_is_pair (performances_))
120     {
121       SCM proc = ly_lily_module_constant ("write-performances-midis");
122  
123       scm_call_3 (proc,
124                   performances (),
125                   output_channel,
126                   scm_long2num (*first_performance_number));
127       *first_performance_number += scm_ilength (performances_);
128     }
129
130   if (scm_is_pair (bookparts_))
131     {
132       for (SCM p = bookparts_; scm_is_pair (p); p = scm_cdr (p))
133         if (Paper_book *pbookpart = unsmob_paper_book (scm_car (p)))
134           {
135             bool is_last_part = (is_last && !scm_is_pair (scm_cdr (p)));
136             page_nb += pbookpart->output_aux (output_channel,
137                                               is_last_part,
138                                               first_page_number,
139                                               first_performance_number);
140           }
141     }
142   else
143     {
144       if (scores_ == SCM_EOL)
145         return 0;
146       paper_->set_variable (ly_symbol2scm ("first-page-number"),
147                             scm_long2num (*first_page_number));
148       paper_->set_variable (ly_symbol2scm ("is-last-bookpart"),
149                             ly_bool2scm (is_last));
150       /* Generate all stencils to trigger font loads.  */
151       page_nb = scm_ilength (pages ());
152       *first_page_number += page_nb;
153     }
154   return page_nb;
155 }
156
157 void
158 Paper_book::output (SCM output_channel)
159 {
160   int first_page_number
161     = robust_scm2int (paper_->c_variable ("first-page-number"), 1);
162   int first_performance_number = 0;
163
164   /* FIXME: We need a line-width for ps output (framework-ps.scm:92).
165      If we don't have any, we take the paper-width unless we know
166      better which line-width to choose (e.g. if there are \bookparts
167      with different line-widths) and why we need it at all.
168   */
169
170   if (paper_->c_variable ("line-width") == SCM_UNDEFINED)
171     paper_->set_variable (ly_symbol2scm ("line-width"),
172                           paper_->c_variable ("paper-width"));
173  
174   if (!output_aux (output_channel,
175                    true,
176                    &first_page_number,
177                    &first_performance_number))
178     return;
179
180   SCM scopes = SCM_EOL;
181   if (ly_is_module (header_))
182     scopes = scm_cons (header_, scopes);
183
184   string mod_nm = "scm framework-" + get_output_backend_name ();
185
186   SCM mod = scm_c_resolve_module (mod_nm.c_str ());
187
188   if (get_program_option ("print-pages"))
189     {
190       SCM framework = ly_module_lookup (mod,
191                                         ly_symbol2scm ("output-framework"));
192
193       if (framework != SCM_BOOL_F)
194         {
195           SCM func = scm_variable_ref (framework);
196           scm_apply_0 (func, scm_list_n (output_channel,
197                                          self_scm (),
198                                          scopes,
199                                          dump_fields (),
200                                          SCM_UNDEFINED));
201         }
202       else
203         warning (_f ("program option -dprint-pages not supported by backend `%s'",
204                      get_output_backend_name ()));
205     }
206
207   if (get_program_option ("preview"))
208     {
209       SCM framework
210         = ly_module_lookup (mod, ly_symbol2scm ("output-preview-framework"));
211
212       if (framework != SCM_BOOL_F)
213         {
214           SCM func = scm_variable_ref (framework);
215           scm_apply_0 (func, scm_list_n (output_channel,
216                                          self_scm (),
217                                          scopes,
218                                          dump_fields (),
219                                          SCM_UNDEFINED));
220         }
221       else
222         warning (_f ("program option -dpreview not supported by backend `%s'",
223                      get_output_backend_name ()));
224     }
225 }
226
227 void
228 Paper_book::classic_output_aux (SCM output,
229                                 int *first_performance_number)
230 {
231   if (scm_is_pair (performances_))
232     {
233       SCM proc = ly_lily_module_constant ("write-performances-midis");
234       scm_call_3 (proc,
235                   performances (),
236                   output,
237                   scm_long2num (*first_performance_number));
238       *first_performance_number += scm_ilength (performances_);
239     }
240   
241   /* Generate all stencils to trigger font loads.  */
242   systems ();
243 }
244
245 void
246 Paper_book::classic_output (SCM output)
247 {
248   int first_performance_number = 0;
249   classic_output_aux (output, &first_performance_number);
250
251   SCM scopes = SCM_EOL;
252   if (ly_is_module (header_))
253     scopes = scm_cons (header_, scopes);
254
255   if (ly_is_module (header_0_))
256     scopes = scm_cons (header_0_, scopes);
257
258   string format = get_output_backend_name ();
259   string mod_nm = "scm framework-" + format;
260
261   SCM mod = scm_c_resolve_module (mod_nm.c_str ());
262   SCM func = scm_c_module_lookup (mod, "output-classic-framework");
263
264   func = scm_variable_ref (func);
265   scm_apply_0 (func, scm_list_n (output,
266                                  self_scm (),
267                                  scopes,
268                                  dump_fields (),
269                                  SCM_UNDEFINED));
270
271   progress_indication ("\n");
272 }
273
274 /* TODO: resurrect more complex user-tweaks for titling?  */
275 Stencil
276 Paper_book::book_title ()
277 {
278   SCM title_func = paper_->lookup_variable (ly_symbol2scm ("book-title"));
279   Stencil title;
280
281   SCM scopes = SCM_EOL;
282   if (ly_is_module (header_))
283     scopes = scm_cons (header_, scopes);
284
285   SCM tit = SCM_EOL;
286   if (ly_is_procedure (title_func))
287     tit = scm_call_2 (title_func,
288                       paper_->self_scm (),
289                       scopes);
290
291   if (unsmob_stencil (tit))
292     title = *unsmob_stencil (tit);
293
294   if (!title.is_empty ())
295     title.align_to (Y_AXIS, UP);
296
297   return title;
298 }
299
300 Stencil
301 Paper_book::score_title (SCM header)
302 {
303   SCM title_func = paper_->lookup_variable (ly_symbol2scm ("score-title"));
304
305   Stencil title;
306
307   SCM scopes = SCM_EOL;
308   if (ly_is_module (header_))
309     scopes = scm_cons (header_, scopes);
310
311   if (ly_is_module (header))
312     scopes = scm_cons (header, scopes);
313
314   SCM tit = SCM_EOL;
315   if (ly_is_procedure (title_func))
316     tit = scm_call_2 (title_func,
317                       paper_->self_scm (),
318                       scopes);
319
320   if (unsmob_stencil (tit))
321     title = *unsmob_stencil (tit);
322
323   if (!title.is_empty ())
324     title.align_to (Y_AXIS, UP);
325
326   return title;
327 }
328
329 void
330 set_page_permission (SCM sys, SCM symbol, SCM permission)
331 {
332   if (Paper_score *ps = dynamic_cast<Paper_score *> (unsmob_music_output (sys)))
333     {
334       vector<Grob *> cols = ps->get_columns ();
335       if (cols.size ())
336         {
337           Paper_column *col = dynamic_cast<Paper_column *> (cols.back ());
338           col->set_property (symbol, permission);
339           col->find_prebroken_piece (LEFT)->set_property (symbol, permission);
340         }
341     }
342   else if (Prob *pb = unsmob_prob (sys))
343     pb->set_property (symbol, permission);
344 }
345
346 /* read the breakbefore property of a score block and set up the preceding
347    system-spec to honour it. That is, SYS should be the system spec that
348    immediately precedes the score (from which HEADER is taken)
349    in the get_system_specs () list */
350 void
351 set_system_penalty (SCM sys, SCM header)
352 {
353   if (ly_is_module (header))
354     {
355       SCM force = ly_module_lookup (header, ly_symbol2scm ("breakbefore"));
356       if (SCM_VARIABLEP (force)
357           && scm_is_bool (SCM_VARIABLE_REF (force)))
358         {
359           if (to_boolean (SCM_VARIABLE_REF (force)))
360             {
361               set_page_permission (sys, ly_symbol2scm ("page-break-permission"),
362                                    ly_symbol2scm ("force"));
363               set_page_permission (sys, ly_symbol2scm ("line-break-permission"),
364                                    ly_symbol2scm ("force"));
365             }
366           else
367             set_page_permission (sys, ly_symbol2scm ("page-break-permission"),
368                                  SCM_EOL);
369         }
370     }
371 }
372
373 void
374 set_labels (SCM sys, SCM labels)
375 {
376   if (Paper_score *ps = dynamic_cast<Paper_score *> (unsmob_music_output (sys)))
377     {
378       vector<Grob*> cols = ps->get_columns ();
379       if (cols.size ())
380         {
381           Paper_column *col = dynamic_cast<Paper_column *> (cols[0]);
382           col->set_property ("labels", 
383                              scm_append_x (scm_list_2 (col->get_property ("labels"),
384                                                        labels)));
385           Paper_column *col_right
386             = dynamic_cast<Paper_column *> (col->find_prebroken_piece (RIGHT));
387           col_right->set_property ("labels", 
388                                    scm_append_x (scm_list_2 (col_right->get_property ("labels"),
389                                                              labels)));
390         }
391     }
392   else if (Prob *pb = unsmob_prob (sys))
393     pb->set_property ("labels", 
394                       scm_append_x (scm_list_2 (pb->get_property ("labels"),
395                                                 labels)));
396 }
397
398 SCM
399 Paper_book::get_score_title (SCM header)
400 {
401   Stencil title = score_title (header);
402   if (title.is_empty ())
403     title = score_title (header_);
404   if (!title.is_empty ())
405     {
406       /*
407         TODO: this should come from the \layout {} block, which should
408         override settings from \paper {}
409       */
410       SCM props
411         = paper_->lookup_variable (ly_symbol2scm ("score-title-properties"));
412       Prob *ps = make_paper_system (props);
413       paper_system_set_stencil (ps, title);
414
415       return ps->self_scm ();
416     }
417
418   return SCM_BOOL_F;
419 }
420
421
422 SCM
423 Paper_book::get_system_specs ()
424 {
425   SCM system_specs = SCM_EOL;
426
427   Stencil title = book_title ();
428   if (!title.is_empty ())
429     {
430       SCM props
431         = paper_->lookup_variable (ly_symbol2scm ("book-title-properties"));
432       Prob *ps = make_paper_system (props);
433       paper_system_set_stencil (ps, title);
434
435       system_specs = scm_cons (ps->self_scm (), system_specs);
436       ps->unprotect ();
437     }
438
439   SCM page_properties
440     = scm_call_1 (ly_lily_module_constant ("layout-extract-page-properties"),
441                   paper_->self_scm ());
442
443   SCM interpret_markup_list = ly_lily_module_constant ("interpret-markup-list");
444   SCM header = SCM_EOL;
445   SCM labels = SCM_EOL;
446   for (SCM s = scm_reverse (scores_); scm_is_pair (s); s = scm_cdr (s))
447     {
448       if (ly_is_module (scm_car (s)))
449         {
450           header = scm_car (s);
451           if (header_0_ == SCM_EOL)
452             header_0_ = header;
453         }
454       else if (Page_marker *page_marker = unsmob_page_marker (scm_car (s)))
455         {
456           /* page markers are used to set page breaking/turning permission,
457              or to place bookmarking labels */ 
458           if (scm_is_symbol (page_marker->permission_symbol ()))
459             {
460               /* set previous element page break or turn permission */
461               if (scm_is_pair (system_specs))
462                 set_page_permission (scm_car (system_specs),
463                                      page_marker->permission_symbol (),
464                                      page_marker->permission_value ());
465             }
466           if (scm_is_symbol (page_marker->label ()))
467             {
468               /* The next element label is to be set */
469               labels = scm_cons (page_marker->label (), labels);
470             }
471         }
472       else if (Music_output *mop = unsmob_music_output (scm_car (s)))
473         {
474           if (Paper_score *pscore = dynamic_cast<Paper_score *> (mop))
475             {
476               SCM title = get_score_title (header);
477
478               if (scm_is_pair (system_specs))
479                 set_system_penalty (scm_car (system_specs), header);
480
481               if (unsmob_prob (title))
482                 {
483                   system_specs = scm_cons (title, system_specs);
484                   unsmob_prob (title)->unprotect ();
485                 }
486
487               header = SCM_EOL;
488               system_specs = scm_cons (pscore->self_scm (), system_specs);
489               if (scm_is_pair (labels))
490                 {
491                   set_labels (scm_car (system_specs), labels);
492                   labels = SCM_EOL;
493                 }
494             }
495           else
496             {
497               /*
498                 Ignore MIDI
499               */
500             }
501         }
502       else if (Text_interface::is_markup_list (scm_car (s)))
503         {
504           SCM texts = scm_call_3 (interpret_markup_list,
505                                   paper_->self_scm (),
506                                   page_properties,
507                                   scm_car (s));
508           for (SCM list = texts ; scm_is_pair (list) ; list = scm_cdr (list))
509             {
510               SCM t = scm_car (list);
511               // TODO: init props
512               Prob *ps = make_paper_system (SCM_EOL);
513               ps->set_property ("page-break-permission",
514                                 ly_symbol2scm ("allow"));
515               ps->set_property ("page-turn-permission",
516                                 ly_symbol2scm ("allow"));
517
518               paper_system_set_stencil (ps, *unsmob_stencil (t));
519               ps->set_property ("is-title", SCM_BOOL_T); 
520               if (scm_is_pair (scm_cdr (list)))
521                 {
522                   /* If an other markup is following, set this markup 
523                    * next padding and next space to 0, so that baseline-skip 
524                    * only should be taken into account for lines vertical
525                    * spacing. */
526                   ps->set_property ("next-padding", scm_double2num (0.0));
527                   ps->set_property ("next-space", scm_double2num (0.0));
528                 }
529               system_specs = scm_cons (ps->self_scm (), system_specs);
530               ps->unprotect ();
531
532               if (scm_is_pair (labels))
533                 {
534                   set_labels (scm_car (system_specs), labels);
535                   labels = SCM_EOL;
536                 }
537               // FIXME: figure out penalty.
538               //set_system_penalty (ps, scores_[i].header_);
539             }
540         }
541       else
542         assert (0);
543     }
544
545   system_specs = scm_reverse_x (system_specs, SCM_EOL);
546   return system_specs;
547 }
548
549 SCM
550 Paper_book::systems ()
551 {
552   if (systems_ != SCM_BOOL_F)
553     return systems_;
554
555   systems_ = SCM_EOL;
556   if (scm_is_pair (bookparts_))
557     {
558       for (SCM p = bookparts_; scm_is_pair (p); p = scm_cdr (p))
559         if (Paper_book *pbookpart = unsmob_paper_book (scm_car (p)))
560           systems_ = scm_append_x (scm_list_2 (systems_,
561                                                pbookpart->systems ()));
562     }
563   else
564     {
565       SCM specs = get_system_specs ();
566       for (SCM s = specs; scm_is_pair (s); s = scm_cdr (s))
567         {
568           if (Paper_score *pscore
569               = dynamic_cast<Paper_score *> (unsmob_music_output (scm_car (s))))
570             {
571               SCM system_list
572                 = scm_vector_to_list (pscore->get_paper_systems ());
573
574               system_list = scm_reverse (system_list);
575               systems_ = scm_append (scm_list_2 (system_list, systems_));
576             }
577           else
578             {
579               systems_ = scm_cons (scm_car (s), systems_);
580             }
581         }
582       systems_ = scm_reverse (systems_);
583
584       /* backwards compatibility for the old page breaker */
585       int i = 0;
586       Prob *last = 0;
587       for (SCM s = systems_; scm_is_pair (s); s = scm_cdr (s))
588         {
589           Prob *ps = unsmob_prob (scm_car (s));
590           ps->set_property ("number", scm_from_int (++i));
591
592           if (last
593               && to_boolean (last->get_property ("is-title"))
594               && !scm_is_number (ps->get_property ("penalty")))
595             ps->set_property ("penalty", scm_from_int (10000));
596           last = ps;
597           
598           if (scm_is_pair (scm_cdr (s)))
599             {
600               SCM perm = ps->get_property ("page-break-permission");
601               Prob *next = unsmob_prob (scm_cadr (s));
602               if (perm == SCM_EOL)
603                 next->set_property ("penalty", scm_from_int (10001));
604               else if (perm == ly_symbol2scm ("force"))
605                 next->set_property ("penalty", scm_from_int (-10001));
606             }
607         }
608     }
609
610   return systems_;
611 }
612
613 SCM
614 Paper_book::pages ()
615 {
616   if (SCM_BOOL_F != pages_)
617     return pages_;
618
619   pages_ = SCM_EOL;
620   if (scm_is_pair (bookparts_))
621     {
622       for (SCM p = bookparts_; scm_is_pair (p); p = scm_cdr (p))
623         if (Paper_book *pbookpart = unsmob_paper_book (scm_car (p)))
624           pages_ = scm_append_x (scm_list_2 (pages_, pbookpart->pages ()));
625     }
626   else if (scm_is_pair (scores_))
627     {
628       SCM page_breaking = paper_->c_variable ("page-breaking");
629       pages_ = scm_apply_0 (page_breaking, scm_list_1 (self_scm ()));
630       SCM post_process = paper_->c_variable ("page-post-process");
631       if (ly_is_procedure (post_process))
632         scm_apply_2 (post_process, paper_->self_scm (), pages_, SCM_EOL);
633
634       /* set systems_ from the pages */
635       if (systems_ == SCM_BOOL_F)
636         {
637           systems_ = SCM_EOL;
638           for (SCM p = pages_; scm_is_pair (p); p = scm_cdr (p))
639             {
640               Prob *page = unsmob_prob (scm_car (p));
641               SCM systems = page->get_property ("lines");
642               systems_ = scm_append (scm_list_2 (systems_, systems));
643             }
644         }
645     }
646   return pages_;
647 }
648
649 SCM
650 Paper_book::performances () const
651 {
652   return scm_reverse (performances_);
653 }