2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 2004--2010 Jan Nieuwenhuizen <janneke@gnu.org>
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.
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.
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/>.
20 #include "paper-book.hh"
23 #include "international.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"
31 #include "program-option.hh"
32 #include "page-marker.hh"
34 #include "ly-smobs.icc"
36 Paper_book::Paper_book ()
43 performances_
= SCM_EOL
;
44 systems_
= SCM_BOOL_F
;
51 Paper_book::~Paper_book ()
55 IMPLEMENT_DEFAULT_EQUAL_P (Paper_book
);
56 IMPLEMENT_SMOBS (Paper_book
);
57 IMPLEMENT_TYPE_P (Paper_book
, "ly:paper-book?");
60 Paper_book::mark_smob (SCM smob
)
62 Paper_book
*b
= (Paper_book
*) SCM_CELL_WORD_1 (smob
);
64 scm_gc_mark (b
->paper_
->self_scm ());
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_
);
77 Paper_book::print_smob (SCM smob
, SCM port
, scm_print_state
*)
79 Paper_book
*b
= (Paper_book
*) SCM_CELL_WORD_1 (smob
);
81 scm_puts ("#<Paper_book>", port
);
86 Paper_book::top_paper ()
88 Output_def
*paper
= paper_
;
89 while (paper
->parent_
)
90 paper
= paper
->parent_
;
98 for (vsize i
= dump_header_fieldnames_global
.size (); i
--;)
100 = scm_cons (ly_symbol2scm (dump_header_fieldnames_global
[i
].c_str ()),
106 Paper_book::add_score (SCM s
)
108 scores_
= scm_cons (s
, scores_
);
112 Paper_book::add_bookpart (SCM p
)
114 bookparts_
= scm_cons (p
, bookparts_
);
118 Paper_book::add_performance (SCM s
)
120 performances_
= scm_cons (s
, performances_
);
124 Paper_book::output_aux (SCM output_channel
,
126 int *first_page_number
,
127 int *first_performance_number
)
130 if (scm_is_pair (performances_
))
132 SCM proc
= ly_lily_module_constant ("write-performances-midis");
137 scm_long2num (*first_performance_number
));
138 *first_performance_number
+= scm_ilength (performances_
);
141 if (scm_is_pair (bookparts_
))
143 for (SCM p
= bookparts_
; scm_is_pair (p
); p
= scm_cdr (p
))
144 if (Paper_book
*pbookpart
= unsmob_paper_book (scm_car (p
)))
146 bool is_last_part
= (is_last
&& !scm_is_pair (scm_cdr (p
)));
147 page_nb
+= pbookpart
->output_aux (output_channel
,
150 first_performance_number
);
155 if (scores_
== SCM_EOL
)
157 paper_
->set_variable (ly_symbol2scm ("first-page-number"),
158 scm_long2num (*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
;
169 Paper_book::output (SCM output_channel
)
171 int first_page_number
172 = robust_scm2int (paper_
->c_variable ("first-page-number"), 1);
173 int first_performance_number
= 0;
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.
181 if (paper_
->c_variable ("line-width") == SCM_UNDEFINED
)
182 paper_
->set_variable (ly_symbol2scm ("line-width"),
183 paper_
->c_variable ("paper-width"));
185 if (!output_aux (output_channel
,
188 &first_performance_number
))
191 SCM scopes
= SCM_EOL
;
192 if (ly_is_module (header_
))
193 scopes
= scm_cons (header_
, scopes
);
195 string mod_nm
= "scm framework-" + get_output_backend_name ();
197 SCM mod
= scm_c_resolve_module (mod_nm
.c_str ());
199 if (get_program_option ("print-pages"))
201 SCM framework
= ly_module_lookup (mod
,
202 ly_symbol2scm ("output-framework"));
204 if (framework
!= SCM_BOOL_F
)
206 SCM func
= scm_variable_ref (framework
);
207 scm_apply_0 (func
, scm_list_n (output_channel
,
214 warning (_f ("program option -dprint-pages not supported by backend `%s'",
215 get_output_backend_name ()));
218 if (get_program_option ("preview"))
221 = ly_module_lookup (mod
, ly_symbol2scm ("output-preview-framework"));
223 if (framework
!= SCM_BOOL_F
)
225 SCM func
= scm_variable_ref (framework
);
226 scm_apply_0 (func
, scm_list_n (output_channel
,
233 warning (_f ("program option -dpreview not supported by backend `%s'",
234 get_output_backend_name ()));
239 Paper_book::classic_output_aux (SCM output
,
240 int *first_performance_number
)
242 if (scm_is_pair (performances_
))
244 SCM proc
= ly_lily_module_constant ("write-performances-midis");
248 scm_long2num (*first_performance_number
));
249 *first_performance_number
+= scm_ilength (performances_
);
252 /* Generate all stencils to trigger font loads. */
257 Paper_book::classic_output (SCM output
)
259 int first_performance_number
= 0;
260 classic_output_aux (output
, &first_performance_number
);
262 SCM scopes
= SCM_EOL
;
263 if (ly_is_module (header_
))
264 scopes
= scm_cons (header_
, scopes
);
266 if (ly_is_module (header_0_
))
267 scopes
= scm_cons (header_0_
, scopes
);
269 string format
= get_output_backend_name ();
270 string mod_nm
= "scm framework-" + format
;
272 SCM mod
= scm_c_resolve_module (mod_nm
.c_str ());
273 SCM func
= scm_c_module_lookup (mod
, "output-classic-framework");
275 func
= scm_variable_ref (func
);
276 scm_apply_0 (func
, scm_list_n (output
,
282 progress_indication ("\n");
285 /* TODO: resurrect more complex user-tweaks for titling? */
287 Paper_book::book_title ()
289 SCM title_func
= paper_
->lookup_variable (ly_symbol2scm ("book-title"));
292 SCM scopes
= SCM_EOL
;
293 if (ly_is_module (header_
))
294 scopes
= scm_cons (header_
, scopes
);
297 if (ly_is_procedure (title_func
))
298 tit
= scm_call_2 (title_func
,
302 if (unsmob_stencil (tit
))
303 title
= *unsmob_stencil (tit
);
305 if (!title
.is_empty ())
306 title
.align_to (Y_AXIS
, UP
);
312 Paper_book::score_title (SCM header
)
314 SCM title_func
= paper_
->lookup_variable (ly_symbol2scm ("score-title"));
318 SCM scopes
= SCM_EOL
;
319 if (ly_is_module (header_
))
320 scopes
= scm_cons (header_
, scopes
);
322 if (ly_is_module (header
))
323 scopes
= scm_cons (header
, scopes
);
326 if (ly_is_procedure (title_func
))
327 tit
= scm_call_2 (title_func
,
331 if (unsmob_stencil (tit
))
332 title
= *unsmob_stencil (tit
);
334 if (!title
.is_empty ())
335 title
.align_to (Y_AXIS
, UP
);
341 set_page_permission (SCM sys
, SCM symbol
, SCM permission
)
343 if (Paper_score
*ps
= dynamic_cast<Paper_score
*> (unsmob_music_output (sys
)))
345 vector
<Grob
*> cols
= ps
->get_columns ();
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
);
353 else if (Prob
*pb
= unsmob_prob (sys
))
354 pb
->set_property (symbol
, permission
);
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 */
362 set_system_penalty (SCM sys
, SCM header
)
364 if (ly_is_module (header
))
366 SCM force
= ly_module_lookup (header
, ly_symbol2scm ("breakbefore"));
367 if (SCM_VARIABLEP (force
)
368 && scm_is_bool (SCM_VARIABLE_REF (force
)))
370 if (to_boolean (SCM_VARIABLE_REF (force
)))
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"));
378 set_page_permission (sys
, ly_symbol2scm ("page-break-permission"),
385 set_labels (SCM sys
, SCM labels
)
387 if (Paper_score
*ps
= dynamic_cast<Paper_score
*> (unsmob_music_output (sys
)))
389 vector
<Grob
*> cols
= ps
->get_columns ();
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"),
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"),
403 else if (Prob
*pb
= unsmob_prob (sys
))
404 pb
->set_property ("labels",
405 scm_append_x (scm_list_2 (pb
->get_property ("labels"),
410 Paper_book::get_score_title (SCM header
)
412 Stencil title
= score_title (header
);
413 if (title
.is_empty ())
414 title
= score_title (header_
);
415 if (!title
.is_empty ())
418 TODO: this should come from the \layout {} block, which should
419 override settings from \paper {}
422 = paper_
->lookup_variable (ly_symbol2scm ("score-title-properties"));
423 Prob
*ps
= make_paper_system (props
);
424 paper_system_set_stencil (ps
, title
);
426 return ps
->self_scm ();
434 Paper_book::get_system_specs ()
436 SCM system_specs
= SCM_EOL
;
438 Stencil title
= book_title ();
439 if (!title
.is_empty ())
442 = paper_
->lookup_variable (ly_symbol2scm ("book-title-properties"));
443 Prob
*ps
= make_paper_system (props
);
444 paper_system_set_stencil (ps
, title
);
446 system_specs
= scm_cons (ps
->self_scm (), system_specs
);
451 = scm_call_1 (ly_lily_module_constant ("layout-extract-page-properties"),
452 paper_
->self_scm ());
454 SCM interpret_markup_list
= ly_lily_module_constant ("interpret-markup-list");
455 SCM header
= SCM_EOL
;
456 SCM labels
= SCM_EOL
;
457 for (SCM s
= scm_reverse (scores_
); scm_is_pair (s
); s
= scm_cdr (s
))
459 if (ly_is_module (scm_car (s
)))
461 header
= scm_car (s
);
462 if (header_0_
== SCM_EOL
)
465 else if (Page_marker
*page_marker
= unsmob_page_marker (scm_car (s
)))
467 /* page markers are used to set page breaking/turning permission,
468 or to place bookmarking labels */
469 if (scm_is_symbol (page_marker
->permission_symbol ()))
471 /* set previous element page break or turn permission */
472 if (scm_is_pair (system_specs
))
473 set_page_permission (scm_car (system_specs
),
474 page_marker
->permission_symbol (),
475 page_marker
->permission_value ());
477 if (scm_is_symbol (page_marker
->label ()))
479 /* The next element label is to be set */
480 labels
= scm_cons (page_marker
->label (), labels
);
483 else if (Music_output
*mop
= unsmob_music_output (scm_car (s
)))
485 if (Paper_score
*pscore
= dynamic_cast<Paper_score
*> (mop
))
487 SCM title
= get_score_title (header
);
489 if (scm_is_pair (system_specs
))
490 set_system_penalty (scm_car (system_specs
), header
);
492 if (unsmob_prob (title
))
494 system_specs
= scm_cons (title
, system_specs
);
495 unsmob_prob (title
)->unprotect ();
499 system_specs
= scm_cons (pscore
->self_scm (), system_specs
);
500 if (scm_is_pair (labels
))
502 set_labels (scm_car (system_specs
), labels
);
513 else if (Text_interface::is_markup_list (scm_car (s
)))
515 SCM texts
= scm_call_3 (interpret_markup_list
,
521 for (list
= texts
; scm_is_pair (list
) ; list
= scm_cdr (list
))
523 SCM t
= scm_car (list
);
525 ps
= make_paper_system (SCM_EOL
);
526 ps
->set_property ("page-break-permission",
527 ly_symbol2scm ("allow"));
528 ps
->set_property ("page-turn-permission",
529 ly_symbol2scm ("allow"));
530 ps
->set_property ("last-markup-line", SCM_BOOL_F
);
531 ps
->set_property ("first-markup-line",
532 list
== texts
? SCM_BOOL_T
: SCM_BOOL_F
);
534 paper_system_set_stencil (ps
, *unsmob_stencil (t
));
535 ps
->set_property ("is-title", SCM_BOOL_T
);
537 /* For each markup other than the first, place it as closely as
538 possible to the previous markup and don't allow stretching. */
539 ps
->set_property ("tight-spacing", SCM_BOOL_T
);
541 system_specs
= scm_cons (ps
->self_scm (), system_specs
);
544 if (scm_is_pair (labels
))
546 set_labels (scm_car (system_specs
), labels
);
549 // FIXME: figure out penalty.
550 //set_system_penalty (ps, scores_[i].header_);
552 // We may want to place a check here, for whether the line is too short
555 // if there is only one line in the paragraph,
556 // do not try to avoid orphans
557 ps
->set_property ("last-markup-line", SCM_BOOL_F
);
558 ps
->set_property ("first-markup-line", SCM_BOOL_F
);
562 ps
->set_property ("last-markup-line", SCM_BOOL_T
);
569 system_specs
= scm_reverse_x (system_specs
, SCM_EOL
);
574 Paper_book::systems ()
576 if (systems_
!= SCM_BOOL_F
)
580 if (scm_is_pair (bookparts_
))
582 for (SCM p
= bookparts_
; scm_is_pair (p
); p
= scm_cdr (p
))
583 if (Paper_book
*pbookpart
= unsmob_paper_book (scm_car (p
)))
584 systems_
= scm_append_x (scm_list_2 (systems_
,
585 pbookpart
->systems ()));
589 SCM specs
= get_system_specs ();
590 for (SCM s
= specs
; scm_is_pair (s
); s
= scm_cdr (s
))
592 if (Paper_score
*pscore
593 = dynamic_cast<Paper_score
*> (unsmob_music_output (scm_car (s
))))
596 = scm_vector_to_list (pscore
->get_paper_systems ());
598 system_list
= scm_reverse (system_list
);
599 systems_
= scm_append (scm_list_2 (system_list
, systems_
));
603 systems_
= scm_cons (scm_car (s
), systems_
);
606 systems_
= scm_reverse (systems_
);
608 /* backwards compatibility for the old page breaker */
611 for (SCM s
= systems_
; scm_is_pair (s
); s
= scm_cdr (s
))
613 Prob
*ps
= unsmob_prob (scm_car (s
));
614 ps
->set_property ("number", scm_from_int (++i
));
617 && to_boolean (last
->get_property ("is-title"))
618 && !scm_is_number (ps
->get_property ("penalty")))
619 ps
->set_property ("penalty", scm_from_int (10000));
622 if (scm_is_pair (scm_cdr (s
)))
624 SCM perm
= ps
->get_property ("page-break-permission");
625 Prob
*next
= unsmob_prob (scm_cadr (s
));
627 next
->set_property ("penalty", scm_from_int (10001));
628 else if (perm
== ly_symbol2scm ("force"))
629 next
->set_property ("penalty", scm_from_int (-10001));
640 if (SCM_BOOL_F
!= pages_
)
644 if (scm_is_pair (bookparts_
))
646 for (SCM p
= bookparts_
; scm_is_pair (p
); p
= scm_cdr (p
))
647 if (Paper_book
*pbookpart
= unsmob_paper_book (scm_car (p
)))
648 pages_
= scm_append_x (scm_list_2 (pages_
, pbookpart
->pages ()));
650 else if (scm_is_pair (scores_
))
652 SCM page_breaking
= paper_
->c_variable ("page-breaking");
653 pages_
= scm_apply_0 (page_breaking
, scm_list_1 (self_scm ()));
654 SCM post_process
= paper_
->c_variable ("page-post-process");
655 if (ly_is_procedure (post_process
))
656 scm_apply_2 (post_process
, paper_
->self_scm (), pages_
, SCM_EOL
);
658 /* set systems_ from the pages */
659 if (systems_
== SCM_BOOL_F
)
662 for (SCM p
= pages_
; scm_is_pair (p
); p
= scm_cdr (p
))
664 Prob
*page
= unsmob_prob (scm_car (p
));
665 SCM systems
= page
->get_property ("lines");
666 systems_
= scm_append (scm_list_2 (systems_
, systems
));
674 Paper_book::performances () const
676 return scm_reverse (performances_
);