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