PageRenderTime 103ms CodeModel.GetById 29ms RepoModel.GetById 2ms app.codeStats 0ms

/source/src/Typeset/Line/lazy_paragraph.cpp

http://itexmacs.googlecode.com/
C++ | 578 lines | 452 code | 54 blank | 72 comment | 168 complexity | 12a8f47570b52a3c9cc1d395aa689cf7 MD5 | raw file
Possible License(s): GPL-3.0, GPL-2.0, MPL-2.0-no-copyleft-exception, LGPL-2.0
  1. /******************************************************************************
  2. * MODULE : lazy_paragraph.cpp
  3. * DESCRIPTION: Last pass for typesetting paragraphs;
  4. * hyphenation and creation of page items
  5. * COPYRIGHT : (C) 1999 Joris van der Hoeven
  6. *******************************************************************************
  7. * This software falls under the GNU general public license version 3 or later.
  8. * It comes WITHOUT ANY WARRANTY WHATSOEVER. For details, see the file LICENSE
  9. * in the root directory or <http://www.gnu.org/licenses/gpl-3.0.html>.
  10. ******************************************************************************/
  11. #include "Line/lazy_paragraph.hpp"
  12. #include "Line/lazy_vstream.hpp"
  13. #include "Format/format.hpp"
  14. #include "Line/lazy_vstream.hpp"
  15. #include "Boxes/construct.hpp"
  16. array<line_item> typeset_concat (edit_env env, tree t, path ip);
  17. void hyphenate (line_item item, int pos, line_item& item1, line_item& item2);
  18. array<path>
  19. line_breaks (array<line_item> a, int start, int end,
  20. SI line_width, SI first_spc, SI last_spc, bool ragged);
  21. /******************************************************************************
  22. * Constructor
  23. ******************************************************************************/
  24. lazy_paragraph_rep::lazy_paragraph_rep (edit_env env2, path ip):
  25. lazy_rep (LAZY_PARAGRAPH, ip),
  26. env (env2), style (""), sss (tm_new<stacker_rep> ())
  27. {
  28. sss->ip= ip; // is this necessary?
  29. style (PAR_FIRST) = env->read (PAR_FIRST);
  30. style (PAR_NO_FIRST)= env->read (PAR_NO_FIRST);
  31. // env->assign (PAR_NO_FIRST, "false");
  32. env->monitored_write_update (PAR_NO_FIRST, "false");
  33. SI d1, d2, d3, d4, d5, d6, d7;
  34. env->get_page_pars (width, d1, d2, d3, d4, d5, d6, d7);
  35. mode = as_string (env->read (PAR_MODE));
  36. flexibility= as_double (env->read (PAR_FLEXIBILITY));
  37. hyphen = as_string (env->read (PAR_HYPHEN));
  38. left = env->get_length (PAR_LEFT);
  39. right = env->get_length (PAR_RIGHT);
  40. bot = 0;
  41. top = env->fn->yx;
  42. sep = env->get_length (PAR_SEP);
  43. hor_sep = env->get_length (PAR_HOR_SEP);
  44. ver_sep = env->get_length (PAR_VER_SEP);
  45. height = env->as_length (string ("1fn"))+ sep;
  46. tab_sep = hor_sep;
  47. line_sep = env->get_vspace (PAR_LINE_SEP);
  48. par_sep = env->get_vspace (PAR_PAR_SEP);
  49. nr_cols = env->get_int (PAR_COLUMNS);
  50. tree dec = env->read (ATOM_DECORATIONS);
  51. if (N(dec) > 0) decs << tuple ("0", dec);
  52. }
  53. lazy_paragraph_rep::~lazy_paragraph_rep () {
  54. tm_delete (sss);
  55. }
  56. lazy_paragraph_rep::operator tree () {
  57. return "Paragraph";
  58. }
  59. /******************************************************************************
  60. * Filling lines of paragraphs
  61. ******************************************************************************/
  62. void
  63. lazy_paragraph_rep::line_print (line_item item) {
  64. // cout << "Printing: " << item << "\n";
  65. if (item->type == CONTROL_ITEM) {
  66. if (is_func (item->t, HTAB))
  67. tabs << tab (N(items), item->t);
  68. else if (is_func (item->t, VAR_VSPACE) || is_func (item->t, VSPACE)) {
  69. space vspc= env->as_vspace (item->t[0]);
  70. if (is_func (item->t, VAR_VSPACE))
  71. sss->vspace_before (vspc);
  72. else sss->vspace_after (vspc);
  73. }
  74. else if (L(item->t) == DATOMS)
  75. decs << tuple (as_string (N(items)), item->t);
  76. else if (item->t == VAR_NO_PAGE_BREAK)
  77. sss->no_page_break_before ();
  78. else if (item->t == NO_PAGE_BREAK)
  79. sss->no_page_break_after ();
  80. else if (is_tuple (item->t, "env_page") ||
  81. (item->t == PAGE_BREAK) ||
  82. (item->t == NEW_PAGE) ||
  83. (item->t == NEW_DPAGE))
  84. sss->print (item->t, nr_cols);
  85. else if (item->t == VAR_PAGE_BREAK)
  86. sss->print (PAGE_BREAK, nr_cols, true);
  87. else if (item->t == VAR_NEW_PAGE)
  88. sss->print (NEW_PAGE, nr_cols, true);
  89. else if (item->t == VAR_NEW_DPAGE)
  90. sss->print (NEW_DPAGE, nr_cols, true);
  91. }
  92. else if (item->type == FLOAT_ITEM) {
  93. fl << item->b->get_leaf_lazy ();
  94. // REPLACE item by item without lazy attachment !
  95. }
  96. if (N(spcs)>0) cur_w = cur_w + spcs[N(spcs)-1];
  97. items << item->b;
  98. spcs << item->spc;
  99. item->b->x0= cur_w->def;
  100. item->b->y0= 0;
  101. cur_w = cur_w + space (item->b->x2);
  102. }
  103. void
  104. lazy_paragraph_rep::line_print (line_item item, path left, path right) {
  105. if (is_nil (left) && is_nil (right)) line_print (item);
  106. else {
  107. line_item item1, item2;
  108. hyphenate (item, is_nil (left)? right->item: left->item, item1, item2);
  109. line_print (is_nil (left) ? item1: item2,
  110. is_nil (left) ? left : left->next,
  111. is_nil (right)? right: right->next);
  112. }
  113. }
  114. void
  115. lazy_paragraph_rep::line_print (path start, path end) {
  116. if (start->item == end->item)
  117. line_print (a[start->item], start->next, end->next);
  118. else {
  119. int i;
  120. line_print (a[start->item], start->next, path ());
  121. for (i=start->item+1; i<end->item; i++)
  122. line_print (a[i]);
  123. if (!is_atom (end))
  124. line_print (a[end->item], path (), end->next);
  125. }
  126. }
  127. /******************************************************************************
  128. * Typesetting a line
  129. ******************************************************************************/
  130. void
  131. lazy_paragraph_rep::make_unit (string mode, SI the_width, bool break_flag) {
  132. int i;
  133. // format tabs
  134. //cout << " " << N(tabs) << "] " << (cur_w->def/PIXEL)
  135. // << " < " << (the_width/PIXEL) << "? (" << break_flag << ")\n";
  136. if (break_flag && (N(tabs)>0) && (cur_w->def<the_width)) {
  137. double tot_weight= 0.0;
  138. int pos_first= -1, pos_last=-1;
  139. int num_hflush= 0;
  140. for (i=0; i<N(tabs); i++) {
  141. tab& tab_i= tabs[i];
  142. tot_weight += tab_i->weight;
  143. if (tab_i->kind == tab_first && pos_first < 0) {
  144. num_hflush++; pos_first= i; }
  145. else if (tab_i->kind == tab_last) {
  146. if (pos_last < 0) num_hflush++;
  147. pos_last= i;
  148. }
  149. else if (tab_i->kind == tab_all && tab_i->weight == 0.0)
  150. num_hflush++;
  151. }
  152. for (i=cur_start; i<N(items)-1; i++) items_sp << spcs[i]->def;
  153. for (i=0; i<N(tabs); i++) {
  154. double part;
  155. if (tot_weight==0.0) {
  156. if (i==pos_first || i==pos_last || tabs[i]->kind==tab_all)
  157. part= 1.0 / num_hflush;
  158. else part= 0.0;
  159. }
  160. else part= tabs[i]->weight / tot_weight;
  161. items_sp[tabs[i]->pos] += (SI) (part * (the_width- cur_w->def));
  162. }
  163. return;
  164. }
  165. // stretching case
  166. if (mode == "justify") {
  167. if ((cur_w->def < the_width) &&
  168. (cur_w->max > cur_w->def) &&
  169. (!break_flag)) {
  170. double f=
  171. ((double) (the_width - cur_w->def)) /
  172. ((double) (cur_w->max - cur_w->def));
  173. if (f <= flexibility) {
  174. for (i=cur_start; i<N(items)-1; i++)
  175. items_sp <<
  176. (spcs[i]->def+ ((SI) (f*((double) spcs[i]->max- spcs[i]->def))));
  177. return;
  178. }
  179. }
  180. }
  181. // shrinking case
  182. if ((cur_w->def > the_width) &&
  183. (cur_w->def > cur_w->min)) {
  184. double f=
  185. ((double) (cur_w->def - the_width)) /
  186. ((double) (cur_w->def - cur_w->min));
  187. if (f>1.0) f=1.0;
  188. for (i=cur_start; i<N(items)-1; i++)
  189. items_sp <<
  190. (spcs[i]->def- ((SI) (f*((double) spcs[i]->def- spcs[i]->min))));
  191. return;
  192. }
  193. if (mode == "center")
  194. items_sp[cur_start] += (the_width- cur_w->def) >> 1;
  195. if (mode == "right")
  196. items_sp[cur_start] += the_width- cur_w->def;
  197. for (i=cur_start; i<N(items)-1; i++)
  198. items_sp << spcs[i]->def;
  199. }
  200. /******************************************************************************
  201. * Handling decorations
  202. ******************************************************************************/
  203. void
  204. lazy_paragraph_rep::handle_decoration (
  205. int& i, int& j, SI& xoff, box& b, SI& b_sp)
  206. {
  207. string xoff_str= as_string (xoff) * "tmpt";
  208. array<box> new_items;
  209. array<SI> new_items_sp;
  210. tree t= decs[j][1]; j++;
  211. handle_decorations (i, j, xoff, new_items, new_items_sp);
  212. b_sp= new_items_sp [0]; new_items_sp[0]= 0;
  213. b = concat_box (ip, new_items, new_items_sp);
  214. int k, n=N(t);
  215. tree e (DBOX);
  216. for (k=n-1; k>=0; k--)
  217. if (is_func (t[k], MACRO, 2))
  218. e= tree (COMPOUND, t[k], e);
  219. if (e != tree (DBOX)) {
  220. // cout << "Typesetting " << e << LF;
  221. env->decorated_boxes << b;
  222. tree old_xoff= env->local_begin (XOFF_DECORATIONS, xoff_str);
  223. box bb= typeset_as_concat (env, attach_middle (e, ip));
  224. env->local_end (XOFF_DECORATIONS, old_xoff);
  225. env->decorated_boxes->resize (N (env->decorated_boxes) - 1);
  226. b= bb;
  227. }
  228. }
  229. void
  230. lazy_paragraph_rep::handle_decorations (
  231. int& i, int& j, SI& xoff, array<box>& new_items, array<SI>& new_items_sp)
  232. {
  233. while (i < N(items)) {
  234. // cout << "Handling " << items[i] << LF;
  235. if ((j < N (decs)) && (as_int (decs[j][0]) == i)) {
  236. tree t= decs[j][1];
  237. if (t == tree (DATOMS)) {
  238. xoff += items_sp[i] + items [i]->x2;
  239. new_items << items [i];
  240. new_items_sp << items_sp [i];
  241. i++; j++;
  242. return;
  243. }
  244. else {
  245. box b;
  246. SI b_sp;
  247. // cout << "Handling decoration " << t << LF << INDENT;
  248. handle_decoration (i, j, xoff, b, b_sp);
  249. // cout << UNINDENT << "Handled " << t << LF;
  250. new_items << b;
  251. new_items_sp << b_sp;
  252. }
  253. }
  254. else {
  255. xoff += items_sp[i] + items [i]->x2;
  256. new_items << items [i];
  257. new_items_sp << items_sp [i];
  258. i++;
  259. }
  260. }
  261. }
  262. void
  263. lazy_paragraph_rep::handle_decorations () {
  264. // cout << "Handling decorations: " << decs << LF << INDENT;
  265. array<box> new_items;
  266. array<SI> new_items_sp;
  267. int i=0, j=0;
  268. SI xoff= 0;
  269. handle_decorations (i, j, xoff, new_items, new_items_sp);
  270. items = new_items;
  271. items_sp= new_items_sp;
  272. // cout << UNINDENT << "Handled decorations " << decs << LF;
  273. array<tree> new_decs;
  274. for (i=0; i<N(decs); i++) {
  275. tree t= decs [i][1];
  276. if (t == tree (DATOMS))
  277. new_decs->resize (max (0, N(new_decs)-1));
  278. else new_decs << tuple ("0", t);
  279. }
  280. decs= new_decs;
  281. // cout << "Decorations on exit: " << decs << LF << HRULE;
  282. }
  283. /******************************************************************************
  284. * Making lines
  285. ******************************************************************************/
  286. void
  287. lazy_paragraph_rep::line_start () {
  288. items = array<box> ();
  289. items_sp= array<SI> ();
  290. spcs = array<space> ();
  291. fl = array<lazy> ();
  292. cur_r = 0;
  293. cur_start= 0;
  294. }
  295. void
  296. lazy_paragraph_rep::line_unit (path start, path end, bool break_flag,
  297. string mode, SI the_left, SI the_right)
  298. {
  299. tabs = array<tab> ();
  300. cur_w= space (0);
  301. int n= N(items_sp);
  302. SI m= the_left- cur_r;
  303. items_sp << m;
  304. SI the_width= the_right- the_left;
  305. line_print (start, end);
  306. make_unit (mode, the_width, break_flag);
  307. int i;
  308. cur_r= the_left+ items_sp[n]- m;
  309. for (i= cur_start; i<N(items); i++)
  310. cur_r += items[i]->w()+ (i<N(items)-1? items_sp[i+1]: 0);
  311. cur_start= N(items);
  312. }
  313. void
  314. lazy_paragraph_rep::line_end (space spc, int penalty) {
  315. if (N(items) == 0) return;
  316. if (N(decs) != 0) handle_decorations ();
  317. // cout << items << ", " << spc << ", " << penalty << LF;
  318. box b= phrase_box (sss->ip, items, items_sp);
  319. sss->print (b, fl, nr_cols);
  320. sss->print (spc);
  321. sss->penalty (penalty);
  322. sss->flush ();
  323. }
  324. void
  325. lazy_paragraph_rep::line_units (
  326. int start, int end,
  327. bool is_start, bool is_end, string mode, string hyphen,
  328. SI the_left, SI the_right, SI the_first, SI the_last)
  329. {
  330. if (start == end) return;
  331. // cout << " Line units " << start << ", " << end << "\n";
  332. // cout << " is_end : " << is_end << "\n";
  333. // cout << " mode : " << mode << "\n";
  334. // cout << " hyphen : " << hyphen << "\n";
  335. // cout << " the_left : " << (the_left/PIXEL) << "\n";
  336. // cout << " the_right: " << (the_right/PIXEL) << "\n";
  337. int i;
  338. bool ragged= (hyphen == "normal");
  339. array<path> hyphs= line_breaks (a, start, end, the_right-the_left,
  340. the_first, the_last, ragged);
  341. for (i=0; i<N(hyphs)-1; i++) {
  342. if (i>0) line_start ();
  343. line_unit (hyphs[i], hyphs[i+1], i==N(hyphs)-2, mode,
  344. the_left+ (is_start&&(i==0)? the_first: 0),
  345. the_right- (is_end&&(i==N(hyphs)-2)? the_last: 0));
  346. if (i<N(hyphs)-2) line_end (line_sep, 1);
  347. }
  348. // cout << " Done!\n";
  349. }
  350. /******************************************************************************
  351. * Typesetting a paragraph
  352. ******************************************************************************/
  353. void
  354. lazy_paragraph_rep::format_paragraph_unit (int the_start, int the_end) {
  355. // cout << "Paragraph unit " << the_start << ", " << the_end << "\n";
  356. int i, start= the_start, end= the_start;
  357. for (i=the_start; i<=the_end; i++)
  358. if ((i==the_end) ||
  359. ((a[i]->type == CONTROL_ITEM) &&
  360. (a[i]->t == NEXT_LINE)))
  361. {
  362. start= end;
  363. end = i;
  364. line_start ();
  365. line_units (start, end, start==the_start, end==the_end,
  366. mode, hyphen,
  367. left, width, first, 0);
  368. if (end<the_end) line_end (line_sep, 1);
  369. else return;
  370. }
  371. // cout << "Unit done\n";
  372. }
  373. void
  374. lazy_paragraph_rep::format_paragraph () {
  375. width -= right;
  376. int start= 0, i, j, k;
  377. // cout << "Typeset " << a << "\n";
  378. for (i=0; i<=N(a); i++) {
  379. // determine the next unit
  380. if (i<N(a)) {
  381. if (a[i]->type != CONTROL_ITEM) continue;
  382. if (a[i]->t == NEW_LINE);
  383. else continue;
  384. }
  385. // determine the style parameters
  386. bool no_first= (style [PAR_NO_FIRST] == "true");
  387. style (PAR_NO_FIRST)= "false";
  388. if (no_first) style (PAR_FIRST)= "0cm";
  389. for (j=start; j<i; j++)
  390. if (a[j]->type == CONTROL_ITEM)
  391. if (is_tuple (a[j]->t, "env_par")) {
  392. if (a[j]->t[1]->label == PAR_FIRST) {
  393. for (k=j-1; k>=start; k--)
  394. if (a[k]->b->w () != 0) break;
  395. if (k >= start) continue;
  396. }
  397. style (a[j]->t[1]->label)= a[j]->t[2];
  398. }
  399. no_first= (style [PAR_NO_FIRST] == "true");
  400. if (no_first) env->monitored_write_update (PAR_NO_FIRST, "true");
  401. if (mode == "center") first= 0;
  402. else first= env->as_length (style [PAR_FIRST]);
  403. sss->set_env_vars (height, sep, hor_sep, ver_sep, bot, top);
  404. // typeset paragraph unit
  405. format_paragraph_unit (start, i);
  406. line_end (line_sep /*+ par_sep*/, 0);
  407. sss->new_paragraph (par_sep);
  408. start= i;
  409. }
  410. // cout << "Paragraph done\n";
  411. /*
  412. array<path> ps;
  413. cout << "pass 3: " << a << "\n";
  414. ps= array<path> (N(a));
  415. for (i=0; i<N(a); i++) ps[i]= a[i]->p;
  416. cout << "paths : " << ps << "\n";
  417. */
  418. }
  419. /******************************************************************************
  420. * User interface
  421. ******************************************************************************/
  422. static array<line_item>
  423. convert (edit_env env, array<box> bs, path ip) {
  424. array<line_item> a;
  425. int i, n=N(bs);
  426. for (i=0; i<n; i++) {
  427. if (i==0) {
  428. box b= empty_box (decorate (ip), 0, 0, 0, env->fn->yx);
  429. tree ct= tuple ("env_par", PAR_FIRST, "0cm");
  430. a << line_item (CONTROL_ITEM, OP_SKIP, b, 0, ct);
  431. }
  432. a << line_item (STD_ITEM, env->mode_op, bs[i], 0);
  433. if (i<(n-1)) {
  434. box b= empty_box (decorate (ip), 0, 0, 0, env->fn->yx);
  435. a << line_item (CONTROL_ITEM, OP_SKIP, b, 0, NEXT_LINE);
  436. }
  437. }
  438. return a;
  439. }
  440. array<line_item>
  441. typeset_concat_or_table (edit_env env, tree t, path ip) {
  442. if (is_func (t, TABLE)) {
  443. array<box> bs= typeset_as_var_table (env, t, ip);
  444. return convert (env, bs, ip);
  445. }
  446. else return typeset_concat (env, t, ip);
  447. }
  448. array<page_item>
  449. typeset_stack (edit_env env, tree t, path ip,
  450. array<line_item> a, array<line_item> b, stack_border& sb)
  451. {
  452. // cout << "Typeset stack " << t << "\n";
  453. lazy_paragraph par (env, ip);
  454. par->a= a;
  455. par->a << typeset_concat_or_table (env, t, ip);
  456. par->a << b;
  457. par->format_paragraph ();
  458. sb= par->sss->sb;
  459. return par->sss->l;
  460. }
  461. lazy
  462. make_lazy_paragraph (edit_env env, tree t, path ip) {
  463. lazy_paragraph par (env, ip);
  464. par->a= typeset_concat (env, t, ip);
  465. return par;
  466. }
  467. lazy
  468. make_lazy_paragraph (edit_env env, array<box> bs, path ip) {
  469. lazy_paragraph par (env, ip);
  470. par->a= convert (env, bs, ip);
  471. return par;
  472. }
  473. array<line_item>
  474. join (array<line_item> a, array<line_item> b) {
  475. int i, m= N(a), n= N(b);
  476. array<line_item> c (m+n);
  477. for (i=0; i<m; i++) c[i ]= a[i];
  478. for (i=0; i<n; i++) c[i+m]= b[i];
  479. return c;
  480. }
  481. format
  482. lazy_paragraph_rep::query (lazy_type request, format fm) {
  483. if ((request == LAZY_BOX) && (fm->type == QUERY_VSTREAM_WIDTH)) {
  484. array<line_item> li= a;
  485. query_vstream_width qvw= (query_vstream_width) fm;
  486. if (N (qvw->before) != 0) li= join (qvw->before, li);
  487. if (N (qvw->after ) != 0) li= join (li, qvw->after);
  488. SI w= 0;
  489. int i, n= N(li);
  490. for (i=0; i<n-1; i++)
  491. w += li[i]->spc->def + li[i]->b->x2;
  492. if (i<n) w += li[i]->b->x2;
  493. w= max (w, 1); // width of a paragraph must be strictly positive for
  494. // correct positioning inside tables
  495. return make_format_width (w);
  496. }
  497. return lazy_rep::query (request, fm);
  498. }
  499. lazy
  500. lazy_paragraph_rep::produce (lazy_type request, format fm) {
  501. if (request == type) return this;
  502. if (request == LAZY_VSTREAM) {
  503. bool hidden= (N(a) == 0);
  504. if (fm->type == FORMAT_VSTREAM) {
  505. format_vstream fs= (format_vstream) fm;
  506. width= fs->width;
  507. if (N (fs->before) != 0) a= join (fs->before, a);
  508. if (N (fs->after ) != 0) a= join (a, fs->after );
  509. }
  510. format_paragraph ();
  511. /* Hide line items of height 0 */
  512. int i, n= N(sss->l);
  513. if (hidden)
  514. for (i=0; i<n; i++) {
  515. box b= sss->l[i]->b;
  516. sss->l[i]->type= PAGE_HIDDEN_ITEM;
  517. sss->l[i]->b = resize_box (ip, b, b->x1, 0, b->x2, 0);
  518. sss->l[i]->spc = space (0, 0, 0);
  519. }
  520. /* End hiding code */
  521. return lazy_vstream (ip, "", sss->l, sss->sb);
  522. }
  523. return lazy_rep::produce (request, fm);
  524. }