1 /* box.c: Box generation routines for libRUIN
2  * Copyright (C) 2011 Julian Graham
3  *
4  * libRUIN is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include <assert.h>
19 #include <glib.h>
20 #include <libguile.h>
21 #include <math.h>
22 #include <strings.h>
23 
24 #include "box.h"
25 #include "css.h"
26 #include "parse.h"
27 #include "window.h"
28 
29 static GHashTable *display_table = NULL;
30 static GHashTable *display_table_cell = NULL;
31 static GHashTable *display_table_column = NULL;
32 static GHashTable *display_table_row = NULL;
33 
34 static GList *box_generate
35 (ruin_window_t *, ruin_node_t *, ruin_box_t *, ruin_box_t *);
36 
37 typedef struct {
38   ruin_node_t *node;
39   char *display;
40 } ruin_node_display_t;
41 
inline_box_type(enum ruin_layout_box_type t)42 int inline_box_type (enum ruin_layout_box_type t)
43 {
44   return t == RUIN_LAYOUT_BOX_TYPE_INLINE ||
45     t == RUIN_LAYOUT_BOX_TYPE_ANONYMOUS_INLINE;
46 }
47 
inline_display(char * display)48 int inline_display (char *display)
49 {
50   return strcmp (display, "inline") == 0;
51 }
52 
row_group_box(char * display)53 static int row_group_box (char *display)
54 {
55   return strcmp (display, "table-row-group") == 0
56     || strcmp (display, "table-header-group") == 0
57     || strcmp (display, "table-footer-group") == 0;
58 }
59 
proper_table_child(char * display)60 static int proper_table_child (char *display)
61 {
62   return strcmp (display, "table-caption") == 0
63     || strcmp (display, "table-column") == 0
64     || strcmp (display, "table-column-group") == 0
65     || strcmp (display, "table-row") == 0
66     || row_group_box (display);
67 }
68 
display_to_box_type(char * disp)69 static enum ruin_layout_box_type display_to_box_type (char *disp)
70 {
71   if (strcmp (disp, "block") == 0)
72     return RUIN_LAYOUT_BOX_TYPE_BLOCK;
73   else if (strcmp (disp, "list-item") == 0)
74     return RUIN_LAYOUT_BOX_TYPE_BLOCK;
75   else if (strcmp (disp, "inline") == 0)
76     return RUIN_LAYOUT_BOX_TYPE_INLINE;
77   else if (strcmp (disp, "none") == 0)
78     return RUIN_LAYOUT_BOX_TYPE_NONE;
79   else if (strcmp (disp, "table") == 0
80 	   || strcmp (disp, "table-caption") == 0
81 	   || strcmp (disp, "table-cell") == 0
82 	   || strcmp (disp, "table-column") == 0
83 	   || strcmp (disp, "table-column-group") == 0
84 	   || strcmp (disp, "table-row") == 0
85 	   || row_group_box (disp))
86     return RUIN_LAYOUT_BOX_TYPE_BLOCK;
87 
88   assert (1 == 0);
89 }
90 
get_box_type(ruin_window_t * w,ruin_node_t * n)91 static enum ruin_layout_box_type get_box_type (ruin_window_t *w, ruin_node_t *n)
92 {
93   switch (n->type)
94     {
95     case RUIN_PARSE_NODE_TYPE_ELEMENT:
96       return display_to_box_type (ruin_css_lookup (w, n, "display"));
97     case RUIN_PARSE_NODE_TYPE_PROTOTYPE:
98       return RUIN_LAYOUT_BOX_TYPE_ANONYMOUS_BLOCK;
99     case RUIN_PARSE_NODE_TYPE_TEXT:
100       return RUIN_LAYOUT_BOX_TYPE_ANONYMOUS_INLINE;
101     default: assert (1 == 0);
102     }
103 }
104 
ruin_box_new(enum ruin_layout_box_type type,ruin_box_t * p,ruin_box_t * cb,ruin_window_t * w,ruin_node_t * t)105 ruin_box_t *ruin_box_new
106 (enum ruin_layout_box_type type, ruin_box_t *p, ruin_box_t *cb,
107  ruin_window_t *w, ruin_node_t *t)
108 {
109   ruin_box_t *box = NULL;
110   if (type == RUIN_LAYOUT_BOX_TYPE_INLINE ||
111       type == RUIN_LAYOUT_BOX_TYPE_ANONYMOUS_INLINE)
112     box = calloc (1, sizeof (ruin_inline_box_t));
113   else if (type == RUIN_LAYOUT_BOX_TYPE_MARKER)
114     box = calloc (1, sizeof (ruin_marker_box_t));
115   else box = calloc (1, sizeof (ruin_box_t));
116 
117   if (type == RUIN_LAYOUT_BOX_TYPE_LINE)
118     box->height.used = 1;
119 
120   box->line_height.used = 1;
121 
122   box->type = type;
123   box->containing_block = cb;
124   box->parent = p;
125   box->window = w;
126   box->generator = t;
127 
128   box->visible = TRUE;
129 
130   return box;
131 }
132 
ruin_inline_box_new(enum ruin_layout_box_type type,ruin_box_t * p,ruin_box_t * cb,ruin_window_t * w,ruin_node_t * t,char * content_ptr)133 ruin_inline_box_t *ruin_inline_box_new
134 (enum ruin_layout_box_type type, ruin_box_t *p, ruin_box_t *cb,
135  ruin_window_t *w, ruin_node_t *t, char *content_ptr)
136 {
137   ruin_inline_box_t *box = (ruin_inline_box_t *)
138     ruin_box_new (type, p, cb, w, t);
139   box->content_ptr = content_ptr;
140   return box;
141 }
142 
ruin_marker_box_new(enum ruin_layout_box_type type,ruin_box_t * p,ruin_box_t * cb,ruin_window_t * w,ruin_node_t * t,enum ruin_layout_list_style_type style,int ordinal,int length)143 ruin_marker_box_t *ruin_marker_box_new
144 (enum ruin_layout_box_type type, ruin_box_t *p, ruin_box_t *cb,
145  ruin_window_t *w, ruin_node_t *t, enum ruin_layout_list_style_type style,
146  int ordinal, int length)
147 {
148   ruin_marker_box_t *box = (ruin_marker_box_t *)
149     ruin_box_new (type, p, cb, w, t);
150 
151   box->style = style;
152   box->ordinal = ordinal;
153   box->length = length;
154 
155   return box;
156 }
157 
ruin_box_clone(ruin_box_t * src,short deep)158 ruin_box_t *ruin_box_clone (ruin_box_t *src, short deep)
159 {
160   ruin_box_t *b = NULL;
161 
162   assert (deep == FALSE); /* for now */
163 
164   switch (src->type)
165     {
166     case RUIN_LAYOUT_BOX_TYPE_INLINE:
167     case RUIN_LAYOUT_BOX_TYPE_ANONYMOUS_INLINE:
168       b = (ruin_box_t *) ruin_inline_box_new
169 	(src->type, src->parent, src->containing_block, src->window,
170 	 src->generator, ((ruin_inline_box_t *) src)->content_ptr);
171       break;
172     case RUIN_LAYOUT_BOX_TYPE_MARKER:
173       b = (ruin_box_t *) ruin_marker_box_new
174 	(src->type, src->parent, src->containing_block, src->window,
175 	 src->generator, ((ruin_marker_box_t *) src)->style,
176 	 ((ruin_marker_box_t *) src)->ordinal,
177 	 ((ruin_marker_box_t *) src)->length);
178       break;
179     default:
180       b = ruin_box_new
181 	(src->type, src->parent, src->containing_block, src->window,
182 	 src->generator);
183     }
184 
185   b->visible = src->visible;
186   b->level = src->level;
187 
188   b->top = src->top;
189   b->left = src->left;
190 
191   b->width = src->width;
192   b->height = src->height;
193 
194   b->min_width = src->min_width;
195   b->max_width = src->max_width;
196 
197   b->min_height = src->min_height;
198   b->max_height = src->max_height;
199 
200   b->margin_top = src->margin_top;
201   b->margin_left = src->margin_left;
202   b->margin_bottom = src->margin_bottom;
203   b->margin_right = src->margin_right;
204 
205   b->padding_top = src->padding_top;
206   b->padding_left = src->padding_left;
207   b->padding_bottom = src->padding_bottom;
208   b->padding_right = src->padding_right;
209 
210   b->border_top_width = src->border_top_width;
211   b->border_left_width = src->border_left_width;
212   b->border_bottom_width = src->border_bottom_width;
213   b->border_right_width = src->border_right_width;
214 
215   b->letter_spacing = src->letter_spacing;
216   b->word_spacing = src->word_spacing;
217   b->line_height = src->line_height;
218 
219   b->text_indent = src->text_indent;
220 
221   return b;
222 }
223 
ruin_box_free(ruin_box_t * box)224 void ruin_box_free (ruin_box_t *box)
225 {
226   free (box);
227 }
228 
generate_block(ruin_window_t * w,ruin_node_t * e,ruin_box_t * p,ruin_box_t * cb)229 static ruin_box_t *generate_block
230 (ruin_window_t *w, ruin_node_t *e, ruin_box_t *p, ruin_box_t *cb)
231 {
232   ruin_box_t *b = ruin_box_new (RUIN_LAYOUT_BOX_TYPE_BLOCK, p, cb, w, e);
233   if (e->first_child != NULL)
234     b->children = box_generate (w, e->first_child, b, b);
235   return b;
236 }
237 
generate_table_caption(ruin_window_t * w,ruin_node_t * e,ruin_box_t * p,ruin_box_t * cb)238 static ruin_box_t *generate_table_caption
239 (ruin_window_t *w, ruin_node_t *e, ruin_box_t *p, ruin_box_t *cb)
240 {
241   ruin_box_t *b = ruin_box_new (get_box_type (w, e), p, cb, w, e);
242   if (e->first_child != NULL)
243     b->children = box_generate (w, e->first_child, b, b);
244   return b;
245 }
246 
generate_table_cell(ruin_window_t * w,ruin_node_t * e,ruin_box_t * p,ruin_box_t * cb)247 static ruin_box_t *generate_table_cell
248 (ruin_window_t *w, ruin_node_t *e, ruin_box_t *p, ruin_box_t *cb)
249 {
250   ruin_box_t *b = ruin_box_new (get_box_type (w, e), p, cb, w, e);
251   if (e->first_child != NULL)
252     b->children = box_generate (w, e->first_child, b, b);
253   return b;
254 }
255 
256 typedef ruin_box_t *(*generation_function)
257   (ruin_window_t *, ruin_node_t *, ruin_box_t *, ruin_box_t *);
258 
259 typedef struct _generation_context
260 {
261   ruin_window_t *window;
262   ruin_box_t *parent;
263   ruin_box_t *containing_block;
264 
265   generation_function generator;
266 } generation_context;
267 
268 typedef struct _prototype_wrapper_generation_context
269 {
270   generation_context *subcontext;
271 
272   int (*display_predicate)(char *);
273   GHashTable *prototype_properties;
274   GList *wrap_children;
275 } prototype_wrapper_generation_context;
276 
generation_context_init(generation_context * context,ruin_window_t * window,ruin_box_t * parent,ruin_box_t * containing_block,generation_function generator)277 static void generation_context_init
278 (generation_context *context, ruin_window_t *window, ruin_box_t *parent,
279  ruin_box_t *containing_block, generation_function generator)
280 {
281   context->window = window;
282   context->parent = parent;
283   context->containing_block = containing_block;
284   context->generator = generator;
285 }
286 
prototype_wrapper_generation_context_init(prototype_wrapper_generation_context * context,generation_context * subcontext,int (* display_predicate)(char *),GHashTable * prototype_properties,GList * wrap_children)287 static void prototype_wrapper_generation_context_init
288 (prototype_wrapper_generation_context *context, generation_context *subcontext,
289  int (*display_predicate)(char *), GHashTable *prototype_properties,
290  GList *wrap_children)
291 {
292   context->subcontext = subcontext;
293   context->display_predicate = display_predicate;
294   context->prototype_properties = prototype_properties;
295   context->wrap_children = wrap_children;
296 }
297 
generate_child(ruin_node_t * e,generation_context * context)298 static ruin_box_t *generate_child
299 (ruin_node_t *e, generation_context *context)
300 {
301   return context->generator
302     (context->window, e, context->parent, context->containing_block);
303 }
304 
is_table_cell(char * display)305 static int is_table_cell (char *display)
306 {
307   return strcmp (display, "table-cell") == 0;
308 }
309 
is_table_column(char * display)310 static int is_table_column (char *display)
311 {
312   return strcmp (display, "table-column") == 0;
313 }
314 
is_table_row(char * display)315 static int is_table_row (char *display)
316 {
317   return strcmp (display, "table-row") == 0;
318 }
319 
generate_prototype_child(prototype_wrapper_generation_context * context)320 static void generate_prototype_child
321 (prototype_wrapper_generation_context *context)
322 {
323   ruin_box_t *b = context->subcontext->parent;
324   ruin_node_t *prototype = (ruin_node_t *) ruin_node_prototype_new
325     (context->prototype_properties);
326 
327   prototype->children = context->wrap_children;
328   prototype->first_child = g_list_nth_data (prototype->children, 0);
329 
330   b->children = g_list_append
331     (b->children, generate_child (prototype, context->subcontext));
332   context->wrap_children = NULL;
333 }
334 
generate_wrapped_child(gpointer data,gpointer user_data)335 static void generate_wrapped_child (gpointer data, gpointer user_data)
336 {
337   ruin_node_t *c = (ruin_node_t *) data;
338 
339   prototype_wrapper_generation_context *context =
340     (prototype_wrapper_generation_context *) user_data;
341 
342   ruin_window_t *w = context->subcontext->window;
343   ruin_box_t *b = context->subcontext->parent;
344 
345   char *display = ruin_css_lookup (w, c, "display");
346   if (context->display_predicate (display))
347     {
348       if (g_list_length (context->wrap_children) > 0)
349 	generate_prototype_child (context);
350       b->children = g_list_append
351 	(b->children, generate_child (c, context->subcontext));
352     }
353   else context->wrap_children = g_list_append (context->wrap_children, c);
354   if (c->next_sibling == NULL && g_list_length (context->wrap_children) > 0)
355     generate_prototype_child (context);
356 }
357 
generate_table_row(ruin_window_t * w,ruin_node_t * e,ruin_box_t * p,ruin_box_t * cb)358 static ruin_box_t *generate_table_row
359 (ruin_window_t *w, ruin_node_t *e, ruin_box_t *p, ruin_box_t *cb)
360 {
361   ruin_box_t *b = ruin_box_new (get_box_type (w, e), p, cb, w, e);
362 
363   generation_context context;
364   prototype_wrapper_generation_context wrapping_context;
365 
366   generation_context_init (&context, w, b, b, generate_table_cell);
367   prototype_wrapper_generation_context_init
368     (&wrapping_context, &context, is_table_cell, display_table_cell, NULL);
369 
370   g_list_foreach (e->children, generate_wrapped_child, &wrapping_context);
371   return b;
372 }
373 
generate_table_row_group(ruin_window_t * w,ruin_node_t * e,ruin_box_t * p,ruin_box_t * cb)374 static ruin_box_t *generate_table_row_group
375 (ruin_window_t *w, ruin_node_t *e, ruin_box_t *p, ruin_box_t *cb)
376 {
377   ruin_box_t *b = ruin_box_new (get_box_type (w, e), p, cb, w, e);
378 
379   generation_context context;
380   prototype_wrapper_generation_context wrapping_context;
381 
382   generation_context_init (&context, w, b, b, generate_table_row);
383   prototype_wrapper_generation_context_init
384     (&wrapping_context, &context, is_table_row, display_table_row, NULL);
385 
386   g_list_foreach (e->children, generate_wrapped_child, &wrapping_context);
387   return b;
388 }
389 
generate_table_column(ruin_window_t * w,ruin_node_t * e,ruin_box_t * p,ruin_box_t * cb)390 static ruin_box_t *generate_table_column
391 (ruin_window_t *w, ruin_node_t *e, ruin_box_t *p, ruin_box_t *cb)
392 {
393   ruin_box_t *b = ruin_box_new (get_box_type (w, e), p, cb, w, e);
394 
395   generation_context context;
396   prototype_wrapper_generation_context wrapping_context;
397 
398   generation_context_init (&context, w, b, b, generate_table_cell);
399   prototype_wrapper_generation_context_init
400     (&wrapping_context, &context, is_table_cell, display_table_cell, NULL);
401 
402   g_list_foreach (e->children, generate_wrapped_child, &wrapping_context);
403   return b;
404 }
405 
generate_table_column_group(ruin_window_t * w,ruin_node_t * e,ruin_box_t * p,ruin_box_t * cb)406 static ruin_box_t *generate_table_column_group
407 (ruin_window_t *w, ruin_node_t *e, ruin_box_t *p, ruin_box_t *cb)
408 {
409   ruin_box_t *b = ruin_box_new (get_box_type (w, e), p, cb, w, e);
410 
411   generation_context context;
412   prototype_wrapper_generation_context wrapping_context;
413 
414   generation_context_init (&context, w, b, b, generate_table_column);
415   prototype_wrapper_generation_context_init
416     (&wrapping_context, &context, is_table_column, display_table_column, NULL);
417 
418   g_list_foreach (e->children, generate_wrapped_child, &wrapping_context);
419   return b;
420 }
421 
generate_table(ruin_window_t * w,ruin_node_t * e,ruin_box_t * p,ruin_box_t * cb)422 static ruin_box_t *generate_table
423 (ruin_window_t *w, ruin_node_t *e, ruin_box_t *p, ruin_box_t *cb)
424 {
425   ruin_box_t *b = ruin_box_new (get_box_type (w, e), p, cb, w, e);
426   ruin_node_t *c = e->first_child;
427   while (c != NULL)
428     {
429       char *display = ruin_css_lookup (w, c, "display");
430       if (proper_table_child (display))
431 	{
432 	  ruin_box_t *child_box = NULL;
433 	  if (row_group_box (display))
434 	    child_box = generate_table_row_group (w, c, b, b);
435 	  else if (strcmp (display, "table-caption") == 0)
436 	    child_box = generate_table_caption (w, c, b, b);
437 	  else if (strcmp (display, "table-row") == 0)
438 	    child_box = generate_table_row (w, c, b, b);
439 	  else if (strcmp (display, "table-column") == 0)
440 	    child_box = generate_table_column (w, c, b, b);
441 	  else if (strcmp (display, "table-column-group") == 0)
442 	    child_box = generate_table_column_group (w, c, b, b);
443 
444 	  b->children = g_list_append (b->children, child_box);
445 	  c = c->next_sibling;
446 	}
447       else
448 	{
449 	  ruin_node_t *prototype_table_row = (ruin_node_t *)
450 	    ruin_node_prototype_new (display_table_row);
451 	  while (!proper_table_child (display))
452 	     {
453 	       if (prototype_table_row->first_child == NULL)
454 		 prototype_table_row->first_child = c;
455 
456 	       prototype_table_row->children =
457 		 g_list_append (prototype_table_row->children, c);
458 	       c = c->next_sibling;
459 	       if (c == NULL)
460 		 break;
461 	       display = ruin_css_lookup (w, c, "display");
462 	     }
463 
464 	  b->children = g_list_append
465 	    (b->children, generate_table_row (w, prototype_table_row, b, b));
466 	}
467     }
468 
469   return b;
470 }
471 
parse_list_style_type(char * style)472 static enum ruin_layout_list_style_type parse_list_style_type (char *style)
473 {
474   if (strcmp (style, "disc") == 0)
475     return RUIN_LAYOUT_LIST_STYLE_TYPE_DISC;
476   else if (strcmp (style, "circle") == 0)
477     return RUIN_LAYOUT_LIST_STYLE_TYPE_CIRCLE;
478   else if (strcmp (style, "square") == 0)
479     return RUIN_LAYOUT_LIST_STYLE_TYPE_SQUARE;
480   else if (strcmp (style, "decimal") == 0)
481     return RUIN_LAYOUT_LIST_STYLE_TYPE_DECIMAL;
482   else if (strcmp (style, "decimal-leading-zero") == 0)
483     return RUIN_LAYOUT_LIST_STYLE_TYPE_DECIMAL_LEADING_ZERO;
484   else if (strcmp (style, "lower-roman") == 0)
485     return RUIN_LAYOUT_LIST_STYLE_TYPE_LOWER_ROMAN;
486   else if (strcmp (style, "upper-roman") == 0)
487     return RUIN_LAYOUT_LIST_STYLE_TYPE_UPPER_ROMAN;
488   else if (strcmp (style, "lower-greek") == 0)
489     return RUIN_LAYOUT_LIST_STYLE_TYPE_LOWER_GREEK;
490   else if (strcmp (style, "lower-latin") == 0)
491     return RUIN_LAYOUT_LIST_STYLE_TYPE_LOWER_LATIN;
492   else if (strcmp (style, "upper-latin") == 0)
493     return RUIN_LAYOUT_LIST_STYLE_TYPE_UPPER_LATIN;
494   else if (strcmp (style, "armenian") == 0)
495     return RUIN_LAYOUT_LIST_STYLE_TYPE_ARMENIAN;
496   else if (strcmp (style, "georgian") == 0)
497     return RUIN_LAYOUT_LIST_STYLE_TYPE_GEORGIAN;
498   else if (strcmp (style, "lower-alpha") == 0)
499     return RUIN_LAYOUT_LIST_STYLE_TYPE_LOWER_ALPHA;
500   else if (strcmp (style, "upper-alpha") == 0)
501     return RUIN_LAYOUT_LIST_STYLE_TYPE_UPPER_ALPHA;
502   else if (strcmp (style, "none") == 0)
503     return RUIN_LAYOUT_LIST_STYLE_TYPE_NONE;
504   else assert (1 == 0);
505 }
506 
generate_list_item(ruin_window_t * w,ruin_node_t * e,ruin_box_t * p,ruin_box_t * cb,int o,int l)507 static GList *generate_list_item
508 (ruin_window_t *w, ruin_node_t *e, ruin_box_t *p, ruin_box_t *cb, int o, int l)
509 {
510   char *position = ruin_css_lookup (w, e, "list-style-position");
511   enum ruin_layout_list_style_type s = parse_list_style_type
512     (ruin_css_lookup (w, e, "list-style-type"));
513   ruin_box_t *principal_box = ruin_box_new
514     (RUIN_LAYOUT_BOX_TYPE_BLOCK, p, cb, w, e);
515 
516   if (strcmp (position, "inside") == 0)
517     {
518       ruin_box_t *line_box = ruin_box_new
519 	(RUIN_LAYOUT_BOX_TYPE_LINE, principal_box, principal_box, w, e);
520       ruin_box_t *marker_box = (ruin_box_t *) ruin_marker_box_new
521 	(RUIN_LAYOUT_BOX_TYPE_MARKER, line_box, principal_box, w, e, s, o, l);
522 
523       line_box->children = g_list_append (NULL, marker_box);
524       line_box->children = g_list_concat
525 	(line_box->children, box_generate
526 	 (w, e->first_child, principal_box, principal_box));
527 
528       principal_box->children = g_list_append (NULL, line_box);
529 
530       return g_list_append (NULL, principal_box);
531     }
532   else if (strcmp (position, "outside") == 0)
533     {
534       ruin_box_t *marker_box = (ruin_box_t *)
535 	ruin_marker_box_new (RUIN_LAYOUT_BOX_TYPE_MARKER, p, cb, w, e, s, o, l);
536       principal_box->children = box_generate
537 	(w, e->first_child, principal_box, principal_box);
538 
539       return g_list_append (g_list_append (NULL, marker_box), principal_box);
540     }
541   assert (1 == 0);
542 }
543 
box_clone(ruin_box_t * b)544 static ruin_box_t *box_clone (ruin_box_t *b)
545 {
546   if (inline_box_type (b->type))
547     return (ruin_box_t *) ruin_inline_box_new
548       (b->type, b->parent, b->containing_block, b->window, b->generator,
549        ((ruin_inline_box_t *) b)->content_ptr);
550   return ruin_box_new
551     (b->type, b->parent, b->containing_block, b->window, b->generator);
552 }
553 
generate_non_inline(ruin_window_t * w,ruin_node_t ** e,ruin_box_t * p,ruin_box_t * cb)554 static GList *generate_non_inline
555 (ruin_window_t *w, ruin_node_t **e, ruin_box_t *p, ruin_box_t *cb)
556 {
557   char *display = ruin_css_lookup (w, *e, "display");
558   GList *ret = NULL;
559 
560   if (strcmp (display, "block") == 0)
561     ret = g_list_append (NULL, generate_block (w, *e, p, cb));
562   else if (strcmp (display, "list-item") == 0)
563     {
564       int len = 0, ord = 0;
565       ruin_node_t *e_ptr = *e;
566       do
567 	{
568 	  len++;
569 
570 	  *e = (*e)->next_sibling;
571 	  if (*e == NULL)
572 	    break;
573 
574 	  display = ruin_css_lookup (w, *e, "display");
575 	}
576       while (strcmp (display, "list-item") == 0);
577 
578       *e = e_ptr;
579       for (ord = 0; ord < len; ord++)
580 	{
581 	  ret = g_list_concat
582 	    (ret, generate_list_item (w, *e, p, cb, ord, len));
583 	  *e = (*e)->next_sibling;
584 	}
585 
586       return ret;
587     }
588   else if (strcmp (display, "table") == 0)
589     ret = g_list_append (NULL, generate_table (w, *e, p, cb));
590   else if (proper_table_child (display))
591     {
592       ruin_node_t *prototype_table = (ruin_node_t *)
593 	ruin_node_prototype_new (display_table);
594       do
595 	{
596 	  if (prototype_table->first_child == NULL)
597 	    prototype_table->first_child = *e;
598 	  prototype_table->children = g_list_append
599 	    (prototype_table->children, *e);
600 
601 	  *e = (*e)->next_sibling;
602 	  if (*e == NULL)
603 	    break;
604 
605 	  display = ruin_css_lookup (w, *e, "display");
606 	}
607       while (proper_table_child (display));
608       return g_list_append (NULL, generate_table (w, prototype_table, p, cb));
609     }
610   else if (strcmp (display, "none") == 0);
611   else assert (1 == 0);
612 
613   *e = (*e)->next_sibling;
614   return ret;
615 }
616 
generate_inline(ruin_window_t * w,ruin_node_t * e,ruin_box_t * p,ruin_box_t * cb)617 static GList *generate_inline
618 (ruin_window_t *w, ruin_node_t *e, ruin_box_t *p, ruin_box_t *cb)
619 {
620   enum ruin_layout_box_type box_type = get_box_type (w, e);
621   char *content = NULL;
622 
623   if (box_type != RUIN_LAYOUT_BOX_TYPE_INLINE &&
624       box_type != RUIN_LAYOUT_BOX_TYPE_ANONYMOUS_INLINE)
625     return generate_non_inline (w, &e, p, cb);
626   if (e->type == RUIN_PARSE_NODE_TYPE_TEXT)
627     content = ((ruin_node_text_t *) e)->content;
628 
629   if (content != NULL)
630     {
631       ruin_inline_box_t *b = ruin_inline_box_new
632 	(RUIN_LAYOUT_BOX_TYPE_INLINE, p, cb, w, e, content);
633       return g_list_append (NULL, b);
634     }
635   else
636     {
637       ruin_inline_box_t *b = ruin_inline_box_new
638 	(RUIN_LAYOUT_BOX_TYPE_INLINE, p, cb, w, e, NULL);
639       GList *ret = g_list_append (NULL, b);
640       GList *child_ptr = e->children;
641       int inline_context_latch = 0;
642 
643       while (child_ptr != NULL)
644 	{
645 	  GList *boxes = generate_inline
646 	    (w, (ruin_node_t *) child_ptr->data, (ruin_box_t *) b, cb);
647 	  GList *box_ptr = boxes;
648 
649 	  while (box_ptr != NULL)
650 	    {
651 	      ruin_box_t *box = box_ptr->data;
652 	      if (!inline_box_type (box->type))
653 		{
654 		  inline_context_latch = 1;
655 		  ret = g_list_append (ret, box);
656 		}
657 	      else
658 		{
659 		  if (inline_context_latch)
660 		    {
661 		      inline_context_latch = 0;
662 		      b = (ruin_inline_box_t *) box_clone ((ruin_box_t *) b);
663 		      ret = g_list_append (ret, b);
664 		    }
665 		  box->parent = (ruin_box_t *) b;
666 		  ((ruin_box_t *) b)->children =
667 		    g_list_append (((ruin_box_t *) b)->children, box);
668 		}
669 	      box_ptr = box_ptr->next;
670 	    }
671 
672 	  child_ptr = child_ptr->next;
673 	}
674 
675       return ret;
676     }
677 }
678 
generate_lines(ruin_window_t * w,ruin_node_t ** e,ruin_box_t * p,ruin_box_t * cb,int non_inline_siblings)679 static GList *generate_lines
680 (ruin_window_t *w, ruin_node_t **e, ruin_box_t *p,
681  ruin_box_t *cb, int non_inline_siblings)
682 {
683   GList *ret = NULL;
684   ruin_box_t *current_line = NULL;
685 
686   while (*e != NULL)
687     {
688       GList *children = NULL;
689       GList *child_ptr = NULL;
690       enum ruin_layout_box_type box_type = get_box_type (w, *e);
691 
692       if (box_type != RUIN_LAYOUT_BOX_TYPE_INLINE &&
693 	  box_type != RUIN_LAYOUT_BOX_TYPE_ANONYMOUS_INLINE)
694 	{
695 	  if (g_list_length (ret) == 1 && !non_inline_siblings)
696 	    {
697 	      ruin_box_t *b = ruin_box_new
698 		(RUIN_LAYOUT_BOX_TYPE_ANONYMOUS_BLOCK, p, cb, w, NULL);
699 	      ruin_box_t *l = ret->data;
700 
701 	      l->parent = b;
702 	      l->containing_block = b;
703 	      b->children = g_list_append (b->children, l);
704 
705 	      return g_list_append (NULL, b);
706 	    }
707 	  break;
708 	}
709 
710       children = generate_inline (w, *e, p, cb);
711       child_ptr = children;
712       while (child_ptr != NULL)
713 	{
714 	  ruin_box_t *child = (ruin_box_t *) child_ptr->data;
715 	  if (!inline_box_type (child->type))
716 	    {
717 	      current_line = NULL;
718 	      if (g_list_length (ret) == 1 && !non_inline_siblings)
719 		{
720 		  ruin_box_t *b = ruin_box_new
721 		    (RUIN_LAYOUT_BOX_TYPE_ANONYMOUS_BLOCK, p, cb, w, NULL);
722 		  ruin_box_t *l = ret->data;
723 
724 		  l->parent = b;
725 		  l->containing_block = b;
726 		  b->children = g_list_append (b->children, l);
727 
728 		  ret = g_list_append (NULL, b);
729 		  non_inline_siblings = TRUE;
730 		}
731 
732 	      ret = g_list_append (ret, child);
733 	    }
734 	  else
735 	    {
736 	      if (current_line == NULL)
737 		{
738 		  if (non_inline_siblings)
739 		    {
740 		      ruin_box_t *b = ruin_box_new
741 			(RUIN_LAYOUT_BOX_TYPE_ANONYMOUS_BLOCK, p, cb, w, NULL);
742 		      current_line = ruin_box_new
743 			(RUIN_LAYOUT_BOX_TYPE_LINE, b, b, w, *e);
744 		      b->children = g_list_append (b->children, current_line);
745 		      ret = g_list_append (ret, b);
746 		    }
747 		  else
748 		    {
749 		      current_line = ruin_box_new
750 			(RUIN_LAYOUT_BOX_TYPE_LINE, p, cb, w, *e);
751 		      ret = g_list_append (ret, current_line);
752 		    }
753 		}
754 	      child->parent = current_line;
755 	      current_line->children = g_list_append
756 		(current_line->children, child);
757 	    }
758 	  child_ptr = child_ptr->next;
759 	}
760 
761       *e = (*e)->next_sibling;
762     }
763 
764   return ret;
765 }
766 
box_generate(ruin_window_t * w,ruin_node_t * e,ruin_box_t * p,ruin_box_t * cb)767 static GList *box_generate
768 (ruin_window_t *w, ruin_node_t *e, ruin_box_t *p, ruin_box_t *cb)
769 {
770   GList *ret = NULL;
771 
772   while (e != NULL)
773     {
774       enum ruin_layout_box_type box_type = get_box_type (w, e);
775       if (box_type == RUIN_LAYOUT_BOX_TYPE_INLINE ||
776 	  box_type == RUIN_LAYOUT_BOX_TYPE_ANONYMOUS_INLINE)
777 	{
778 	  ret = g_list_concat
779 	    (ret, generate_lines (w, &e, p, cb, g_list_length (ret) > 0));
780 	  continue;
781 	}
782       else ret = g_list_concat (ret, generate_non_inline (w, &e, p, cb));
783     }
784 
785   return ret;
786 }
787 
ruin_box_generate(ruin_window_t * w,ruin_node_t * e)788 GList *ruin_box_generate (ruin_window_t *w, ruin_node_t *e)
789 {
790   ruin_box_t *viewport_box = ruin_box_new
791     (RUIN_LAYOUT_BOX_TYPE_ANONYMOUS_BLOCK, NULL, NULL, w, NULL);
792   assert (e->next_sibling == NULL);
793   return box_generate (w, e, NULL, viewport_box);
794 }
795 
ruin_box_css_lookup(ruin_box_t * box,char * prop)796 char *ruin_box_css_lookup (ruin_box_t *box, char *prop)
797 {
798   if (box->type == RUIN_LAYOUT_BOX_TYPE_ANONYMOUS_BLOCK
799       || box->type == RUIN_LAYOUT_BOX_TYPE_ANONYMOUS_INLINE
800       || box->type == RUIN_LAYOUT_BOX_TYPE_LINE)
801     {
802       if (box->parent == NULL)
803 	return ruin_css_initial_value (box->window, prop);
804       else if (ruin_css_is_inherited (box->window, prop))
805 	return ruin_box_css_lookup (box->parent, prop);
806       else return ruin_css_initial_value (box->window, prop);
807     }
808   else if (box->type == RUIN_LAYOUT_BOX_TYPE_BLOCK
809 	   || box->type == RUIN_LAYOUT_BOX_TYPE_INLINE
810 	   || box->type == RUIN_LAYOUT_BOX_TYPE_MARKER)
811     {
812       assert (box->generator != NULL);
813       return ruin_css_lookup (box->window, box->generator, prop);
814     }
815   else assert (1 == 0);
816   return NULL;
817 }
818 
ruin_box_normalize_width(ruin_length_t * l,ruin_box_t * containing_block,int round_to_zero)819 void ruin_box_normalize_width
820 (ruin_length_t *l, ruin_box_t *containing_block, int round_to_zero)
821 {
822   ruin_css_normalize_length
823     (l, &containing_block->width, containing_block->window->font_width,
824      containing_block->window->dpi, round_to_zero);
825 }
826 
ruin_box_normalize_height(ruin_length_t * l,ruin_box_t * containing_block,int round_to_zero)827 void ruin_box_normalize_height
828 (ruin_length_t *l, ruin_box_t *containing_block, int round_to_zero)
829 {
830   ruin_css_normalize_length
831     (l, &containing_block->height, containing_block->window->font_height,
832      containing_block->window->dpi, round_to_zero);
833 }
834 
_ruin_box_init()835 void _ruin_box_init ()
836 {
837   display_table = g_hash_table_new (g_str_hash, g_str_equal);
838   display_table_cell = g_hash_table_new (g_str_hash, g_str_equal);
839   display_table_column = g_hash_table_new (g_str_hash, g_str_equal);
840   display_table_row = g_hash_table_new (g_str_hash, g_str_equal);
841 
842   g_hash_table_insert (display_table, "display", "table");
843   g_hash_table_insert (display_table_cell, "display", "table-cell");
844   g_hash_table_insert (display_table_column, "display", "table-column");
845   g_hash_table_insert (display_table_row, "display", "table-row");
846 }
847