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