Back to index

texmacs  1.0.7.15
lazy_paragraph.cpp
Go to the documentation of this file.
00001 
00002 /******************************************************************************
00003 * MODULE     : lazy_paragraph.cpp
00004 * DESCRIPTION: Last pass for typesetting paragraphs;
00005 *              hyphenation and creation of page items
00006 * COPYRIGHT  : (C) 1999  Joris van der Hoeven
00007 *******************************************************************************
00008 * This software falls under the GNU general public license version 3 or later.
00009 * It comes WITHOUT ANY WARRANTY WHATSOEVER. For details, see the file LICENSE
00010 * in the root directory or <http://www.gnu.org/licenses/gpl-3.0.html>.
00011 ******************************************************************************/
00012 
00013 #include "Line/lazy_paragraph.hpp"
00014 #include "Line/lazy_vstream.hpp"
00015 #include "Format/format.hpp"
00016 #include "Line/lazy_vstream.hpp"
00017 #include "Boxes/construct.hpp"
00018 
00019 array<line_item> typeset_concat (edit_env env, tree t, path ip);
00020 void hyphenate (line_item item, int pos, line_item& item1, line_item& item2);
00021 array<path>
00022 line_breaks (array<line_item> a, int start, int end,
00023             SI line_width, SI first_spc, SI last_spc, bool ragged);
00024 
00025 /******************************************************************************
00026 * Constructor
00027 ******************************************************************************/
00028 
00029 lazy_paragraph_rep::lazy_paragraph_rep (edit_env env2, path ip):
00030   lazy_rep (LAZY_PARAGRAPH, ip),
00031   env (env2), style (""), sss (tm_new<stacker_rep> ())
00032 {
00033   sss->ip= ip; // is this necessary?
00034   style (PAR_FIRST)   = env->read (PAR_FIRST);
00035   style (PAR_NO_FIRST)= env->read (PAR_NO_FIRST);
00036   // env->assign (PAR_NO_FIRST, "false");
00037   env->monitored_write_update (PAR_NO_FIRST, "false");
00038 
00039   SI d1, d2, d3, d4, d5, d6, d7;
00040   env->get_page_pars (width, d1, d2, d3, d4, d5, d6, d7);
00041 
00042   mode       = as_string (env->read (PAR_MODE));
00043   flexibility= as_double (env->read (PAR_FLEXIBILITY));
00044   hyphen     = as_string (env->read (PAR_HYPHEN));
00045   left       = env->get_length (PAR_LEFT);
00046   right      = env->get_length (PAR_RIGHT);
00047   bot        = 0;
00048   top        = env->fn->yx;
00049   sep        = env->get_length (PAR_SEP);
00050   hor_sep    = env->get_length (PAR_HOR_SEP);
00051   ver_sep    = env->get_length (PAR_VER_SEP);
00052   height     = env->as_length (string ("1fn"))+ sep;
00053   tab_sep    = hor_sep;
00054   line_sep   = env->get_vspace (PAR_LINE_SEP);
00055   par_sep    = env->get_vspace (PAR_PAR_SEP);
00056   nr_cols    = env->get_int (PAR_COLUMNS);
00057 
00058   tree dec   = env->read (ATOM_DECORATIONS);
00059   if (N(dec) > 0) decs << tuple ("0", dec);
00060 }
00061 
00062 lazy_paragraph_rep::~lazy_paragraph_rep () {
00063   tm_delete (sss);
00064 }
00065 
00066 lazy_paragraph_rep::operator tree () {
00067   return "Paragraph";
00068 }
00069 
00070 /******************************************************************************
00071 * Filling lines of paragraphs
00072 ******************************************************************************/
00073 
00074 void
00075 lazy_paragraph_rep::line_print (line_item item) {
00076   // cout << "Printing: " << item << "\n";
00077   if (item->type == CONTROL_ITEM) {
00078     if (is_func (item->t, HTAB))
00079       tabs << tab (N(items), item->t);
00080     else if (is_func (item->t, VAR_VSPACE) || is_func (item->t, VSPACE)) {
00081       space vspc= env->as_vspace (item->t[0]);
00082       if (is_func (item->t, VAR_VSPACE))
00083        sss->vspace_before (vspc);
00084       else sss->vspace_after (vspc);
00085     }
00086     else if (L(item->t) == DATOMS)
00087       decs << tuple (as_string (N(items)), item->t);
00088     else if (item->t == VAR_NO_PAGE_BREAK)
00089       sss->no_page_break_before ();
00090     else if (item->t == NO_PAGE_BREAK)
00091       sss->no_page_break_after ();
00092     else if (is_tuple (item->t, "env_page") ||
00093             (item->t == PAGE_BREAK) ||
00094             (item->t == NEW_PAGE) ||
00095             (item->t == NEW_DPAGE))
00096       sss->print (item->t, nr_cols);
00097     else if (item->t == VAR_PAGE_BREAK)
00098       sss->print (PAGE_BREAK, nr_cols, true);
00099     else if (item->t == VAR_NEW_PAGE)
00100       sss->print (NEW_PAGE, nr_cols, true);
00101     else if (item->t == VAR_NEW_DPAGE)
00102       sss->print (NEW_DPAGE, nr_cols, true);
00103   }
00104   else if (item->type == FLOAT_ITEM) {
00105     fl << item->b->get_leaf_lazy ();
00106     // REPLACE item by item without lazy attachment !
00107   }
00108 
00109   if (N(spcs)>0) cur_w = cur_w + spcs[N(spcs)-1];
00110   items << item->b;
00111   spcs  << item->spc;
00112   item->b->x0= cur_w->def;
00113   item->b->y0= 0;
00114   cur_w =  cur_w + space (item->b->x2);
00115 }
00116 
00117 void
00118 lazy_paragraph_rep::line_print (line_item item, path left, path right) {
00119   if (is_nil (left) && is_nil (right)) line_print (item);
00120   else {
00121     line_item item1, item2;
00122     hyphenate (item, is_nil (left)? right->item: left->item, item1, item2);
00123     line_print (is_nil (left) ? item1: item2,
00124               is_nil (left) ? left : left->next,
00125               is_nil (right)? right: right->next);
00126   }
00127 }
00128 
00129 void
00130 lazy_paragraph_rep::line_print (path start, path end) {
00131   if (start->item == end->item)
00132     line_print (a[start->item], start->next, end->next);
00133   else {
00134     int i;
00135     line_print (a[start->item], start->next, path ());
00136     for (i=start->item+1; i<end->item; i++)
00137       line_print (a[i]);
00138     if (!is_atom (end))
00139       line_print (a[end->item], path (), end->next);
00140   }
00141 }
00142 
00143 /******************************************************************************
00144 * Typesetting a line
00145 ******************************************************************************/
00146 
00147 void
00148 lazy_paragraph_rep::make_unit (string mode, SI the_width, bool break_flag) {
00149   int i;
00150 
00151   // format tabs
00152   //cout << "      " << N(tabs) << "] " << (cur_w->def/PIXEL)
00153   //     << " < " << (the_width/PIXEL) << "? (" << break_flag << ")\n";
00154   if (break_flag && (N(tabs)>0) && (cur_w->def<the_width)) {
00155     double tot_weight= 0.0;
00156     int pos_first= -1, pos_last=-1;
00157     int num_hflush= 0;
00158     for (i=0; i<N(tabs); i++) {
00159       tab& tab_i= tabs[i];
00160       tot_weight += tab_i->weight;
00161       if (tab_i->kind == tab_first && pos_first < 0) {
00162        num_hflush++; pos_first= i; }
00163       else if (tab_i->kind == tab_last) {
00164        if (pos_last < 0) num_hflush++;
00165        pos_last= i; 
00166       }
00167       else if (tab_i->kind == tab_all && tab_i->weight == 0.0)
00168        num_hflush++;
00169     }
00170     for (i=cur_start; i<N(items)-1; i++) items_sp << spcs[i]->def;
00171     for (i=0; i<N(tabs); i++) {
00172       double part;
00173       if (tot_weight==0.0) {
00174        if (i==pos_first || i==pos_last || tabs[i]->kind==tab_all)
00175          part= 1.0 / num_hflush;
00176        else part= 0.0;
00177       }
00178       else part= tabs[i]->weight / tot_weight;
00179       items_sp[tabs[i]->pos] += (SI) (part * (the_width- cur_w->def));
00180     }
00181     return;
00182   }
00183 
00184   // stretching case
00185   if (mode == "justify") {
00186     if ((cur_w->def < the_width) &&
00187        (cur_w->max > cur_w->def) &&
00188        (!break_flag)) {
00189       double f=
00190        ((double) (the_width - cur_w->def)) /
00191        ((double) (cur_w->max - cur_w->def));
00192       if (f <= flexibility) {
00193         for (i=cur_start; i<N(items)-1; i++)
00194           items_sp <<
00195             (spcs[i]->def+ ((SI) (f*((double) spcs[i]->max- spcs[i]->def))));
00196         return;
00197       }
00198     }
00199   }
00200   
00201   // shrinking case
00202   if ((cur_w->def > the_width) &&
00203       (cur_w->def > cur_w->min)) {
00204     double f=
00205       ((double) (cur_w->def - the_width)) /
00206       ((double) (cur_w->def - cur_w->min));
00207     if (f>1.0) f=1.0;
00208     for (i=cur_start; i<N(items)-1; i++)
00209       items_sp <<
00210        (spcs[i]->def- ((SI) (f*((double) spcs[i]->def- spcs[i]->min))));
00211     return;
00212   }
00213 
00214   if (mode == "center")
00215     items_sp[cur_start] += (the_width- cur_w->def) >> 1;
00216   if (mode == "right")
00217     items_sp[cur_start] += the_width- cur_w->def;
00218   for (i=cur_start; i<N(items)-1; i++)
00219     items_sp << spcs[i]->def;
00220 }
00221 
00222 /******************************************************************************
00223 * Handling decorations
00224 ******************************************************************************/
00225 
00226 void
00227 lazy_paragraph_rep::handle_decoration (
00228   int& i, int& j, SI& xoff, box& b, SI& b_sp)
00229 {
00230   string xoff_str= as_string (xoff) * "tmpt";
00231   array<box> new_items;
00232   array<SI>  new_items_sp;
00233   tree t= decs[j][1]; j++;
00234   handle_decorations (i, j, xoff, new_items, new_items_sp);
00235   b_sp= new_items_sp [0]; new_items_sp[0]= 0;
00236   b   = concat_box (ip, new_items, new_items_sp);
00237 
00238   int k, n=N(t);
00239   tree e (DBOX);
00240   for (k=n-1; k>=0; k--)
00241     if (is_func (t[k], MACRO, 2))
00242       e= tree (COMPOUND, t[k], e);
00243   if (e != tree (DBOX)) {
00244     // cout << "Typesetting " << e << LF;
00245     env->decorated_boxes << b;
00246     tree old_xoff= env->local_begin (XOFF_DECORATIONS, xoff_str);
00247     box bb= typeset_as_concat (env, attach_middle (e, ip));
00248     env->local_end (XOFF_DECORATIONS, old_xoff);
00249     env->decorated_boxes->resize (N (env->decorated_boxes) - 1);
00250     b= bb;
00251   }
00252 }
00253 
00254 void
00255 lazy_paragraph_rep::handle_decorations (
00256   int& i, int& j, SI& xoff, array<box>& new_items, array<SI>& new_items_sp)
00257 {
00258   while (i < N(items)) {
00259     // cout << "Handling " << items[i] << LF;
00260     if ((j < N (decs)) && (as_int (decs[j][0]) == i)) {
00261       tree t= decs[j][1];
00262       if (t == tree (DATOMS)) {
00263        xoff += items_sp[i] + items [i]->x2;
00264        new_items    << items [i];
00265        new_items_sp << items_sp [i];
00266        i++; j++;
00267        return;
00268       }
00269       else {
00270        box b;
00271        SI  b_sp;
00272        // cout << "Handling decoration " << t << LF << INDENT;
00273        handle_decoration (i, j, xoff, b, b_sp);
00274        // cout << UNINDENT << "Handled " << t << LF;
00275        new_items    << b;
00276        new_items_sp << b_sp;
00277       }
00278     }
00279     else {
00280       xoff += items_sp[i] + items [i]->x2;
00281       new_items    << items [i];
00282       new_items_sp << items_sp [i];
00283       i++;
00284     }
00285   }
00286 }
00287 
00288 void
00289 lazy_paragraph_rep::handle_decorations () {
00290   // cout << "Handling decorations: " << decs << LF << INDENT;
00291   array<box> new_items;
00292   array<SI>  new_items_sp;
00293   int i=0, j=0;
00294   SI  xoff= 0;
00295   handle_decorations (i, j, xoff, new_items, new_items_sp);
00296   items   = new_items;
00297   items_sp= new_items_sp;
00298   // cout << UNINDENT << "Handled decorations " << decs << LF;
00299 
00300   array<tree> new_decs;
00301   for (i=0; i<N(decs); i++) {
00302     tree t= decs [i][1];
00303     if (t == tree (DATOMS))
00304       new_decs->resize (max (0, N(new_decs)-1));
00305     else new_decs << tuple ("0", t);
00306   }
00307   decs= new_decs;
00308   // cout << "Decorations on exit: " << decs << LF << HRULE;
00309 }
00310 
00311 /******************************************************************************
00312 * Making lines
00313 ******************************************************************************/
00314 
00315 void
00316 lazy_paragraph_rep::line_start () {
00317   items   = array<box> ();
00318   items_sp= array<SI> ();
00319   spcs    = array<space> ();
00320   fl      = array<lazy> ();
00321 
00322   cur_r    = 0;
00323   cur_start= 0;
00324 }
00325 
00326 void
00327 lazy_paragraph_rep::line_unit (path start, path end, bool break_flag,
00328                             string mode, SI the_left, SI the_right)
00329 {
00330   tabs = array<tab> ();
00331   cur_w= space (0);
00332   int n= N(items_sp);
00333   SI  m= the_left- cur_r;
00334   items_sp << m;
00335 
00336   SI the_width= the_right- the_left;
00337   line_print (start, end);
00338   make_unit (mode, the_width, break_flag);
00339 
00340   int i;
00341   cur_r= the_left+ items_sp[n]- m;
00342   for (i= cur_start; i<N(items); i++)
00343     cur_r += items[i]->w()+ (i<N(items)-1? items_sp[i+1]: 0);
00344   cur_start= N(items);
00345 }
00346 
00347 void
00348 lazy_paragraph_rep::line_end (space spc, int penalty) {
00349   if (N(items) == 0) return;
00350   if (N(decs) != 0) handle_decorations ();
00351   // cout << items << ", " << spc << ", " << penalty << LF;
00352   box b= phrase_box (sss->ip, items, items_sp);
00353   sss->print (b, fl, nr_cols);
00354   sss->print (spc);
00355   sss->penalty (penalty);
00356   sss->flush ();
00357 }
00358 
00359 void
00360 lazy_paragraph_rep::line_units (
00361   int start, int end,
00362   bool is_start, bool is_end, string mode, string hyphen,
00363   SI the_left, SI the_right, SI the_first, SI the_last)
00364 {
00365   if (start == end) return;
00366   // cout << "  Line units " << start << ", " << end << "\n";
00367   // cout << "    is_end   : " << is_end << "\n";
00368   // cout << "    mode     : " << mode << "\n";
00369   // cout << "    hyphen   : " << hyphen << "\n";
00370   // cout << "    the_left : " << (the_left/PIXEL) << "\n";
00371   // cout << "    the_right: " << (the_right/PIXEL) << "\n";
00372 
00373   int i;
00374   bool ragged= (hyphen == "normal");
00375   array<path> hyphs= line_breaks (a, start, end, the_right-the_left,
00376                               the_first, the_last, ragged);
00377   for (i=0; i<N(hyphs)-1; i++) {
00378     if (i>0) line_start ();
00379     line_unit (hyphs[i], hyphs[i+1], i==N(hyphs)-2, mode,
00380               the_left+ (is_start&&(i==0)? the_first: 0),
00381               the_right- (is_end&&(i==N(hyphs)-2)? the_last: 0));
00382     if (i<N(hyphs)-2) line_end (line_sep, 1);
00383   }
00384   // cout << "    Done!\n";
00385 }
00386 
00387 /******************************************************************************
00388 * Typesetting a paragraph
00389 ******************************************************************************/
00390 
00391 void
00392 lazy_paragraph_rep::format_paragraph_unit (int the_start, int the_end) {
00393   // cout << "Paragraph unit " << the_start << ", " << the_end << "\n";
00394   int i, start= the_start, end= the_start;
00395   for (i=the_start; i<=the_end; i++)
00396     if ((i==the_end) ||
00397        ((a[i]->type == CONTROL_ITEM) &&
00398         (a[i]->t == NEXT_LINE)))
00399     {
00400       start= end;
00401       end  = i;
00402       line_start ();
00403       line_units (start, end, start==the_start, end==the_end,
00404                 mode, hyphen,
00405                 left, width, first, 0);
00406       if (end<the_end) line_end (line_sep, 1);
00407       else return;
00408     }
00409   // cout << "Unit done\n";
00410 }
00411 
00412 void
00413 lazy_paragraph_rep::format_paragraph () {
00414   width -= right;
00415 
00416   int start= 0, i, j, k;
00417   // cout << "Typeset " << a << "\n";
00418   for (i=0; i<=N(a); i++) {
00419     // determine the next unit
00420     if (i<N(a)) {
00421       if (a[i]->type != CONTROL_ITEM) continue;
00422       if (a[i]->t == NEW_LINE);
00423       else continue;
00424     }
00425 
00426     // determine the style parameters
00427     bool no_first= (style [PAR_NO_FIRST] == "true");
00428     style (PAR_NO_FIRST)= "false";
00429     if (no_first) style (PAR_FIRST)= "0cm";
00430     for (j=start; j<i; j++)
00431       if (a[j]->type == CONTROL_ITEM)
00432        if (is_tuple (a[j]->t, "env_par")) {
00433          if (a[j]->t[1]->label == PAR_FIRST) {
00434            for (k=j-1; k>=start; k--)
00435              if (a[k]->b->w () != 0) break;
00436            if (k >= start) continue;
00437          }
00438          style (a[j]->t[1]->label)= a[j]->t[2];
00439        }
00440     no_first= (style [PAR_NO_FIRST] == "true");
00441     if (no_first) env->monitored_write_update (PAR_NO_FIRST, "true");
00442     if (mode == "center") first= 0;
00443     else first= env->as_length (style [PAR_FIRST]);
00444     sss->set_env_vars (height, sep, hor_sep, ver_sep, bot, top);
00445 
00446     // typeset paragraph unit
00447     format_paragraph_unit (start, i);
00448     line_end (line_sep /*+ par_sep*/, 0);
00449     sss->new_paragraph (par_sep);
00450 
00451     start= i;
00452   }
00453   // cout << "Paragraph done\n";
00454 
00455   /*
00456   array<path> ps;
00457   cout << "pass 3: " << a << "\n";
00458   ps= array<path> (N(a));
00459   for (i=0; i<N(a); i++) ps[i]= a[i]->p;
00460   cout << "paths : " << ps << "\n";
00461   */
00462 }
00463 
00464 /******************************************************************************
00465 * User interface
00466 ******************************************************************************/
00467 
00468 static array<line_item>
00469 convert (edit_env env, array<box> bs, path ip) {
00470   array<line_item> a;
00471   int i, n=N(bs);
00472   for (i=0; i<n; i++) {
00473     if (i==0) {
00474       box b= empty_box (decorate (ip), 0, 0, 0, env->fn->yx);
00475       tree ct= tuple ("env_par", PAR_FIRST, "0cm");
00476       a << line_item (CONTROL_ITEM, OP_SKIP, b, 0, ct);
00477     }
00478     a << line_item (STD_ITEM, env->mode_op, bs[i], 0);
00479     if (i<(n-1)) {
00480       box b= empty_box (decorate (ip), 0, 0, 0, env->fn->yx);
00481       a << line_item (CONTROL_ITEM, OP_SKIP, b, 0, NEXT_LINE);
00482     }
00483   }
00484   return a;
00485 }
00486 
00487 array<line_item>
00488 typeset_concat_or_table (edit_env env, tree t, path ip) {
00489   if (is_func (t, TABLE)) {
00490     array<box> bs= typeset_as_var_table (env, t, ip);
00491     return convert (env, bs, ip);
00492   }
00493   else return typeset_concat (env, t, ip);
00494 }
00495 
00496 array<page_item>
00497 typeset_stack (edit_env env, tree t, path ip,
00498               array<line_item> a, array<line_item> b, stack_border& sb)
00499 {
00500   // cout << "Typeset stack " << t << "\n";
00501   lazy_paragraph par (env, ip);
00502   par->a= a;
00503   par->a << typeset_concat_or_table (env, t, ip);
00504   par->a << b;
00505   par->format_paragraph ();
00506   sb= par->sss->sb;
00507   return par->sss->l;
00508 }
00509 
00510 lazy
00511 make_lazy_paragraph (edit_env env, tree t, path ip) {
00512   lazy_paragraph par (env, ip);
00513   par->a= typeset_concat (env, t, ip);
00514   return par;
00515 }
00516 
00517 lazy
00518 make_lazy_paragraph (edit_env env, array<box> bs, path ip) {
00519   lazy_paragraph par (env, ip);
00520   par->a= convert (env, bs, ip);
00521   return par;
00522 }
00523 
00524 array<line_item>
00525 join (array<line_item> a, array<line_item> b) {
00526   int i, m= N(a), n= N(b);
00527   array<line_item> c (m+n);
00528   for (i=0; i<m; i++) c[i  ]= a[i];
00529   for (i=0; i<n; i++) c[i+m]= b[i];
00530   return c;
00531 }
00532 
00533 format
00534 lazy_paragraph_rep::query (lazy_type request, format fm) {
00535   if ((request == LAZY_BOX) && (fm->type == QUERY_VSTREAM_WIDTH)) {
00536     array<line_item> li= a;
00537     query_vstream_width qvw= (query_vstream_width) fm;
00538     if (N (qvw->before) != 0) li= join (qvw->before, li);
00539     if (N (qvw->after ) != 0) li= join (li, qvw->after);
00540 
00541     SI w= 0;
00542     int i, n= N(li);
00543     for (i=0; i<n-1; i++)
00544       w += li[i]->spc->def + li[i]->b->x2;
00545     if (i<n) w += li[i]->b->x2;
00546     w= max (w, 1);  // width of a paragraph must be strictly positive for
00547                     // correct positioning inside tables
00548     return make_format_width (w);
00549   }
00550   return lazy_rep::query (request, fm);
00551 }
00552 
00553 lazy
00554 lazy_paragraph_rep::produce (lazy_type request, format fm) {
00555   if (request == type) return this;
00556   if (request == LAZY_VSTREAM) {
00557     bool hidden= (N(a) == 0);
00558     if (fm->type == FORMAT_VSTREAM) {
00559       format_vstream fs= (format_vstream) fm;
00560       width= fs->width;
00561       if (N (fs->before) != 0) a= join (fs->before, a);
00562       if (N (fs->after ) != 0) a= join (a, fs->after );
00563     }
00564     format_paragraph ();
00565     /* Hide line items of height 0 */
00566     int i, n= N(sss->l);
00567     if (hidden)
00568       for (i=0; i<n; i++) {
00569        box b= sss->l[i]->b;
00570        sss->l[i]->type= PAGE_HIDDEN_ITEM;
00571        sss->l[i]->b   = resize_box (ip, b, b->x1, 0, b->x2, 0);
00572        sss->l[i]->spc = space (0, 0, 0);
00573       }
00574     /* End hiding code */
00575     return lazy_vstream (ip, "", sss->l, sss->sb);
00576   }
00577   return lazy_rep::produce (request, fm);
00578 }