]> git.donarmstrong.com Git - lilypond.git/blob - lily/paper-book.cc
0fed5b78fb8b3c525da05c940dd53ce48f20f9a0
[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 <stdio.h>
10
11 #include "ly-module.hh"
12 #include "main.hh"
13 #include "paper-book.hh"
14 #include "paper-def.hh"
15 #include "paper-outputter.hh"
16 #include "paper-score.hh"
17 #include "stencil.hh"
18
19 static SCM
20 stencil2line (Stencil* stil)
21 {
22   static SCM z = ly_offset2scm (Offset (0, 0));
23   Offset dim = Offset (stil->extent (X_AXIS).length (),
24                        stil->extent (Y_AXIS).length ());
25   return scm_cons (ly_offset2scm (dim),
26                    scm_list_1 (scm_cons (z, stil->smobbed_copy ())));
27 }
28
29 static SCM
30 title2line (Stencil* stil)
31 {
32   SCM s = stencil2line (stil);
33   /* whugh, add marker recognise titles.  */
34   return scm_cons (scm_cons (ly_symbol2scm ("title"), ly_car (s)), ly_cdr (s));
35 }
36
37 /* Simplistic page interface */
38 class Page
39 {
40 public:
41   Paper_def *paper_;
42   static int page_count_;
43   int number_;
44   int line_count_;
45
46   Protected_scm lines_;
47   Protected_scm header_;
48   Protected_scm footer_;
49   Protected_scm copyright_;
50   Protected_scm tagline_;
51   
52   Stencil *get_header () { return unsmob_stencil (header_); }
53   Stencil *get_copyright () { return unsmob_stencil (copyright_); }
54   Stencil *get_tagline () { return unsmob_stencil (tagline_); }
55   Stencil *get_footer () { return unsmob_stencil (footer_); }
56
57   /* actual height filled with text.  */
58   Real height_;
59   
60   // HMMM all this size stuff to paper/paper-outputter?
61   Real hsize_;
62   Real vsize_;
63   Real left_margin_;
64   Real top_margin_;
65   Real bottom_margin_;
66   Real foot_sep_;
67   Real head_sep_;
68   Real text_width_;
69
70   /* available area for text.  */
71   Real text_height ();
72
73   Page (Paper_def*, int);
74   void output (Paper_outputter*, bool);
75 };
76
77 int Page::page_count_ = 0;
78
79 Page::Page (Paper_def *paper, int number)
80 {
81   paper_ = paper;
82   number_ = number;
83   page_count_++;
84   
85   height_ = 0;
86   lines_ = SCM_EOL;
87   line_count_ = 0;
88
89   hsize_ = paper->get_realvar (ly_symbol2scm ("hsize"));
90   vsize_ = paper->get_realvar (ly_symbol2scm ("vsize"));
91   top_margin_ = paper->get_realvar (ly_symbol2scm ("top-margin"));
92   bottom_margin_ = paper->get_realvar (ly_symbol2scm ("bottom-margin"));
93   head_sep_ = paper->get_realvar (ly_symbol2scm ("head-sep"));
94   foot_sep_ = paper->get_realvar (ly_symbol2scm ("foot-sep"));
95   text_width_ = paper->get_realvar (ly_symbol2scm ("linewidth"));
96   left_margin_ = (hsize_ - text_width_) / 2;
97   
98   copyright_ = SCM_EOL;
99   tagline_ = SCM_EOL;
100   
101   SCM make_header = scm_primitive_eval (ly_symbol2scm ("make-header"));
102   SCM make_footer = scm_primitive_eval (ly_symbol2scm ("make-footer"));
103
104   header_ = scm_call_2 (make_header, paper_->smobbed_copy (),
105                         scm_int2num (number_));
106   // FIXME: why does this (generates Stencil) not trigger font load?
107   if (get_header ())
108     get_header ()->align_to (Y_AXIS, UP);
109     
110   footer_ = scm_call_2 (make_footer, paper_->smobbed_copy (),
111                         scm_int2num (number_));
112   if (get_footer ())
113     get_footer ()->align_to (Y_AXIS, UP);
114 }
115
116 void
117 Page::output (Paper_outputter *out, bool is_last)
118 {
119   progress_indication ("[" + to_string (number_));
120   out->output_scheme (scm_list_1 (ly_symbol2scm ("start-page")));
121   Offset o (left_margin_, top_margin_);
122   Real vfill = line_count_ > 1 ? (text_height () - height_) / (line_count_ - 1)
123     : 0;
124   if (get_header ())
125     {
126       out->output_line (stencil2line (get_header ()), &o, false);
127       o[Y_AXIS] += head_sep_;
128     }
129   for (SCM s = lines_; gh_pair_p (s); s = ly_cdr (s))
130     {
131       SCM line = ly_car (s);
132       SCM offset = ly_car (line);
133       if (ly_car (offset) == ly_symbol2scm ("title"))
134         line = scm_cons (ly_cdr (offset), ly_cdr (line));
135       out->output_line (line, &o,
136                         is_last && gh_pair_p (ly_cdr (s)) && !get_copyright ()
137                         && !get_tagline () && !get_footer ());
138       if (gh_pair_p (ly_cdr (s)) && ly_car (offset) != ly_symbol2scm ("title"))
139         o[Y_AXIS] += vfill; 
140     }
141   if (get_copyright () || get_tagline () || get_footer ())
142     o[Y_AXIS] += foot_sep_;
143   if (get_copyright ())
144     out->output_line (stencil2line (get_copyright ()), &o,
145                       is_last && !get_tagline () && !get_footer ());
146   if (get_tagline ())
147     out->output_line (stencil2line (get_tagline ()), &o,
148                       is_last && !get_footer ());
149   if (get_footer ())
150     out->output_line (stencil2line (get_footer ()), &o, is_last);
151   out->output_scheme (scm_list_2 (ly_symbol2scm ("stop-page"),
152                                   gh_bool2scm (is_last && !get_footer ())));
153   progress_indication ("]");
154 }
155
156 Real
157 Page::text_height ()
158 {
159   Real h = vsize_ - top_margin_ - bottom_margin_;
160   if (get_header ())
161     h -= get_header ()->extent (Y_AXIS).length () + head_sep_;
162   if (get_copyright () || get_tagline () || get_footer ())
163     h -= foot_sep_;
164   if (get_copyright ())
165     h -= get_copyright ()->extent (Y_AXIS).length ();
166   if (get_tagline ())
167     h -= get_tagline ()->extent (Y_AXIS).length ();
168   if (get_footer ())
169     h -= get_footer ()->extent (Y_AXIS).length ();
170   return h;
171 }
172
173 /****************************************************************/
174
175 /* Current global paper book.  Gives default_rendering access from
176    input-file-results.  */
177 Paper_book *paper_book;
178
179 Paper_book::Paper_book ()
180 {
181   smobify_self ();
182 }
183
184 Paper_book::~Paper_book ()
185 {
186 }
187
188 void
189 Paper_book::output (String outname)
190 {
191   /* Generate all stencils to trigger font loads.  */
192   Link_array<Page> *pages = get_pages ();
193
194   Paper_def *paper = papers_[0];
195   Paper_outputter *out = paper->get_paper_outputter (outname);
196   out->output_metadata (get_scopes (0), paper);
197   out->output_header (paper);
198
199   int page_count = pages->size ();
200   for (int i = 0; i < page_count; i++)
201     (*pages)[i]->output (out, i + 1 == page_count);
202
203   out->output_scheme (scm_list_1 (ly_symbol2scm ("end-output")));
204   progress_indication ("\n");
205 }
206
207 SCM
208 Paper_book::get_scopes (int i)
209 {
210   SCM scopes = SCM_EOL;
211   if (headers_[i])
212     scopes = scm_cons (headers_[i], scopes);
213   if (global_headers_[i] && global_headers_[i] != headers_[i])
214     scopes = scm_cons (global_headers_[i], scopes);
215   return scopes;
216 }
217
218 Stencil*
219 Paper_book::get_title (int i)
220 {
221   SCM user_title = scm_primitive_eval (ly_symbol2scm ("user-title"));
222   SCM book_title = scm_primitive_eval (ly_symbol2scm ("book-title"));
223   SCM score_title = scm_primitive_eval (ly_symbol2scm ("score-title"));
224   SCM field = (i == 0 ? ly_symbol2scm ("bookTitle")
225                : ly_symbol2scm ("scoreTitle"));
226
227   Stencil *title = 0;
228   SCM scopes = get_scopes (i);
229   SCM s = ly_modules_lookup (scopes, field);
230   if (s != SCM_UNDEFINED && scm_variable_bound_p (s) == SCM_BOOL_T)
231     title = unsmob_stencil (scm_call_2 (user_title,
232                                         papers_[0]->self_scm (),
233                                         scm_variable_ref (s)));
234   else
235     title = unsmob_stencil (scm_call_2 (i == 0 ? book_title : score_title,
236                                         papers_[0]->self_scm (),
237                                         scopes));
238   if (title)
239     title->align_to (Y_AXIS, UP);
240   
241   return title;
242 }
243
244 /* Ideas:
245    - real page breaking algorithm (Gourlay?)
246    - override: # pages, or pageBreakLines= #'(3 3 4), ?  */
247 Link_array<Page>*
248 Paper_book::get_pages ()
249 {
250   Link_array<Page> *pages = new Link_array<Page>;
251   int score_count = scores_.size ();
252
253   /* Calculate the full book height.  Hmm, can't we cache system
254      heights while making stencils?  */
255   Real book_height = 0;
256   for (int i = 0; i < score_count; i++)
257     {
258       Stencil *title = get_title (i);
259       if (title)
260         book_height += title->extent (Y_AXIS).length ();
261
262       int line_count = SCM_VECTOR_LENGTH ((SCM) scores_[i]);
263       for (int j = 0; j < line_count; j++)
264         {
265           SCM line = scm_vector_ref ((SCM) scores_[i], scm_int2num (j));
266           book_height += ly_scm2offset (ly_car (line))[Y_AXIS];
267         }
268     }
269
270   Paper_def *paper = papers_[0];
271   SCM scopes = get_scopes (0);
272
273   SCM make_tagline = scm_primitive_eval (ly_symbol2scm ("make-tagline"));
274   SCM tagline = scm_call_2 (make_tagline, paper->smobbed_copy (), scopes);
275   Real tag_height = 0;
276   if (Stencil *s = unsmob_stencil (tagline))
277     tag_height = s->extent (Y_AXIS).length ();
278   book_height += tag_height;
279
280   SCM make_copyright = scm_primitive_eval (ly_symbol2scm ("make-copyright"));
281   SCM copyright = scm_call_2 (make_copyright, paper->smobbed_copy (), scopes);
282   Real copy_height = 0;
283   if (Stencil *s = unsmob_stencil (copyright))
284     copy_height = s->extent (Y_AXIS).length ();
285   book_height += copy_height;
286   
287   Page::page_count_ = 0;
288   int page_number = 0;
289   Page *page = new Page (paper, ++page_number);
290   int page_count = int (book_height / page->text_height () + 0.5);
291   if (unsmob_stencil (copyright))
292     page->copyright_ = copyright;
293   if (unsmob_stencil (tagline) && page_number == page_count)
294     page->tagline_ = tagline;
295   
296 #if 0  
297   fprintf (stderr, "book_height: %f\n", book_height);
298   fprintf (stderr, "vsize: %f\n", page->vsize_);
299   fprintf (stderr, "pages: %d\n", page_count);
300 #endif
301
302   /* Simplistic page breaking.  */
303   Real text_height = page->text_height ();
304   for (int i = 0; i < score_count; i++)
305     {
306       Real h = 0;
307       Stencil *title = get_title (i);
308       if (title)
309         h = title->extent (Y_AXIS).length ();
310
311       int line_count = SCM_VECTOR_LENGTH ((SCM) scores_[i]);
312       for (int j = 0; j < line_count; j++)
313         {
314           SCM line = scm_vector_ref ((SCM) scores_[i], scm_int2num (j));
315           h += ly_scm2offset (ly_car (line))[Y_AXIS];
316           if (page->height_ + h > text_height)
317             {
318               pages->push (page);
319               page = new Page (paper, ++page_number);
320               if (unsmob_stencil (tagline) && page_number == page_count)
321                 page->tagline_ = tagline;
322               text_height = page->text_height ();
323             }
324           if (page->height_ + h <= text_height || page->height_ == 0)
325             {
326               if (j == 0 && title)
327                 page->lines_
328                   = ly_snoc (title2line (title), page->lines_);
329               page->lines_ = ly_snoc (line, page->lines_);
330               page->line_count_++;
331               page->height_ += h;
332               h = 0;
333             }
334         }
335     }
336   
337   pages->push (page);
338   return pages;
339 }
340
341 void
342 Paper_book::classic_output (String outname)
343 {
344   Paper_outputter *out = papers_.top ()->get_paper_outputter (outname);
345   int count = scores_.size ();
346   
347   out->output_metadata (get_scopes (count - 1), papers_.top ());
348   out->output_header (papers_.top ());
349
350   int line_count = SCM_VECTOR_LENGTH ((SCM) scores_.top ());
351   for (int i = 0; i < line_count; i++)
352     out->output_line (scm_vector_ref ((SCM) scores_.top (), scm_int2num (i)),
353                       0, i == line_count - 1);
354   
355   out->output_scheme (scm_list_1 (ly_symbol2scm ("end-output")));
356   progress_indication ("\n");
357 }
358
359
360 #include "ly-smobs.icc"
361
362 IMPLEMENT_DEFAULT_EQUAL_P (Paper_book);
363 IMPLEMENT_SMOBS (Paper_book)
364 IMPLEMENT_TYPE_P (Paper_book, "ly:paper-book?")
365
366 SCM
367 Paper_book::mark_smob (SCM smob)
368 {
369   Paper_book *pb = (Paper_book*) SCM_CELL_WORD_1 (smob);
370   for (int i = 0; i < pb->headers_.size (); i++)
371     scm_gc_mark (pb->headers_[i]);
372   for (int i = 0; i < pb->global_headers_.size (); i++)
373     scm_gc_mark (pb->global_headers_[i]);
374   for (int i = 0; i < pb->papers_.size (); i++)
375     scm_gc_mark (pb->papers_[i]->self_scm ());
376   for (int i = 0; i < pb->scores_.size (); i++)
377     scm_gc_mark (pb->scores_[i]);
378   return SCM_EOL;
379 }
380
381 int
382 Paper_book::print_smob (SCM smob, SCM port, scm_print_state*)
383 {
384   Paper_book *b = (Paper_book*) ly_cdr (smob);
385      
386   scm_puts ("#<", port);
387   scm_puts (classname (b), port);
388   scm_puts (" ", port);
389   //scm_puts (b->, port);
390   scm_puts (">", port);
391   return 1;
392 }