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