]> git.donarmstrong.com Git - lilypond.git/blob - lily/paper-book.cc
Fix crash when output-preview-framework is missing
[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 = robust_scm2int (paper_->c_variable ("first-page-number"), 1);
161   int first_performance_number = 0;
162   if (!output_aux (output_channel,
163                    true,
164                    &first_page_number,
165                    &first_performance_number))
166     return;
167       
168   SCM scopes = SCM_EOL;
169   if (ly_is_module (header_))
170     scopes = scm_cons (header_, scopes);
171
172   string mod_nm = "scm framework-" + get_output_backend_name ();
173
174   SCM mod = scm_c_resolve_module (mod_nm.c_str ());
175
176   if (get_program_option ("print-pages"))
177     {
178       SCM framework = ly_module_lookup (mod, ly_symbol2scm ("output-framework"));
179
180       if (framework != SCM_BOOL_F)
181         {
182           SCM func = scm_variable_ref (framework);
183           scm_apply_0 (func, scm_list_n (output_channel,
184                                          self_scm (),
185                                          scopes,
186                                          dump_fields (),
187                                          SCM_UNDEFINED));
188         }
189       else
190         warning (_f ("program option -dprint-pages not supported by backend `%s'",
191                      get_output_backend_name ()));
192     }
193
194   if (get_program_option ("preview"))
195     {
196       SCM framework = ly_module_lookup (mod, ly_symbol2scm ("output-preview-framework"));
197
198       if (framework != SCM_BOOL_F)
199         {
200           SCM func = scm_variable_ref (framework);
201           scm_apply_0 (func, scm_list_n (output_channel,
202                                          self_scm (),
203                                          scopes,
204                                          dump_fields (),
205                                          SCM_UNDEFINED));
206         }
207       else
208         warning (_f ("program option -dpreview not supported by backend `%s'",
209                      get_output_backend_name ()));
210     }
211 }
212
213 void
214 Paper_book::classic_output_aux (SCM output,
215                                 int *first_performance_number)
216 {
217   if (scm_is_pair (performances_))
218     {
219       SCM proc = ly_lily_module_constant ("write-performances-midis");
220       scm_call_3 (proc,
221                   performances (),
222                   output,
223                   scm_long2num (*first_performance_number));
224       *first_performance_number += scm_ilength (performances_);
225     }
226   
227   /* Generate all stencils to trigger font loads.  */
228   systems ();
229 }
230
231 void
232 Paper_book::classic_output (SCM output)
233 {
234   int first_performance_number = 0;
235   classic_output_aux (output, &first_performance_number);
236
237   SCM scopes = SCM_EOL;
238   if (ly_is_module (header_))
239     scopes = scm_cons (header_, scopes);
240
241   if (ly_is_module (header_0_))
242     scopes = scm_cons (header_0_, scopes);
243
244   string format = get_output_backend_name ();
245   string mod_nm = "scm framework-" + format;
246
247   SCM mod = scm_c_resolve_module (mod_nm.c_str ());
248   SCM func = scm_c_module_lookup (mod, "output-classic-framework");
249
250   func = scm_variable_ref (func);
251   scm_apply_0 (func, scm_list_n (output,
252                                  self_scm (),
253                                  scopes,
254                                  dump_fields (),
255                                  SCM_UNDEFINED));
256
257   progress_indication ("\n");
258 }
259
260 /* TODO: resurrect more complex user-tweaks for titling?  */
261 Stencil
262 Paper_book::book_title ()
263 {
264   SCM title_func = paper_->lookup_variable (ly_symbol2scm ("book-title"));
265   Stencil title;
266
267   SCM scopes = SCM_EOL;
268   if (ly_is_module (header_))
269     scopes = scm_cons (header_, scopes);
270
271   SCM tit = SCM_EOL;
272   if (ly_is_procedure (title_func))
273     tit = scm_call_2 (title_func,
274                       paper_->self_scm (),
275                       scopes);
276
277   if (unsmob_stencil (tit))
278     title = *unsmob_stencil (tit);
279
280   if (!title.is_empty ())
281     title.align_to (Y_AXIS, UP);
282
283   return title;
284 }
285
286 Stencil
287 Paper_book::score_title (SCM header)
288 {
289   SCM title_func = paper_->lookup_variable (ly_symbol2scm ("score-title"));
290
291   Stencil title;
292
293   SCM scopes = SCM_EOL;
294   if (ly_is_module (header_))
295     scopes = scm_cons (header_, scopes);
296
297   if (ly_is_module (header))
298     scopes = scm_cons (header, scopes);
299
300   SCM tit = SCM_EOL;
301   if (ly_is_procedure (title_func))
302     tit = scm_call_2 (title_func,
303                       paper_->self_scm (),
304                       scopes);
305
306   if (unsmob_stencil (tit))
307     title = *unsmob_stencil (tit);
308
309   if (!title.is_empty ())
310     title.align_to (Y_AXIS, UP);
311
312   return title;
313 }
314
315 void
316 set_page_permission (SCM sys, SCM symbol, SCM permission)
317 {
318   if (Paper_score *ps = dynamic_cast<Paper_score*> (unsmob_music_output (sys)))
319     {
320       vector<Grob*> cols = ps->get_columns ();
321       if (cols.size ())
322         {
323           Paper_column *col = dynamic_cast<Paper_column*> (cols.back ());
324           col->set_property (symbol, permission);
325           col->find_prebroken_piece (LEFT)->set_property (symbol, permission);
326         }
327     }
328   else if (Prob *pb = unsmob_prob (sys))
329     pb->set_property (symbol, permission);
330 }
331
332 /* read the breakbefore property of a score block and set up the preceding
333    system-spec to honour it. That is, SYS should be the system spec that
334    immediately precedes the score (from which HEADER is taken)
335    in the get_system_specs () list */
336 void
337 set_system_penalty (SCM sys, SCM header)
338 {
339   if (ly_is_module (header))
340     {
341       SCM force = ly_module_lookup (header, ly_symbol2scm ("breakbefore"));
342       if (SCM_VARIABLEP (force)
343           && scm_is_bool (SCM_VARIABLE_REF (force)))
344         {
345           if (to_boolean (SCM_VARIABLE_REF (force)))
346             {
347               set_page_permission (sys, ly_symbol2scm ("page-break-permission"),
348                                    ly_symbol2scm ("force"));
349               set_page_permission (sys, ly_symbol2scm ("line-break-permission"),
350                                    ly_symbol2scm ("force"));
351             }
352           else
353             set_page_permission (sys, ly_symbol2scm ("page-break-permission"), SCM_EOL);
354         }
355     }
356 }
357
358 void
359 set_labels (SCM sys, SCM labels)
360 {
361   if (Paper_score *ps = dynamic_cast<Paper_score*> (unsmob_music_output (sys)))
362     {
363       vector<Grob*> cols = ps->get_columns ();
364       if (cols.size ())
365         {
366           Paper_column *col = dynamic_cast<Paper_column*> (cols[0]);
367           col->set_property ("labels", 
368                              scm_append_x (scm_list_2 (col->get_property ("labels"),
369                                                        labels)));
370           Paper_column *col_right = dynamic_cast<Paper_column*> (col->find_prebroken_piece (RIGHT));
371           col_right->set_property ("labels", 
372                                    scm_append_x (scm_list_2 (col_right->get_property ("labels"),
373                                                              labels)));
374         }
375     }
376   else if (Prob *pb = unsmob_prob (sys))
377     pb->set_property ("labels", 
378                       scm_append_x (scm_list_2 (pb->get_property ("labels"),
379                                                 labels)));
380 }
381
382 SCM
383 Paper_book::get_score_title (SCM header)
384 {
385   Stencil title = score_title (header);
386   if (title.is_empty ())
387     title = score_title (header_);
388   if (!title.is_empty ())
389     {
390       /*
391         TODO: this should come from the \layout {} block, which should
392         override settings from \paper {}
393       */
394       SCM props = paper_->lookup_variable (ly_symbol2scm ("score-title-properties"));
395       Prob *ps = make_paper_system (props);
396       paper_system_set_stencil (ps, title);
397
398       return ps->self_scm ();
399     }
400
401   return SCM_BOOL_F;
402 }
403
404
405 SCM
406 Paper_book::get_system_specs ()
407 {
408   SCM system_specs = SCM_EOL;
409   
410   Stencil title = book_title ();
411   if (!title.is_empty ())
412     {
413       SCM props = paper_->lookup_variable (ly_symbol2scm ("book-title-properties"));
414       Prob *ps = make_paper_system (props);
415       paper_system_set_stencil (ps, title);
416       
417       system_specs = scm_cons (ps->self_scm (), system_specs);
418       ps->unprotect ();
419     }
420
421   SCM page_properties
422     = scm_call_1 (ly_lily_module_constant ("layout-extract-page-properties"),
423                   paper_->self_scm ());
424
425   SCM interpret_markup_list = ly_lily_module_constant ("interpret-markup-list");
426   SCM header = SCM_EOL;
427   SCM labels = SCM_EOL;
428   for (SCM s = scm_reverse (scores_); scm_is_pair (s); s = scm_cdr (s))
429     {
430       if (ly_is_module (scm_car (s)))
431         {
432           header = scm_car (s);
433           if (header_0_ == SCM_EOL)
434             header_0_ = header;
435         }
436       else if (Page_marker *page_marker = unsmob_page_marker (scm_car (s)))
437         {
438           /* page markers are used to set page breaking/turning permission,
439              or to place bookmarking labels */ 
440           if (scm_is_symbol (page_marker->permission_symbol ()))
441             {
442               /* set previous element page break or turn permission */
443               if (scm_is_pair (system_specs))
444                 set_page_permission (scm_car (system_specs),
445                                      page_marker->permission_symbol (),
446                                      page_marker->permission_value ());
447             }
448           if (scm_is_symbol (page_marker->label ()))
449             {
450               /* The next element label is to be set */
451               labels = scm_cons (page_marker->label (), labels);
452             }
453         }
454       else if (Music_output *mop = unsmob_music_output (scm_car (s)))
455         {
456           if (Paper_score *pscore = dynamic_cast<Paper_score *> (mop))
457             {
458               SCM title = get_score_title (header);
459
460               if (scm_is_pair (system_specs))
461                 set_system_penalty (scm_car (system_specs), header);
462
463               if (unsmob_prob (title))
464                 {
465                   system_specs = scm_cons (title, system_specs);
466                   unsmob_prob (title)->unprotect ();
467                 }
468
469               header = SCM_EOL;
470               system_specs = scm_cons (pscore->self_scm (), system_specs);
471               if (scm_is_pair (labels))
472                 {
473                   set_labels (scm_car (system_specs), labels);
474                   labels = SCM_EOL;
475                 }
476             }
477           else
478             {
479               /*
480                 Ignore MIDI
481               */
482             }
483         }
484       else if (Text_interface::is_markup_list (scm_car (s)))
485         {
486           SCM texts = scm_call_3 (interpret_markup_list,
487                                   paper_->self_scm (),
488                                   page_properties,
489                                   scm_car (s));
490           for (SCM list = texts ; scm_is_pair (list) ; list = scm_cdr (list))
491             {
492               SCM t = scm_car (list);
493               // TODO: init props
494               Prob *ps = make_paper_system (SCM_EOL);
495               ps->set_property ("page-break-permission", ly_symbol2scm ("allow"));
496               ps->set_property ("page-turn-permission", ly_symbol2scm ("allow"));
497               
498               paper_system_set_stencil (ps, *unsmob_stencil (t));
499               ps->set_property ("is-title", SCM_BOOL_T); 
500               if (scm_is_pair (scm_cdr (list)))
501                 {
502                   /* If an other markup is following, set this markup 
503                    * next padding and next space to 0, so that baseline-skip 
504                    * only should be taken into account for lines vertical
505                    * spacing. */
506                   ps->set_property ("next-padding", scm_double2num (0.0));
507                   ps->set_property ("next-space", scm_double2num (0.0));
508                 }
509               system_specs = scm_cons (ps->self_scm (), system_specs);
510               ps->unprotect ();
511               
512               if (scm_is_pair (labels))
513                 {
514                   set_labels (scm_car (system_specs), labels);
515                   labels = SCM_EOL;
516                 }
517               // FIXME: figure out penalty.
518               //set_system_penalty (ps, scores_[i].header_);
519             }
520         }
521       else
522         assert (0);
523     }
524
525   system_specs = scm_reverse_x (system_specs, SCM_EOL);
526   return system_specs;
527 }
528
529 SCM
530 Paper_book::systems ()
531 {
532   if (systems_ != SCM_BOOL_F)
533     return systems_;
534
535   systems_ = SCM_EOL;
536   if (scm_is_pair (bookparts_))
537     {
538       for (SCM p = bookparts_; scm_is_pair (p); p = scm_cdr (p))
539         if (Paper_book *pbookpart = unsmob_paper_book (scm_car (p)))
540           systems_ = scm_append_x (scm_list_2 (systems_, pbookpart->systems ()));
541     }
542   else
543     {
544       SCM specs = get_system_specs ();
545       for (SCM s = specs; scm_is_pair (s); s = scm_cdr (s))
546         {
547           if (Paper_score *pscore = dynamic_cast<Paper_score*> (unsmob_music_output (scm_car (s))))
548             {
549               SCM system_list = scm_vector_to_list (pscore->get_paper_systems ());
550               system_list = scm_reverse (system_list);
551               systems_ = scm_append (scm_list_2 (system_list, systems_));
552             }
553           else
554             {
555               systems_ = scm_cons (scm_car (s), systems_);
556             }
557         }
558       systems_ = scm_reverse (systems_);
559
560       /* backwards compatibility for the old page breaker */
561       int i = 0;
562       Prob *last = 0;
563       for (SCM s = systems_; scm_is_pair (s); s = scm_cdr (s))
564         {
565           Prob *ps = unsmob_prob (scm_car (s));
566           ps->set_property ("number", scm_from_int (++i));
567           
568           if (last
569               && to_boolean (last->get_property ("is-title"))
570               && !scm_is_number (ps->get_property ("penalty")))
571             ps->set_property ("penalty", scm_from_int (10000));
572           last = ps;
573           
574           if (scm_is_pair (scm_cdr (s)))
575             {
576               SCM perm = ps->get_property ("page-break-permission");
577               Prob *next = unsmob_prob (scm_cadr (s));
578               if (perm == SCM_EOL)
579                 next->set_property ("penalty", scm_from_int (10001));
580               else if (perm == ly_symbol2scm ("force"))
581                 next->set_property ("penalty", scm_from_int (-10001));
582             }
583         }
584     }
585
586   return systems_;
587 }
588
589 SCM
590 Paper_book::pages ()
591 {
592   if (SCM_BOOL_F != pages_)
593     return pages_;
594
595   pages_ = SCM_EOL;
596   if (scm_is_pair (bookparts_))
597     {
598       for (SCM p = bookparts_; scm_is_pair (p); p = scm_cdr (p))
599         if (Paper_book *pbookpart = unsmob_paper_book (scm_car (p)))
600           pages_ = scm_append_x (scm_list_2 (pages_, pbookpart->pages ()));
601     }
602   else if (scm_is_pair (scores_))
603     {
604       SCM proc = paper_->c_variable ("page-breaking-wrapper");
605       pages_ = scm_apply_0 (proc, scm_list_1 (self_scm ()));
606
607       /* set systems_ from the pages */
608       if (systems_ == SCM_BOOL_F)
609         {
610           systems_ = SCM_EOL;
611           for (SCM p = pages_; scm_is_pair (p); p = scm_cdr (p))
612             {
613               Prob *page = unsmob_prob (scm_car (p));
614               SCM systems = page->get_property ("lines");
615               systems_ = scm_append (scm_list_2 (systems_, systems));
616             }
617         }
618     }
619   return pages_;
620 }
621
622 SCM
623 Paper_book::performances () const
624 {
625   return scm_reverse (performances_);
626 }