]> git.donarmstrong.com Git - lilypond.git/blob - lily/paper-book.cc
(ly_bookpaper_fonts): move from Paperdef
[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 "paper-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 // JUNKME
21 SCM
22 stencil2line (Stencil stil, bool is_title = false)
23 {
24   static SCM z;
25   if (!z)
26     z = scm_permanent_object (ly_offset2scm (Offset (0, 0)));
27   Offset dim = Offset (stil.extent (X_AXIS).length (),
28                        stil.extent (Y_AXIS).length ());
29   Paper_line *pl = new Paper_line (dim, scm_cons (stil.smobbed_copy (),
30                                                   SCM_EOL),
31                                    -10001 * is_title, is_title);
32
33   return scm_gc_unprotect_object (pl->self_scm ());
34 }
35
36
37 Paper_book::Paper_book ()
38 {
39   copyright_ = SCM_EOL;
40   tagline_ = SCM_EOL;
41   
42   smobify_self ();
43 }
44
45 Paper_book::~Paper_book ()
46 {
47 }
48
49 #include "ly-smobs.icc"
50
51 IMPLEMENT_DEFAULT_EQUAL_P (Paper_book);
52 IMPLEMENT_SMOBS (Paper_book)
53 IMPLEMENT_TYPE_P (Paper_book, "ly:paper-book?")
54
55 SCM
56 Paper_book::mark_smob (SCM smob)
57 {
58   Paper_book *b = (Paper_book*) SCM_CELL_WORD_1 (smob);
59   for (int i = 0; i < b->score_lines_.size (); i++)
60     b->score_lines_[i].gc_mark ();
61
62   scm_gc_mark (b->copyright_);
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 void
80 Paper_book::output (String outname)
81 {
82   if (!score_lines_.size ())
83     // FIXME: no end-output?
84     return;
85     
86   /* Generate all stencils to trigger font loads.  */
87   SCM pages = this->pages ();
88
89   Paper_def *paper = score_lines_[0].paper_;
90   Paper_outputter *out = paper->get_paper_outputter (outname);
91   int page_count = scm_ilength (pages);
92   out->output_header (paper->bookpaper_, scopes (0), page_count, false);
93
94   for (SCM s = pages; s != SCM_EOL; s = ly_cdr (s))
95     {
96       Page *p = unsmob_page (ly_car (s));
97       progress_indication ("[" + to_string (p->number_));
98       out->output_page (p, ly_cdr (s) == SCM_EOL);
99       progress_indication ("]");
100     }
101
102   out->output_scheme (scm_list_1 (ly_symbol2scm ("end-output")));
103   progress_indication ("\n");
104 }
105
106 SCM
107 Paper_book::scopes (int i)
108 {
109   return score_lines_[i].scopes ();
110 }
111
112
113 Stencil
114 Paper_book::title (int i)
115 {
116   /*
117     TODO: get from book-paper definition.
118    */
119   SCM user_title = ly_scheme_function ("user-title");
120   SCM book_title = ly_scheme_function ("book-title");
121   SCM score_title = ly_scheme_function ("score-title");
122   SCM field = (i == 0 ? ly_symbol2scm ("bookTitle")
123                : ly_symbol2scm ("scoreTitle"));
124
125   Stencil title;
126   SCM scopes = this->scopes (i);
127   SCM s = ly_modules_lookup (scopes, field);
128   if (s != SCM_UNDEFINED && scm_variable_bound_p (s) == SCM_BOOL_T)
129     title = *unsmob_stencil (scm_call_2 (user_title,
130                                          score_lines_[0].paper_->self_scm (),
131                                         scm_variable_ref (s)));
132   else
133     title = *unsmob_stencil (scm_call_2 (i == 0 ? book_title : score_title,
134                                          score_lines_[0].paper_->self_scm (),
135                                         scopes));
136   if (!title.is_empty ())
137     title.align_to (Y_AXIS, UP);
138   
139   return title;
140 }
141
142 void
143 Paper_book::classic_output (String outname)
144 {
145   int count = score_lines_.size ();
146   Paper_def * p = score_lines_.top ().paper_;
147   Paper_outputter *out = p->get_paper_outputter (outname);
148   out->output_header (p->bookpaper_, scopes (count - 1), 0, true);
149
150   SCM top_lines = score_lines_.top ().lines_;
151   Paper_line *first = unsmob_paper_line (scm_vector_ref (top_lines,
152                                                          scm_int2num (0)));
153   Offset o (0, -0.5 * first->dim ()[Y_AXIS]);
154   int line_count = SCM_VECTOR_LENGTH (top_lines);
155   for (int i = 0; i < line_count; i++)
156     {
157       /* In classic compatibility TeX tracks how large things are, and
158          advances the Y pos for us.  If we advance it too, we get too
159          much space.
160
161          FIXME: vague... why is TeX is different from other ouput
162                 backends, why not fix the TeX backend? -- jcn */
163       if (output_format_global == "tex")
164         o = Offset (0, 0);
165
166       out->output_line (scm_vector_ref (top_lines, scm_int2num (i)),
167                         &o, i == line_count - 1);
168     }
169   
170   out->output_scheme (scm_list_1 (ly_symbol2scm ("end-output")));
171   progress_indication ("\n");
172 }
173
174 /* calculate book height, #lines, stencils.  */
175 void
176 Paper_book::init ()
177 {
178   int score_count = score_lines_.size ();
179
180   /* Calculate the full book height.  Hmm, can't we cache system
181      heights while making stencils?  */
182   height_ = 0;
183   for (int i = 0; i < score_count; i++)
184     {
185       Stencil title = this->title (i);
186       if (!title.is_empty ())
187         height_ += title.extent (Y_AXIS).length ();
188
189       int line_count = SCM_VECTOR_LENGTH (score_lines_[i].lines_);
190       for (int j = 0; j < line_count; j++)
191         {
192           SCM s = scm_vector_ref ((SCM) score_lines_[i].lines_, scm_int2num (j));
193           height_ += unsmob_paper_line (s)->dim ()[Y_AXIS];
194         }
195     }
196
197   Paper_def *paper = score_lines_[0].paper_;
198   SCM scopes = this->scopes (0);
199
200   SCM make_tagline = paper->c_variable ("make-tagline");
201   tagline_ = scm_call_2 (make_tagline, paper->self_scm (), scopes);
202   Real tag_height = 0;
203   if (Stencil *s = unsmob_stencil (tagline_))
204     tag_height = s->extent (Y_AXIS).length ();
205   height_ += tag_height;
206
207   SCM make_copyright = paper->c_variable ("make-copyright");
208   copyright_ = scm_call_2 (make_copyright, paper->self_scm (), scopes);
209   Real copy_height = 0;
210   if (Stencil *s = unsmob_stencil (copyright_))
211     copy_height = s->extent (Y_AXIS).length ();
212   height_ += copy_height;
213 }
214
215 SCM
216 Paper_book::lines ()
217 {
218   int score_count = score_lines_.size ();
219   SCM lines = SCM_EOL;
220   for (int i = 0; i < score_count; i++)
221     {
222       Stencil title = this->title (i);      
223       if (!title.is_empty ())
224         lines = ly_snoc (stencil2line (title, true), lines);
225       lines = scm_append (scm_list_2 (lines, scm_vector_to_list (score_lines_[i].lines_)));
226     }
227   
228   //debug helper; ughr
229   int i = 0;
230   for (SCM s = lines; s != SCM_EOL; s = ly_cdr (s))
231     unsmob_paper_line (ly_car (s))->number_ = ++i;
232   return lines;
233 }
234
235 SCM
236 Paper_book::pages ()
237 {
238   init ();
239   Page::page_count_ = 0;
240   Paper_def *paper = score_lines_[0].paper_;
241   Page *page = new Page (paper, 1);
242
243   Real text_height = page->text_height ();
244
245   Real copy_height = 0;
246   if (Stencil *s = unsmob_stencil (copyright_))
247     copy_height = s->extent (Y_AXIS).length ();
248
249   Real tag_height = 0;
250   if (Stencil *s = unsmob_stencil (tagline_))
251     tag_height = s->extent (Y_AXIS).length ();
252
253   SCM all = lines ();
254   SCM proc = paper->c_variable ("page-breaking");
255   SCM breaks = scm_apply_0 (proc, scm_list_n (all, scm_make_real (height_),
256                                               scm_make_real (text_height),
257                                               scm_make_real (-copy_height),
258                                               scm_make_real (-tag_height),
259                                               SCM_UNDEFINED));
260
261   /* Copyright on first page.  */
262   if (unsmob_stencil (copyright_))
263     page->copyright_ = copyright_;
264
265   SCM pages = SCM_EOL;
266   int page_count = SCM_VECTOR_LENGTH ((SCM) breaks);
267   int line = 1;
268   for (int i = 0; i < page_count; i++)
269     {
270       if (i)
271         page = new Page (paper, i + 1);
272
273       int next = i + 1 < page_count
274         ? ly_scm2int (scm_vector_ref (breaks, scm_int2num (i))) : 0;
275       while ((!next && all != SCM_EOL) || line <= next)
276         {
277           SCM s = ly_car (all);
278           page->lines_ = ly_snoc (s, page->lines_);
279           page->height_ += unsmob_paper_line (s)->dim ()[Y_AXIS];
280           page->line_count_++;
281           all = ly_cdr (all);
282           line++;
283         }
284       pages = scm_cons (page->self_scm (), pages);
285     }
286
287   /* Tagline on last page.  */
288   if (unsmob_stencil (tagline_))
289     page->tagline_ = tagline_;
290
291   return scm_reverse (pages);
292 }
293
294 static SCM
295 c_ragged_page_breaks (SCM lines, Real book_height, Real text_height,
296                       Real first, Real last)
297 {
298   int page_number = 0;
299   int page_count = int (book_height / text_height + 0.5);
300   SCM breaks = SCM_EOL;
301   Real page_height = text_height + first;
302   Real h = 0;
303   int number = 0;
304   for (SCM s = lines; s != SCM_EOL; s = ly_cdr (s))
305     {
306       Paper_line *pl = unsmob_paper_line (ly_car (s));
307       if (!pl->is_title () && h < page_height)
308         number++;
309       h += pl->dim ()[Y_AXIS];
310       if (!pl->is_title () && h > page_height)
311         {
312           breaks = ly_snoc (scm_int2num (number), breaks);
313           page_number++;
314           page_height = text_height + (page_number == page_count) * last;
315           h = 0;
316         }
317       if (ly_cdr (s) == SCM_EOL)
318         breaks = ly_snoc (scm_int2num (pl->number_), breaks);
319     }
320
321   return scm_vector (breaks);
322 }
323
324 LY_DEFINE (ly_ragged_page_breaks, "ly:ragged-page-breaks",
325            5, 0, 0, (SCM lines, SCM book, SCM text, SCM first, SCM last),
326            "Return a vector with line numbers of page breaks.")
327 {
328   SCM_ASSERT_TYPE (scm_pair_p (lines), lines, SCM_ARG1, __FUNCTION__, "list");
329   SCM_ASSERT_TYPE (ly_c_number_p (book), book, SCM_ARG2, __FUNCTION__, "real");
330   SCM_ASSERT_TYPE (ly_c_number_p (text), text, SCM_ARG2, __FUNCTION__, "real");
331   SCM_ASSERT_TYPE (ly_c_number_p (first), first, SCM_ARG2, __FUNCTION__, "real");
332   SCM_ASSERT_TYPE (ly_c_number_p (last), last, SCM_ARG2, __FUNCTION__, "real");
333
334   return c_ragged_page_breaks (lines,
335                                ly_scm2double (book), ly_scm2double (text),
336                                ly_scm2double (first), ly_scm2double (last));
337 }
338
339 /****************************************************************/
340
341 Score_lines::Score_lines ()
342 {
343   lines_ = SCM_EOL;
344   global_header_ = SCM_EOL;
345   header_ = SCM_EOL;
346   paper_ = 0;
347 }
348
349 void
350 Score_lines::gc_mark ()
351 {
352   scm_gc_mark (lines_);
353   scm_gc_mark (global_header_);
354   scm_gc_mark (header_);
355   if (paper_)
356     scm_gc_mark (paper_->self_scm ());
357 }
358
359
360 SCM 
361 Score_lines::scopes ()
362 {
363   SCM scopes = SCM_EOL;
364   if (header_)
365     scopes = scm_cons (header_, scopes);
366   if (SCM_MODULEP(global_header_)
367       && global_header_ != header_)
368     scopes = scm_cons (global_header_, scopes);
369
370   return scopes;
371 }