]> git.donarmstrong.com Git - lilypond.git/blob - lily/paper-book.cc
* Documentation/user/changing-defaults.itely (Creating contexts):
[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_BOOL_F;
31   lines_ = SCM_BOOL_F;
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 lst = SCM_EOL;
122   for (int i = 0; i < output_formats.size (); i ++)
123     lst = scm_cons (scm_makfrom0str (output_formats[i].to_str0 ()), lst);
124   
125   return lst; 
126 }
127
128 /*
129   TODO: there is too much code dup, and the interface is not
130   clear. FIXME.
131  */
132 void
133 Paper_book::output (String outname)
134 {
135   if (!score_lines_.size ())
136     return;
137
138   
139   /* Generate all stencils to trigger font loads.  */
140   pages ();
141
142   
143   SCM formats = ly_output_formats();
144   for (SCM s = formats; ly_c_pair_p (s); s = ly_cdr (s)) 
145     {
146       String format = ly_scm2string (ly_car (s));
147       
148       Paper_outputter *out = get_paper_outputter (outname + "." + format, format);
149   
150       SCM scopes = SCM_EOL;
151       if (ly_c_module_p (header_))
152         scopes = scm_cons (header_, scopes);
153   
154       String func_nm = format;
155       func_nm = "output-framework-" + func_nm;
156         
157       SCM func = ly_scheme_function (func_nm.to_str0 ());
158       scm_apply_0 (func, scm_list_n (out->self_scm (),
159                                      self_scm (),
160                                      scopes,
161                                      dump_fields (),
162                                      scm_makfrom0str (outname.to_str0 ()),
163                                      SCM_UNDEFINED
164                                      )) ;
165
166       scm_gc_unprotect_object (out->self_scm ());
167     }
168 }
169
170
171 void
172 Paper_book::classic_output (String outname)
173 {
174
175   /* Generate all stencils to trigger font loads.  */
176   lines ();
177
178
179   // ugh code dup
180   SCM scopes = SCM_EOL;
181   if (ly_c_module_p (header_))
182     scopes = scm_cons (header_, scopes);
183
184   if (ly_c_module_p (score_lines_[0].header_))
185     scopes = scm_cons (score_lines_[0].header_, scopes);
186   //end ugh
187
188
189   Array<String> output_formats = split_string (output_format_global, ',');
190
191   for (int i = 0; i < output_formats.size (); i++)
192     {
193       String format = output_formats[i];
194       String func_nm = format;
195       func_nm = "output-classic-framework-" + func_nm;
196       
197       SCM func = ly_scheme_function (func_nm.to_str0 ());
198
199       Paper_outputter *out = get_paper_outputter (outname + "." + format, format);
200
201       scm_apply_0 (func, scm_list_n (out->self_scm (),
202                                      self_scm (),
203                                      scopes,
204                                      dump_fields (),
205                                      scm_makfrom0str (outname.to_str0 ()),
206                                      SCM_UNDEFINED
207                                      )) ;
208
209       scm_gc_unprotect_object (out->self_scm ());
210     }
211   
212   progress_indication ("\n");
213 }
214
215
216
217
218 LY_DEFINE(ly_paper_book_pages, "ly:paper-book-pages",
219           1,0,0,
220           (SCM pb),
221           "Return pages in book PB.")
222 {
223   return unsmob_paper_book(pb)->pages ();
224 }
225
226
227 LY_DEFINE(ly_paper_book_lines, "ly:paper-book-lines",
228           1,0,0,
229           (SCM pb),
230           "Return lines in book PB.")
231 {
232   return unsmob_paper_book (pb)->lines ();
233 }
234
235
236 LY_DEFINE(ly_paper_book_book_paper, "ly:paper-book-book-paper",
237           1,0,0,
238           (SCM pb),
239           "Return pages in book PB.")
240 {
241   return unsmob_paper_book(pb)->bookpaper_->self_scm ();
242 }
243
244 /*
245
246 TODO: resurrect more complex user-tweaks for titling .
247
248 */
249 Stencil
250 Paper_book::book_title ()
251 {
252   SCM title_func = bookpaper_->lookup_variable (ly_symbol2scm ("book-title"));
253   Stencil title;
254
255   SCM scopes = SCM_EOL;
256   if (ly_c_module_p (header_))
257     scopes = scm_cons (header_, scopes);
258
259  
260   SCM tit = SCM_EOL;
261   if (ly_c_procedure_p (title_func))
262     tit = scm_call_2 (title_func,
263                      bookpaper_->self_scm (),
264                      scopes);
265
266   if (unsmob_stencil (tit))
267     title = *unsmob_stencil (tit);
268
269   if (!title.is_empty ())
270     title.align_to (Y_AXIS, UP);
271   
272   return title;
273 }
274
275   
276
277 Stencil
278 Paper_book::score_title (int i)
279 {
280   SCM title_func = bookpaper_->lookup_variable (ly_symbol2scm ("score-title"));
281
282   Stencil title;
283
284   // ugh code dup
285   SCM scopes = SCM_EOL;
286   if (ly_c_module_p (header_))
287     scopes = scm_cons (header_, scopes);
288
289   if (ly_c_module_p (score_lines_[i].header_))
290     scopes = scm_cons (score_lines_[i].header_, scopes);
291   //end ugh
292
293   SCM tit = SCM_EOL;
294   if (ly_c_procedure_p (title_func))
295     tit =scm_call_2 (title_func,
296                      bookpaper_->self_scm (),
297                      scopes);
298
299   if (unsmob_stencil (tit))
300     title = *unsmob_stencil (tit);
301
302
303   if (!title.is_empty ())
304     title.align_to (Y_AXIS, UP);
305   
306   return title;
307 }
308   
309
310 SCM
311 Paper_book::lines ()
312 {
313   if (SCM_BOOL_F != lines_)
314     return lines_;
315
316   lines_ = SCM_EOL;
317   Stencil title = book_title ();
318
319   if (!title.is_empty ())
320     {
321       Paper_line *pl = new Paper_line (title, true);
322       
323       lines_ = scm_cons (pl->self_scm (), lines_);
324       scm_gc_unprotect_object (pl->self_scm ());
325     }
326   
327   int score_count = score_lines_.size ();
328   for (int i = 0; i < score_count; i++)
329     {
330       Stencil title = score_title (i);      
331       if (!title.is_empty ())
332         {
333           Paper_line *pl = new Paper_line (title, true);
334           lines_ = scm_cons (pl->self_scm (), lines_);
335           scm_gc_unprotect_object (pl->self_scm ());
336         }
337       
338       if (scm_vector_p (score_lines_[i].lines_) == SCM_BOOL_T)
339         {
340           SCM line_list = scm_vector_to_list (score_lines_[i].lines_); // guh.
341
342           line_list = scm_reverse (line_list);
343           lines_ = scm_append (scm_list_2 (line_list, lines_));
344         }
345     }
346   
347   lines_ = scm_reverse (lines_);
348   
349   int i = 0;
350   Paper_line * last = 0;
351   for (SCM s = lines_; s != SCM_EOL; s = ly_cdr (s))
352     {
353       Paper_line * p = unsmob_paper_line (ly_car (s));
354       p->number_ = ++i;
355
356       if (last && last->is_title ())
357         {
358           p->penalty_ = 10000;  // ugh, hardcoded.
359         }
360       last = p;
361     }
362
363   
364   return lines_;
365 }
366
367
368 SCM
369 make_tagline (Output_def*paper, SCM scopes)
370 {
371   SCM make_tagline = paper->c_variable ("make-tagline");
372   SCM tagline = scm_call_2 (make_tagline, paper->self_scm (), scopes);
373   return tagline;
374 }
375
376 SCM
377 make_copyright (Output_def *paper, SCM scopes)
378 {
379   SCM make_copyright = paper->c_variable ("make-copyright");
380   SCM  copyright = scm_call_2 (make_copyright, paper->self_scm (), scopes);
381   return copyright;
382 }
383
384 SCM
385 Paper_book::pages ()
386 {
387   if (SCM_BOOL_F != pages_)
388     return pages_;
389
390   pages_ = SCM_EOL;
391   
392   Output_def *paper = bookpaper_;
393
394
395   // dummy to extract dims
396   Page *page = new Page (SCM_EOL, paper, 1); // ugh
397   Real text_height = page->text_height ();
398
399   Real copy_height = 0;
400   if (Stencil *s = unsmob_stencil (copyright_))
401     copy_height = s->extent (Y_AXIS).length ();
402
403   Real tag_height = 0;
404   if (Stencil *s = unsmob_stencil (tagline_))
405     tag_height = s->extent (Y_AXIS).length ();
406
407   scm_gc_unprotect_object (page->self_scm ());
408
409   /*
410     UGH - move this out of C++.
411    */
412   SCM scopes = SCM_EOL;
413   if (ly_c_module_p (header_))
414     scopes = scm_cons (header_, scopes);
415   
416   tagline_ = make_tagline (bookpaper_, scopes);
417   copyright_ = make_tagline (bookpaper_, scopes);
418
419
420   SCM all = lines ();
421   SCM proc = paper->c_variable ("page-breaking");
422   SCM pages = scm_apply_0 (proc, scm_list_n (all,
423                                               self_scm (),
424                                               scm_make_real (text_height),
425                                               scm_make_real (-copy_height),
426                                               scm_make_real (-tag_height),
427                                               SCM_UNDEFINED));
428
429
430   SCM *page_tail = &pages_;
431   int num = 0;
432   for (SCM s = pages; ly_c_pair_p (s); s =  ly_cdr (s))
433     {
434       Page * page = new Page (ly_car (s), paper, ++num);
435       
436       *page_tail = scm_cons (page->self_scm () , SCM_EOL);
437       page_tail = SCM_CDRLOC(*page_tail);
438
439       scm_gc_unprotect_object (page->self_scm ());
440
441       if (!ly_c_pair_p (ly_cdr (s)))
442         page->is_last_ = true;
443     }
444
445   return pages_;
446 }
447
448
449
450 static SCM
451 c_ragged_page_breaks (SCM lines,
452                       Paper_book *book,
453                       Real text_height,
454                       Real first, Real last)
455 {
456   int page_number = 0;
457
458   Real book_height =0.;
459   for (SCM s = lines ; ly_c_pair_p (s);  s = ly_cdr (s))
460     {
461       book_height += unsmob_paper_line (ly_car (s))->dim ()[Y_AXIS];
462     }
463
464   /*
465     UGH. following stuff should go out of C++.
466    */
467   SCM scopes = SCM_EOL;
468   if (ly_c_module_p (book->header_))
469     scopes = scm_cons (book->header_, scopes);
470   
471
472   SCM tag = make_tagline (book->bookpaper_, scopes);
473   if (unsmob_stencil (tag))
474     {
475       book_height += unsmob_stencil (tag)->extent (Y_AXIS).length ();
476     }
477
478   SCM cr = make_copyright (book->bookpaper_, scopes);
479   if (unsmob_stencil (cr))
480     {
481       book_height += unsmob_stencil (cr)->extent (Y_AXIS).length ();
482     }
483
484   int page_count = int (book_height / text_height + 0.5); // ceil?
485   SCM breaks = SCM_EOL;
486   Real page_height = text_height + first;
487   Real h = 0;
488   int number = 0;
489   for (SCM s = lines; ly_c_pair_p (s); s = ly_cdr (s))
490     {
491       Paper_line *pl = unsmob_paper_line (ly_car (s));
492       if (!pl->is_title () && h < page_height)
493         number++;
494       h += pl->dim ()[Y_AXIS];
495       if (!pl->is_title () && h > page_height)
496         {
497           breaks = ly_snoc (scm_int2num (number), breaks);
498           page_number++;
499           page_height = text_height + (page_number == page_count) * last;
500           h = 0;
501         }
502       if (ly_cdr (s) == SCM_EOL)
503         breaks = ly_snoc (scm_int2num (pl->number_), breaks);
504     }
505
506   return scm_vector (breaks);
507 }
508
509 LY_DEFINE (ly_ragged_page_breaks, "ly:ragged-page-breaks",
510            5, 0, 0, (SCM lines, SCM book, SCM text, SCM first, SCM last),
511            "Return a vector with line numbers of page breaks.")
512 {
513   Paper_book* b = unsmob_paper_book (book);
514
515   SCM_ASSERT_TYPE (scm_pair_p (lines), lines, SCM_ARG1, __FUNCTION__, "list");
516   SCM_ASSERT_TYPE (b, book, SCM_ARG2, __FUNCTION__, "Paper_book");
517   SCM_ASSERT_TYPE (ly_c_number_p (text), text, SCM_ARG3, __FUNCTION__, "number");
518   SCM_ASSERT_TYPE (ly_c_number_p (first), first, SCM_ARG4, __FUNCTION__, "number");
519   SCM_ASSERT_TYPE (ly_c_number_p (last), last, SCM_ARG5, __FUNCTION__, "number");
520
521   return c_ragged_page_breaks (lines, b,
522                                ly_scm2double (text),
523                                ly_scm2double (first), ly_scm2double (last));
524 }
525
526 /****************************************************************/
527
528 Score_lines::Score_lines ()
529 {
530   lines_ = SCM_EOL;
531   header_ = SCM_EOL;
532 }
533
534 void
535 Score_lines::gc_mark ()
536 {
537   scm_gc_mark (lines_);
538   scm_gc_mark (header_);
539 }
540