1 /* Fo
2  * fo-area-layout.c: Layout area object
3  *
4  * Copyright (C) 2001 Sun Microsystems
5  * Copyright (C) 2007-2010 Menteith Consulting Ltd
6  *
7  * See COPYING for the status of this software.
8  */
9 
10 #include "fo-utils.h"
11 #include "fo/fo-text.h"
12 #include "fo/fo-block.h"
13 #include "fo-layout-private.h"
14 #include "fo-area.h"
15 #include "fo-area-area.h"
16 #include "fo-area-layout.h"
17 #include "fo-area-layout-private.h"
18 #include "fo-area-page.h"
19 #include "property/fo-property-text-align.h"
20 #include "property/fo-property-orphans.h"
21 #include "property/fo-property-widows.h"
22 
23 enum {
24   PROP_0,
25   PROP_LAYOUT
26 };
27 
28 
29 static void _init         (FoAreaLayout      *fo_area_layout);
30 static void _class_init   (FoAreaLayoutClass *klass);
31 static void fo_area_layout_get_property (GObject           *object,
32 					 guint              prop_id,
33 					 GValue            *value,
34 					 GParamSpec        *pspec);
35 static void fo_area_layout_set_property (GObject           *object,
36 					 guint              prop_id,
37 					 const GValue      *value,
38 					 GParamSpec        *pspec);
39 static void _dispose     (GObject           *object);
40 
41 static void _debug_dump_properties  (FoArea *area,
42 				     gint   depth);
43 static void fo_area_layout_update_after_clone     (FoArea *clone,
44 						   FoArea *original);
45 static FoArea* fo_area_layout_split_before_height (FoArea *area,
46 						   gdouble  max_height);
47 static gboolean fo_area_layout_split_before_height_check (FoArea *area,
48 							  gdouble max_height);
49 static void _resolve_text_align (FoArea *area);
50 
51 static gpointer parent_class;
52 
53 /**
54  * fo_area_layout_get_type:
55  * @void:
56  *
57  * Register the #FoAreaLayout object type.
58  *
59  * Return value: #GType value of the #FoAreaLayout object type.
60  **/
61 GType
fo_area_layout_get_type(void)62 fo_area_layout_get_type (void)
63 {
64   static GType object_type = 0;
65 
66   if (!object_type)
67     {
68       static const GTypeInfo object_info =
69 	{
70 	  sizeof (FoAreaLayoutClass),
71 	  (GBaseInitFunc) NULL,
72 	  (GBaseFinalizeFunc) NULL,
73 	  (GClassInitFunc) _class_init,
74 	  NULL,           /* class_finalize */
75 	  NULL,           /* class_data */
76 	  sizeof (FoAreaLayout),
77 	  0,              /* n_preallocs */
78 	  (GInstanceInitFunc) _init,
79 	  NULL
80 	};
81 
82       object_type = g_type_register_static (FO_TYPE_AREA_AREA,
83                                             "FoAreaLayout",
84                                             &object_info, 0);
85     }
86 
87   return object_type;
88 }
89 
90 /**
91  * _init:
92  * @fo_area_layout: #FoAreaLayout object to initialise.
93  *
94  * Implements #GInstanceInitFunc for #FoAreaLayout.
95  **/
96 static void
_init(FoAreaLayout * fo_area_layout)97 _init (FoAreaLayout *fo_area_layout)
98 {
99   FO_AREA (fo_area_layout)->class = FO_AREA_FLAG_CLASS_NORMAL;
100 }
101 
102 /**
103  * _class_init:
104  * @klass: FoAreaLayoutClass object to initialise.
105  *
106  * Implements GClassInitFunc for FoAreaLayoutClass.
107  **/
108 static void
_class_init(FoAreaLayoutClass * klass)109 _class_init (FoAreaLayoutClass *klass)
110 {
111   GObjectClass *object_class = G_OBJECT_CLASS (klass);
112   FoAreaClass *fo_area_class = FO_AREA_CLASS (klass);
113 
114   parent_class = g_type_class_peek_parent (klass);
115 
116   object_class->dispose = _dispose;
117 
118   object_class->set_property = fo_area_layout_set_property;
119   object_class->get_property = fo_area_layout_get_property;
120 
121   fo_area_class->debug_dump_properties = _debug_dump_properties;
122   fo_area_class->update_after_clone = fo_area_layout_update_after_clone;
123   fo_area_class->split_before_height =
124     fo_area_layout_split_before_height;
125   fo_area_class->split_before_height_check =
126     fo_area_layout_split_before_height_check;
127   fo_area_class->resolve_text_align = _resolve_text_align;
128 
129   g_object_class_install_property
130     (object_class,
131      PROP_LAYOUT,
132      g_param_spec_pointer ("layout",
133 			   _("FoLayout layout"),
134 			   _("FoLayout layout of formatted text of block"),
135 			   G_PARAM_READWRITE));
136 }
137 
138 /**
139  * _dispose:
140  * @object: FoAreaLayout object to dispose
141  *
142  * Implements GObjectDisposeFunc for FoAreaLayout
143  **/
144 static void
_dispose(GObject * object)145 _dispose (GObject *object)
146 {
147   FoAreaLayout *fo_area_layout = FO_AREA_LAYOUT (object);
148 
149   g_object_unref (fo_area_layout->layout);
150   g_free (fo_area_layout->text);
151   g_slist_free (fo_area_layout->line_heights);
152 
153   G_OBJECT_CLASS (parent_class)->dispose (object);
154 }
155 
156 /**
157  * fo_area_layout_get_property:
158  * @object:  GObject whose property will be retrieved
159  * @prop_id: Property ID assigned when property registered
160  * @value:   GValue to set with property value
161  * @pspec:   Parameter specification for this property type
162  *
163  * Implements #GObjectGetPropertyFunc for FoAreaLayout
164  **/
165 void
fo_area_layout_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)166 fo_area_layout_get_property (GObject         *object,
167 			     guint            prop_id,
168 			     GValue          *value,
169 			     GParamSpec      *pspec)
170 {
171   FoArea *fo_area;
172 
173   fo_area = FO_AREA (object);
174 
175   switch (prop_id)
176     {
177     case PROP_LAYOUT:
178       g_value_set_pointer (value, (gpointer) fo_area_layout_get_layout (fo_area));
179     default:
180       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
181       break;
182     }
183 }
184 
185 /**
186  * fo_area_layout_set_property:
187  * @object:  GObject whose property will be set
188  * @prop_id: Property ID assigned when property registered
189  * @value:   New value for property
190  * @pspec:   Parameter specification for this property type
191  *
192  * Implements #GObjectSetPropertyFunc for FoAreaLayout
193  **/
194 void
fo_area_layout_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)195 fo_area_layout_set_property (GObject         *object,
196 			     guint            prop_id,
197 			     const GValue    *value,
198 			     GParamSpec      *pspec)
199 {
200   FoArea *fo_area;
201 
202   fo_area = FO_AREA (object);
203 
204   switch (prop_id)
205     {
206     case PROP_LAYOUT:
207       fo_area_layout_set_layout (fo_area,
208 				 (FoLayout *) g_value_get_pointer (value));
209     default:
210       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
211       break;
212     }
213 }
214 
215 /**
216  * fo_area_layout_new:
217  *
218  * Creates a new #FoAreaLayout initialized to default value.
219  *
220  * Return value: the new #FoAreaLayout
221  **/
222 FoArea *
fo_area_layout_new(void)223 fo_area_layout_new (void)
224 {
225   return FO_AREA (g_object_new (fo_area_layout_get_type (),
226 				NULL));
227 }
228 
229 /**
230  * fo_area_layout_new_with_layout:
231  * @layout: #FoLayout used by the new #FoAreaLayout
232  *
233  * Creates a new #FoAreaLayout initialized with @layout.
234  *
235  * Return value: the new #FoAreaLayout
236  **/
237 FoArea *
fo_area_layout_new_with_layout(FoLayout * layout)238 fo_area_layout_new_with_layout (FoLayout *layout)
239 {
240   FoArea *fo_area_layout = fo_area_layout_new ();
241 
242   fo_area_layout_set_layout (fo_area_layout,
243 			     layout);
244 
245   return fo_area_layout;
246 }
247 
248 /**
249  * fo_area_layout_get_layout:
250  * @fo_area_layout: The #FoAreaLayout object
251  *
252  * Gets the #layout property of @area_layout
253  *
254  * Return value: The "layout" property value
255 **/
256 FoLayout *
fo_area_layout_get_layout(FoArea * fo_area_layout)257 fo_area_layout_get_layout (FoArea *fo_area_layout)
258 {
259   g_return_val_if_fail (fo_area_layout != NULL, 0);
260   g_return_val_if_fail (FO_IS_AREA_LAYOUT (fo_area_layout), 0);
261 
262   return FO_AREA_LAYOUT (fo_area_layout)->layout;
263 }
264 
265 /**
266  * fo_area_layout_set_layout:
267  * @fo_area_layout: The #FoAreaLayout object
268  * @new_layout: The new "layout" property value
269  *
270  * Sets the #layout property of @area_area to @new_layout
271 **/
272 void
fo_area_layout_set_layout(FoArea * fo_area_layout,FoLayout * new_layout)273 fo_area_layout_set_layout (FoArea   *fo_area_layout,
274 			   FoLayout *new_layout)
275 {
276   g_return_if_fail (fo_area_layout != NULL);
277   g_return_if_fail (FO_IS_AREA_LAYOUT (fo_area_layout));
278 
279   if (FO_AREA_LAYOUT (fo_area_layout)->layout != NULL)
280     {
281       g_object_unref (FO_AREA_LAYOUT (fo_area_layout)->layout);
282     }
283 
284   if (FO_AREA_LAYOUT (fo_area_layout)->line_heights != NULL)
285     {
286       g_slist_free (FO_AREA_LAYOUT (fo_area_layout)->line_heights);
287     }
288 
289   FO_AREA_LAYOUT (fo_area_layout)->layout = g_object_ref_sink (new_layout);
290 
291   gint cumulative_height = 0;
292   GSList *line_heights = NULL;
293   FoRectangle logical;
294   gint line_count = fo_layout_get_line_count (new_layout);
295   fo_area_layout_set_line_first (fo_area_layout,
296 				 0);
297   fo_area_layout_set_line_last (fo_area_layout,
298 				line_count - 1);
299   gint line_index;
300   for (line_index = 0; line_index < line_count; line_index++)
301     {
302       pango_layout_line_get_extents (pango_layout_get_line_readonly (fo_layout_get_pango_layout (new_layout),
303 								     line_index),
304 				     NULL,
305 				     (PangoRectangle *) &logical);
306 
307       cumulative_height += logical.height;
308       line_heights =
309 	g_slist_prepend (line_heights, GINT_TO_POINTER (cumulative_height));
310     }
311 
312   FO_AREA_LAYOUT (fo_area_layout)->line_heights =
313     g_slist_reverse (line_heights);
314 
315   /*g_object_notify (G_OBJECT (fo_area_layout), "layout");*/
316 }
317 
318 /**
319  * fo_area_layout_get_line_first:
320  * @fo_area_layout: The #FoAreaLayout object
321  *
322  * Gets the #line-first property of @area_layout
323  *
324  * Return value: The "line_first" property value
325 **/
326 guint
fo_area_layout_get_line_first(FoArea * fo_area_layout)327 fo_area_layout_get_line_first (FoArea *fo_area_layout)
328 {
329   g_return_val_if_fail (fo_area_layout != NULL, 0);
330   g_return_val_if_fail (FO_IS_AREA_LAYOUT (fo_area_layout), 0);
331 
332   return FO_AREA_LAYOUT (fo_area_layout)->line_first;
333 }
334 
335 /**
336  * fo_area_layout_set_line_first:
337  * @fo_area_layout: The #FoAreaLayout object
338  * @new_line_first: The new "line_first" property value
339  *
340  * Sets the #line-first property of @area_area to @new_line_first
341 **/
342 void
fo_area_layout_set_line_first(FoArea * fo_area_layout,guint new_line_first)343 fo_area_layout_set_line_first (FoArea *fo_area_layout,
344 			       guint   new_line_first)
345 {
346   g_return_if_fail (fo_area_layout != NULL);
347   g_return_if_fail (FO_IS_AREA_LAYOUT (fo_area_layout));
348 
349   FO_AREA_LAYOUT (fo_area_layout)->line_first = new_line_first;
350   /* g_object_notify (G_OBJECT (fo_area_layout), "line_first"); */
351 }
352 
353 /**
354  * fo_area_layout_get_line_last:
355  * @fo_area_layout: The #FoAreaLayout object.
356  *
357  * Gets the #line-last property of @fo_area_layout.
358  *
359  * Return value: The "line_last" property value.
360 **/
361 guint
fo_area_layout_get_line_last(FoArea * fo_area_layout)362 fo_area_layout_get_line_last (FoArea *fo_area_layout)
363 {
364   g_return_val_if_fail (fo_area_layout != NULL, 0);
365   g_return_val_if_fail (FO_IS_AREA_LAYOUT (fo_area_layout), 0);
366 
367   return FO_AREA_LAYOUT (fo_area_layout)->line_last;
368 }
369 
370 /**
371  * fo_area_layout_set_line_last:
372  * @fo_area_layout: The #FoAreaLayout object.
373  * @new_line_last:  The new "line_last" property value.
374  *
375  * Sets the #line-last property of @fo_area_layout to @new_line_last.
376 **/
377 void
fo_area_layout_set_line_last(FoArea * fo_area_layout,guint new_line_last)378 fo_area_layout_set_line_last (FoArea *fo_area_layout,
379 			      guint   new_line_last)
380 {
381   g_return_if_fail (fo_area_layout != NULL);
382   g_return_if_fail (FO_IS_AREA_LAYOUT (fo_area_layout));
383 
384   FO_AREA_LAYOUT (fo_area_layout)->line_last = new_line_last;
385   /* g_object_notify (G_OBJECT (fo_area_layout), "line-last");*/
386 }
387 
388 static void
_debug_dump_line_height(gpointer value,gpointer data)389 _debug_dump_line_height (gpointer value,
390 			 gpointer data)
391 {
392   gchar *indent = g_strnfill (GPOINTER_TO_INT (data) * 2, ' ');
393 
394   g_log (G_LOG_DOMAIN,
395 	 G_LOG_LEVEL_DEBUG,
396 	 "%s%1.1f",
397 	 indent, (float) GPOINTER_TO_INT (value) / PANGO_SCALE);
398 
399   g_free (indent);
400 }
401 
402 /**
403  * _debug_dump_properties:
404  * @area:  The #FoArea object
405  * @depth: Indent level to add to the output
406  *
407  * Logs the value of each significant property of @area then calls
408  * debug_dump_properties method of parent class.
409  **/
410 static void
_debug_dump_properties(FoArea * area,gint depth)411 _debug_dump_properties (FoArea *area,
412 			gint depth)
413 {
414   g_return_if_fail (area != NULL);
415   g_return_if_fail (FO_IS_AREA_LAYOUT (area));
416 
417   FoAreaLayout *layout = FO_AREA_LAYOUT (area);
418   gchar *indent = g_strnfill (depth * 2, ' ');
419 
420   g_log (G_LOG_DOMAIN,
421 	 G_LOG_LEVEL_DEBUG,
422 	 "%sline heights: %p",
423 	 indent, layout->line_heights);
424   g_slist_foreach (layout->line_heights,
425 		   _debug_dump_line_height,
426 		   GINT_TO_POINTER (depth + 1));
427   g_log (G_LOG_DOMAIN,
428 	 G_LOG_LEVEL_DEBUG,
429 	 "%sline-first:   %d",
430 	 indent,
431 	 layout->line_first);
432   g_log (G_LOG_DOMAIN,
433 	 G_LOG_LEVEL_DEBUG,
434 	 "%sline-last:    %d",
435 	 indent,
436 	 layout->line_last);
437 
438   gchar *fo_layout_string = fo_object_debug_sprintf (layout->layout);
439   g_log (G_LOG_DOMAIN,
440 	 G_LOG_LEVEL_DEBUG,
441 	 "%s%s",
442 	 indent,
443 	 fo_layout_string);
444   g_free (fo_layout_string);
445 
446   g_free (indent);
447 
448   FO_AREA_CLASS (parent_class)->debug_dump_properties (area, depth + 1);
449 }
450 
451 /**
452  * fo_area_layout_update_after_clone:
453  * @clone:    New object cloned from @original
454  * @original: Original area object
455  *
456  * Update the FoAreaArea-specific instance variables of @clone to
457  * match those of @original
458  **/
459 void
fo_area_layout_update_after_clone(FoArea * clone,FoArea * original)460 fo_area_layout_update_after_clone (FoArea *clone,
461 				   FoArea *original)
462 {
463   g_return_if_fail (clone != NULL);
464   g_return_if_fail (FO_IS_AREA_LAYOUT (clone));
465   g_return_if_fail (original != NULL);
466   g_return_if_fail (FO_IS_AREA_LAYOUT (original));
467 
468   FO_AREA_CLASS (parent_class)->update_after_clone (clone, original);
469 
470   FoAreaLayout *original_layout = FO_AREA_LAYOUT (original);
471   FoAreaLayout *clone_layout = FO_AREA_LAYOUT (clone);
472 
473   clone_layout->layout = g_object_ref (original_layout->layout);
474   clone_layout->text = original_layout->text;
475   clone_layout->line_first = original_layout->line_first;
476   clone_layout->line_last = original_layout->line_last;
477   clone_layout->prev_portion = original_layout->prev_portion;
478   clone_layout->next_portion = original_layout->next_portion;
479   clone_layout->line_heights = g_slist_copy (original_layout->line_heights);
480 }
481 
482 /**
483  * fo_area_layout_split_before_height:
484  * @area:   #FoArea to be split
485  * @height: Maximum block-progression-dimension of @area
486  *
487  * Split @area at or before @height.
488  *
489  * Return value: The part of @area spit from @area, or NULL if unsplit.
490  **/
491 FoArea *
fo_area_layout_split_before_height(FoArea * area,gdouble max_height)492 fo_area_layout_split_before_height (FoArea *area,
493 				    gdouble max_height)
494 {
495   FoAreaLayout *layout;
496   FoArea *new_area;
497   GSList *line_heights;
498   gint line_count;
499   gint line_first, line_last;
500   gint widows, orphans;
501   gint pre_widow_max;
502   gdouble line_first_pre_height = 0;
503 
504   g_return_val_if_fail (FO_IS_AREA_LAYOUT (area), area);
505 
506 #if defined(LIBFO_DEBUG) && 0
507   g_message ("layout_split_before_height:: %p", area);
508 #endif
509   layout = (FoAreaLayout *) area;
510 
511   line_heights = layout->line_heights;
512   line_first = layout->line_first;
513   line_last = layout->line_last;
514   line_count = line_last - line_first + 1;
515   if (line_first > 0)
516     line_first_pre_height =
517       (float) GPOINTER_TO_INT (g_slist_nth_data (line_heights,
518 						 line_first - 1)) /
519       PANGO_SCALE;
520 
521   widows =
522     fo_integer_get_value (fo_property_get_value (fo_block_get_widows (area->generated_by)));
523   orphans =
524     fo_integer_get_value (fo_property_get_value (fo_block_get_orphans (area->generated_by)));
525 
526   pre_widow_max = line_last - widows;
527 
528 #if defined(LIBFO_DEBUG) && 0
529   g_message ("layout_split_before_height:: layout: %p; line_first: %d; line_first_pre_height: %1.1f; line_count: %d; widows: %d; orphans: %d",
530 	     area,
531 	     line_first,
532 	     line_first_pre_height,
533 	     line_count,
534 	     widows,
535 	     orphans);
536 #endif
537 
538   if (line_count < widows + orphans)
539     {
540 #if defined(LIBFO_DEBUG) && 0
541       g_message ("layout_split_before_height:: Not enough lines (%p): %d < %d + %d + %d",
542 		 area,
543 		 line_count,
544 		 widows,
545 		 orphans,
546 		 line_first);
547 #endif
548       return NULL;
549     }
550   else if (max_height <
551 	   ((float) GPOINTER_TO_INT (g_slist_nth_data (line_heights,
552 						       orphans + line_first)) /
553 	    PANGO_SCALE) - line_first_pre_height)
554     {
555 #if defined(LIBFO_DEBUG) && 0
556       g_message ("Post-orphans exceeds available height (%p): %1.1f - %1.1f > %1.1f",
557 		 area,
558 		 (float) GPOINTER_TO_INT (g_slist_nth_data (line_heights, orphans + line_first))  / PANGO_SCALE,
559 		 line_first_pre_height,
560 		 max_height);
561 #endif
562       return NULL;
563     }
564   else
565     {
566       gint line_index = 0;
567 
568       while (line_heights != NULL)
569 	{
570 	  if (line_index < line_first)
571 	    {
572 #if defined(LIBFO_DEBUG) && 0
573 	      g_message ("layout_split_before_height:: ignore:: line_index: %d; line_first: %d",
574 			 line_index,
575 			 line_first);
576 #endif
577 	    }
578 	  else if (line_index < orphans + line_first)
579 	    {
580 #if defined(LIBFO_DEBUG) && 0
581 	      g_message ("layout_split_before_height:: Can't break - orphans:: line_index: %d; line_first: %d; orphans: %d",
582 			 line_index,
583 			 line_first,
584 			 orphans);
585 #endif
586 	    }
587 	  else if (line_index >= pre_widow_max)
588 	    {
589 #if defined(LIBFO_DEBUG) && 0
590 	      g_message ("layout_split_before_height:: Can't break - widows:: line_index: %d; orphans: %d",
591 			 line_index,
592 			 pre_widow_max);
593 #endif
594 	      return NULL;
595 	    }
596 	  else
597 	    {
598 	      if (max_height >
599 		  ((float) GPOINTER_TO_INT (line_heights->data) / PANGO_SCALE) - line_first_pre_height)
600 		{
601 #if defined(LIBFO_DEBUG) && 0
602 		  g_message ("layout_split_before_height:: fits:: line_index: %d; line height: %f; line height - line_first_pre_height: %f; max height: %f",
603 			     line_index,
604 			     (float) GPOINTER_TO_INT (line_heights->data) / PANGO_SCALE,
605 			     (float) GPOINTER_TO_INT (line_heights->data) / PANGO_SCALE - line_first_pre_height,
606 			     max_height);
607 #endif
608 		  if ((max_height <
609 		       (float) GPOINTER_TO_INT (line_heights->next->data) /
610 		       PANGO_SCALE - line_first_pre_height) ||
611 		      line_index == pre_widow_max - 1)
612 		    {
613 #if defined(LIBFO_DEBUG) && 0
614 		      g_message ("layout_split_before_height:: Break:: line: %d",
615 				 line_index);
616 #endif
617 
618 		      new_area = g_object_ref_sink (fo_area_clone (area));
619 
620 		      fo_area_set_is_first (new_area, FALSE);
621 		      fo_area_set_is_last (area, FALSE);
622 		      /* FIXME: handle padding */
623 		      /* FIXME: handle conditionality of borders and padding */
624 		      fo_area_area_set_border_after (area,
625 						     0.0);
626 		      fo_area_area_set_border_before (new_area,
627 						      0.0);
628 		      gboolean retain_padding_before =
629 			fo_length_cond_get_condity (fo_property_get_value (fo_block_get_padding_before (area->generated_by)));
630 
631 		      if (!retain_padding_before)
632 			{
633 			  fo_area_area_set_padding_before (new_area,
634 							   0.0);
635 			}
636 		      fo_area_layout_set_line_first (new_area,
637 						     line_index + 1);
638 		      fo_area_layout_set_line_last (area,
639 						    line_index);
640 		      fo_area_area_set_height (new_area,
641 					       ((float) GPOINTER_TO_INT (g_slist_last (line_heights)->data)) / PANGO_SCALE -
642 					       (float) GPOINTER_TO_INT (line_heights->data) / PANGO_SCALE + fo_area_area_get_padding_before (new_area));
643 		      fo_area_area_set_height (area,
644 					       (float) GPOINTER_TO_INT (line_heights->data) / PANGO_SCALE - line_first_pre_height + fo_area_area_get_padding_before (area));
645 		      fo_area_resolve_text_align (area);
646 #if defined(LIBFO_DEBUG) && 0
647 		      g_message ("layout_split_before_height:: area height: %f; new_area height: %f",
648 				 fo_area_area_get_height (area),
649 				 fo_area_area_get_height (new_area));
650 #endif
651 		      /*
652 		      fo_area_size_request (area);
653 		      */
654 		      return new_area;
655 		    }
656 		}
657 	      else
658 		{
659 #if defined(LIBFO_DEBUG) && 0
660 		  g_message ("Line %d doesn't fit: %1.1f > %1.1f",
661 			     line_index,
662 			     (float) GPOINTER_TO_INT (line_heights->data) / PANGO_SCALE,
663 			     max_height);
664 #endif
665 		}
666 	    }
667 	  line_index++;
668 	  line_heights = line_heights->next;
669 	}
670     }
671   return NULL;
672 }
673 
674 /**
675  * fo_area_layout_split_before_height_check:
676  * @area:   #FoArea to be split
677  * @height: Maximum block-progression-dimension of @area
678  *
679  * Check whether @area can split at or before @height.
680  *
681  * Return value: TRUE if can split, otherwise FALSE.
682  **/
683 gboolean
fo_area_layout_split_before_height_check(FoArea * area,gdouble max_height)684 fo_area_layout_split_before_height_check (FoArea *area,
685 					  gdouble max_height)
686 {
687   FoAreaLayout *layout;
688   GSList *line_heights;
689   gint line_count;
690   gint line_first, line_last;
691   gint widows, orphans;
692   gint pre_widow_max;
693   gdouble line_first_pre_height = 0;
694 
695   g_return_val_if_fail (FO_IS_AREA_LAYOUT (area), FALSE);
696 
697 #if defined(LIBFO_DEBUG) && 0
698   g_message ("layout_split_before_height_check:: %p", area);
699 #endif
700   layout = (FoAreaLayout *) area;
701 
702   line_heights = layout->line_heights;
703   line_first = layout->line_first;
704   line_last = layout->line_last;
705   line_count = line_last - line_first + 1;
706   if (line_first > 0)
707     line_first_pre_height =
708       (float) GPOINTER_TO_INT (g_slist_nth_data (line_heights,
709 						 line_first - 1)) /
710       PANGO_SCALE;
711 
712   widows =
713     fo_integer_get_value (fo_property_get_value (FO_PROPERTY (fo_block_get_widows (area->generated_by))));
714   orphans =
715     fo_integer_get_value (fo_property_get_value (FO_PROPERTY (fo_block_get_orphans (area->generated_by))));
716 
717   pre_widow_max = line_last - widows;
718 
719 #if defined(LIBFO_DEBUG) && 0
720   g_message ("layout_split_before_height_check:: layout: %p; line_first: %d; line_first_pre_height: %1.1f; line_count: %d; widows: %d; orphans: %d",
721 	     area,
722 	     line_first,
723 	     line_first_pre_height,
724 	     line_count,
725 	     widows,
726 	     orphans);
727 #endif
728   if (line_count < widows + orphans)
729     {
730 #if defined(LIBFO_DEBUG) && 0
731       g_message ("layout_split_before_height_check:: Not enough lines (%p): %d < %d + %d + %d",
732 		 area,
733 		 line_count,
734 		 widows,
735 		 orphans,
736 		 line_first);
737 #endif
738       return FALSE;
739     }
740   else if (max_height <
741 	   ((float) GPOINTER_TO_INT (g_slist_nth_data (line_heights,
742 						       orphans + line_first)) /
743 	    PANGO_SCALE) - line_first_pre_height)
744     {
745 #if defined(LIBFO_DEBUG) && 0
746       g_message ("Post-orphans exceeds available height (%p): %1.1f - %1.1f > %1.1f",
747 		 area,
748 		 (float) GPOINTER_TO_INT (g_slist_nth_data (line_heights, orphans + line_first))  / PANGO_SCALE,
749 		 line_first_pre_height,
750 		 max_height);
751 #endif
752       return FALSE;
753     }
754   else
755     {
756       gint line_index = 0;
757 
758       while (line_heights != NULL)
759 	{
760 	  if (line_index < line_first)
761 	    {
762 #if defined(LIBFO_DEBUG) && 0
763 	      g_message ("layout_split_before_height_check:: ignore:: line_index: %d; line_first: %d",
764 			 line_index,
765 			 line_first);
766 #endif
767 	    }
768 	  else if (line_index < orphans + line_first)
769 	    {
770 #if defined(LIBFO_DEBUG) && 0
771 	      g_message ("layout_split_before_height_check:: Can't break - orphans:: line_index: %d; line_first: %d; orphans: %d",
772 			 line_index,
773 			 line_first,
774 			 orphans);
775 #endif
776 	    }
777 	  else if (line_index >= pre_widow_max)
778 	    {
779 #if defined(LIBFO_DEBUG) && 0
780 	      g_message ("layout_split_before_height_check:: Can't break - widows:: line_index: %d; orphans: %d",
781 			 line_index,
782 			 pre_widow_max);
783 #endif
784 	      return FALSE;
785 	    }
786 	  else
787 	    {
788 	      if (max_height >
789 		  ((float) GPOINTER_TO_INT (line_heights->data) / PANGO_SCALE) - line_first_pre_height)
790 		{
791 #if defined(LIBFO_DEBUG) && 0
792 		  g_message ("layout_split_before_height_check:: fits:: line_index: %d; line height: %f; line height - line_first_pre_height: %f; max height: %f",
793 			     line_index,
794 			     (float) GPOINTER_TO_INT (line_heights->data) / PANGO_SCALE,
795 			     (float) GPOINTER_TO_INT (line_heights->data) / PANGO_SCALE - line_first_pre_height,
796 			     max_height);
797 #endif
798 		  if ((max_height <
799 		       (float) GPOINTER_TO_INT (line_heights->next->data) /
800 		       PANGO_SCALE - line_first_pre_height) ||
801 		      line_index == pre_widow_max - 1)
802 		    {
803 #if defined(LIBFO_DEBUG) && 0
804 		      g_message ("layout_split_before_height_check:: Break:: line: %d",
805 				 line_index);
806 #endif
807 
808 		      return TRUE;
809 		    }
810 		}
811 	      else
812 		{
813 #if defined(LIBFO_DEBUG) && 0
814 		  g_message ("Line %d doesn't fit: %1.1f > %1.1f",
815 			     line_index,
816 			     (float) GPOINTER_TO_INT (line_heights->data) / PANGO_SCALE,
817 			     max_height);
818 #endif
819 		}
820 	    }
821 	  line_index++;
822 	  line_heights = line_heights->next;
823 	}
824     }
825   return FALSE;
826 }
827 
828 /**
829  * _resolve_text_align:
830  * @area:   #FoArea.
831  *
832  * Determine the Pango alignment from the XSL 'text-align' property.
833  **/
834 static void
_resolve_text_align(FoArea * area)835 _resolve_text_align (FoArea *area)
836 {
837   FoAreaLayout *area_layout;
838   FoEnumEnum area_align;
839   PangoAlignment pango_align;
840   PangoDirection base_dir;
841   gint page_number_mod_2;
842 
843   g_return_if_fail (area != NULL);
844   g_return_if_fail (FO_IS_AREA_LAYOUT (area));
845 
846   area_layout = FO_AREA_LAYOUT (area);
847 
848   area_align = fo_enum_get_value (fo_property_get_value (fo_block_get_text_align (area->generated_by)));
849 
850   page_number_mod_2 =
851     fo_area_page_get_page_number (fo_area_get_page (area)) % 2;
852   base_dir =
853     pango_context_get_base_dir (pango_layout_get_context (fo_layout_get_pango_layout (area_layout->layout)));
854   /* FIXME this will get more complicated when necessary to support
855      orientation and tb writing direction */
856   if (area_align == FO_ENUM_ENUM_CENTER)
857     {
858       pango_align = PANGO_ALIGN_CENTER;
859     }
860   else if ((area_align == FO_ENUM_ENUM_RIGHT) ||
861       ((page_number_mod_2 == 0) &&
862        (area_align == FO_ENUM_ENUM_INSIDE)) ||
863       ((page_number_mod_2 == 1) &&
864        (area_align == FO_ENUM_ENUM_OUTSIDE)) ||
865       ((base_dir == PANGO_DIRECTION_LTR) &&
866        (area_align == FO_ENUM_ENUM_END)) ||
867       ((base_dir == PANGO_DIRECTION_RTL) &&
868        ((area_align == FO_ENUM_ENUM_START))))
869     {
870       pango_align = PANGO_ALIGN_RIGHT;
871     }
872   else
873     {
874       pango_align = PANGO_ALIGN_LEFT;
875     }
876 
877   pango_layout_set_alignment (fo_layout_get_pango_layout (area_layout->layout),
878 			      pango_align);
879   pango_layout_set_auto_dir (fo_layout_get_pango_layout (area_layout->layout),
880 			     FALSE);
881 }
882 
883 /**
884  * fo_area_layout_get_line_height:
885  * @fo_area_layout: #FoArea.
886  * @line_number:    Number of the line for which to get the height.
887  *
888  * Get the height of line @line_number.
889  *
890  * Return value: The line height in points.
891  **/
892 gdouble
fo_area_layout_get_line_height(FoArea * fo_area_layout,gint line_number)893 fo_area_layout_get_line_height (FoArea *fo_area_layout,
894 				gint    line_number)
895 {
896   g_return_val_if_fail (fo_area_layout != NULL, 0.0);
897   g_return_val_if_fail (FO_IS_AREA_LAYOUT (fo_area_layout), 0.0);
898 
899   FoAreaLayout *layout = FO_AREA_LAYOUT (fo_area_layout);
900 
901   return
902     (gdouble) GPOINTER_TO_INT (g_slist_nth_data (layout->line_heights,
903 						 line_number)) /
904     PANGO_SCALE;
905 }
906