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