3 * rendered-value.c: Management & utility routines for formated
6 * Copyright (C) 2000, 2001 Jody Goldberg (jody@gnome.org)
7 * Copyright (C) 2001-2009 Morten Welinder (terra@gnome.org)
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 2 of the
12 * License, or (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
24 #include <gnumeric-config.h>
26 #include <rendered-value.h>
31 #include <style-color.h>
32 #include <style-font.h>
33 #include <style-border.h>
34 #include <style-conditions.h>
36 #include <sheet-merge.h>
37 #include <gnm-format.h>
39 #include <parse-util.h>
44 #include <goffice/goffice.h>
46 #undef DEBUG_BOUNDING_BOX
49 #define USE_RV_POOLS 0
53 /* Memory pool for GnmRenderedValue. */
54 static GOMemChunk
*rendered_value_pool
;
55 static GOMemChunk
*rendered_rotated_value_pool
;
56 #define CHUNK_ALLOC(T,p) ((T*)go_mem_chunk_alloc (p))
57 #define CHUNK_FREE(p,v) go_mem_chunk_free ((p), (v))
59 static int rv_allocations
;
60 #define CHUNK_ALLOC(T,c) (rv_allocations++, g_slice_new (T))
61 #define CHUNK_FREE(p,v) (rv_allocations--, g_slice_free1 (sizeof(*v),(v)))
64 // Number of decimal digits in a gnm_float rounded down to an integer.
65 static int sane_digits
;
72 res
= gnm_debug_flag ("rendered-value");
78 * Some valgrind versions have a hard time with signed bitfields,
79 * such as GnmRenderedValue::rotation.
82 valgrind_bitfield_workarounds (void)
86 res
= gnm_debug_flag ("valgrind-bitfield-workarounds");
93 calc_indent (PangoContext
*context
, const GnmStyle
*mstyle
)
96 if (gnm_style_is_element_set (mstyle
, MSTYLE_INDENT
)) {
97 int n
= gnm_style_get_indent (mstyle
);
99 GnmFont
*style_font
= gnm_style_get_font (mstyle
, context
);
100 indent
= PANGO_PIXELS (n
* style_font
->go
.metrics
->avg_digit_width
);
103 return MIN (indent
, 65535);
108 gnm_rendered_value_remeasure (GnmRenderedValue
*rv
)
111 GnmRenderedRotatedValue
*rrv
= (GnmRenderedRotatedValue
*)rv
;
112 PangoContext
*context
= pango_layout_get_context (rv
->layout
);
113 double sin_a
, abs_sin_a
, cos_a
;
116 PangoLayoutIter
*iter
;
119 PangoMatrix rotmat
= PANGO_MATRIX_INIT
;
121 pango_matrix_rotate (&rotmat
, rv
->rotation
);
124 rrv
->sin_a_neg
= (sin_a
< 0);
125 abs_sin_a
= fabs (sin_a
);
127 pango_context_set_matrix (context
, &rotmat
);
128 pango_layout_context_changed (rv
->layout
);
130 rrv
->linecount
= pango_layout_get_line_count (rv
->layout
);
131 rrv
->lines
= g_new (struct GnmRenderedRotatedValueInfo
, rrv
->linecount
);
132 pango_layout_get_size (rv
->layout
, &lwidth
, NULL
);
134 rv
->layout_natural_height
= 0;
136 iter
= pango_layout_get_iter (rv
->layout
);
138 PangoRectangle logical
;
139 int x
, dx
, dy
, indent
;
140 int h
, ytop
, ybot
, baseline
;
142 pango_layout_iter_get_line_extents (iter
, NULL
, &logical
);
143 pango_layout_iter_get_line_yrange (iter
, &ytop
, &ybot
);
144 baseline
= pango_layout_iter_get_baseline (iter
);
149 if (l
== 0 && rv
->noborders
)
150 sdx
= (int)(baseline
* sin_a
- ybot
/ sin_a
);
151 dx
= sdx
+ (int)(ybot
/ sin_a
+ indent
* cos_a
);
152 dy
= (int)((baseline
- ybot
) * cos_a
- indent
* sin_a
);
154 rrv
->lines
[l
].dx
= dx
;
155 rrv
->lines
[l
].dy
= dy
;
158 x
= dx
- (int)((baseline
- ytop
) * sin_a
);
162 x
= dx
+ (int)(logical
.width
* cos_a
+ (ybot
- baseline
) * sin_a
);
165 h
= logical
.width
* abs_sin_a
+ logical
.height
* cos_a
;
166 if (h
> rv
->layout_natural_height
)
167 rv
->layout_natural_height
= h
;
170 } while (pango_layout_iter_next_line (iter
));
171 pango_layout_iter_free (iter
);
173 rv
->layout_natural_width
= x1
- x0
;
175 int dx
= rv
->layout_natural_width
;
176 for (l
= 0; l
< rrv
->linecount
; l
++)
177 rrv
->lines
[l
].dx
+= dx
;
179 for (l
= 0; l
< rrv
->linecount
; l
++)
180 rrv
->lines
[l
].dy
+= rv
->layout_natural_height
;
183 g_print ("Natural size: %d x %d\n", rv
->layout_natural_width
, rv
->layout_natural_height
);
186 pango_context_set_matrix (context
, NULL
);
187 pango_layout_context_changed (rv
->layout
);
189 pango_layout_get_size (rv
->layout
,
190 &rv
->layout_natural_width
,
191 &rv
->layout_natural_height
);
198 } rv_adjust_attributes_t
;
201 rv_adjust_filter (PangoAttribute
*attribute
, rv_adjust_attributes_t
*raat
)
203 if (attribute
->klass
->type
== PANGO_ATTR_RISE
) {
204 PangoAttrInt
*pa_rise
= (PangoAttrInt
*)attribute
;
205 pa_rise
->value
= raat
->scale
* pa_rise
->value
+ raat
->rise
;
207 if (attribute
->klass
->type
== PANGO_ATTR_SCALE
&& raat
->scale
!= 1.) {
208 PangoAttrFloat
*pa_scale
= (PangoAttrFloat
*)attribute
;
209 pa_scale
->value
= pa_scale
->value
* raat
->zoom
;
215 rv_adjust_attributes (PangoAttrList
*markup
, double zoom
, double scale
, int rise
)
217 rv_adjust_attributes_t raat
= {zoom
, scale
, rise
};
219 pango_attr_list_filter (markup
, (PangoAttrFilterFunc
) rv_adjust_filter
,
224 too_many_digits (const char *s
)
229 // Count significant digits
230 for (p
= s
; *p
; p
= g_utf8_next_char (p
)) {
231 gunichar uc
= g_utf8_get_char (p
);
232 if (uc
== '0' && count
== 0)
234 if (g_unichar_isdigit (uc
))
236 if (uc
== 'e' || uc
== 'E')
240 return count
> sane_digits
;
244 * gnm_rendered_value_new: (skip)
246 * @variable_width: Allow format to depend on column width.
248 * Formats the value of the cell according to the format style given in @mstyle
250 * Returns: a new GnmRenderedValue
253 gnm_rendered_value_new (GnmCell
const *cell
,
254 PangoContext
*context
,
255 gboolean allow_variable_width
,
258 GnmRenderedValue
*res
;
260 PangoAttrList
*attrs
;
263 gboolean displayed_formula
;
264 GnmStyle
const *mstyle
;
267 gboolean debug
= debug_rv ();
268 GnmStyleConditions
*conds
;
270 g_return_val_if_fail (cell
!= NULL
, NULL
);
272 /* sheet->workbook can be NULL when called from preview-grid.c */
273 sheet
= cell
->base
.sheet
;
276 g_printerr ("Rendering %s value [%s]\n",
278 value_peek_string (cell
->value
));
281 gnm_cell_has_expr (cell
) && sheet
->display_formulas
;
283 /* Special handling for manual recalc.
284 * If a cell has a new expression and something tries to display it we
285 * need to recalc the value */
286 if (cell
->base
.flags
& GNM_CELL_HAS_NEW_EXPR
) {
287 gnm_cell_eval ((GnmCell
*)cell
);
290 /* Must come after above gnm_cell_eval. */
291 g_return_val_if_fail (cell
->value
!= NULL
, NULL
);
293 mstyle
= gnm_cell_get_style (cell
);
295 conds
= gnm_style_get_conditions (mstyle
);
299 eval_pos_init_cell (&ep
, cell
);
301 res
= gnm_style_conditions_eval (conds
, &ep
);
303 mstyle
= gnm_style_get_cond_style (mstyle
, res
);
306 rotation
= gnm_style_get_rotation (mstyle
);
308 GnmRenderedRotatedValue
*rrv
;
311 rrv
= CHUNK_ALLOC (GnmRenderedRotatedValue
, rendered_rotated_value_pool
);
313 if (valgrind_bitfield_workarounds ()) {
314 memset (&res
->go_fore_color
+ 1,
316 (char *)(res
+ 1) - (char *)(&res
->go_fore_color
+ 1));
322 res
->noborders
= TRUE
;
323 /* Deliberately exclude diagonals. */
324 for (e
= MSTYLE_BORDER_TOP
; e
<= MSTYLE_BORDER_RIGHT
; e
++) {
325 GnmBorder
*b
= gnm_style_get_border (mstyle
, e
);
326 if (!gnm_style_border_is_blank (b
)) {
327 res
->noborders
= FALSE
;
332 res
= CHUNK_ALLOC (GnmRenderedValue
, rendered_value_pool
);
333 res
->noborders
= FALSE
;
335 res
->rotation
= rotation
;
337 res
->layout
= layout
= pango_layout_new (context
);
338 res
->hfilled
= FALSE
;
339 res
->vfilled
= FALSE
;
340 res
->variable_width
= FALSE
;
343 /* ---------------------------------------- */
345 attrs
= gnm_style_get_pango_attrs (mstyle
, context
, zoom
);
346 #ifdef DEBUG_BOUNDING_BOX
347 /* Make the whole layout end up with a red background. */
349 PangoAttrList
*new_attrs
= pango_attr_list_copy (attrs
);
350 PangoAttribute
*attr
;
352 pango_attr_list_unref (attrs
);
354 attr
= pango_attr_background_new (0xffff, 0, 0);
355 attr
->start_index
= 0;
356 attr
->end_index
= -1;
357 pango_attr_list_insert (attrs
, attr
);
363 GOFormat
const *fmt
= VALUE_FMT (cell
->value
);
364 if (fmt
!= NULL
&& go_format_is_markup (fmt
)) {
365 PangoAttrList
*orig
= attrs
;
366 const PangoAttrList
*markup
= go_format_get_markup (fmt
);
367 PangoAttrList
*c_markup
= NULL
;
368 PangoAttrIterator
*iter
;
369 GSList
*extra_attrs
= NULL
, *l
;
370 PangoFontDescription
*desc
= pango_font_description_new ();
371 double font_size
, scale
= 1., tscale
;
375 g_printerr (" Markup on value: %s\n", go_format_as_XL (fmt
));
377 attrs
= pango_attr_list_copy (attrs
);
379 iter
= pango_attr_list_get_iterator (attrs
);
380 pango_attr_iterator_get_font (iter
,
384 font_size
= pango_font_description_get_size (desc
)/
387 for (l
= extra_attrs
; l
!= NULL
; l
= l
->next
) {
388 PangoAttribute
*pa
= l
->data
;
389 if (pa
->klass
->type
== PANGO_ATTR_RISE
) {
390 PangoAttrInt
*pa_rise
= l
->data
;
391 rise
= pa_rise
->value
;
394 if (pa
->klass
->type
== PANGO_ATTR_SCALE
) {
395 PangoAttrFloat
*pa_scale
= l
->data
;
396 scale
= pa_scale
->value
;
399 g_slist_free_full (extra_attrs
,
400 (GFreeFunc
) pango_attribute_destroy
);
401 pango_font_description_free (desc
);
402 pango_attr_iterator_destroy (iter
);
404 tscale
= font_size
/10. * scale
;
405 if (tscale
!= 1|| rise
!= 0) {
406 markup
= c_markup
= pango_attr_list_copy
407 ((PangoAttrList
*)markup
);
408 rv_adjust_attributes (c_markup
, zoom
, tscale
, rise
);
411 pango_attr_list_splice (attrs
, (PangoAttrList
*)markup
, 0, 0);
412 pango_attr_list_unref (orig
);
413 pango_attr_list_unref (c_markup
);
417 pango_layout_set_attributes (res
->layout
, attrs
);
418 pango_attr_list_unref (attrs
);
420 /* Store foreground color. */
421 /* Wrapping this colour around the attribute list drops performance! */
422 res
->go_fore_color
= (gnm_style_get_font_color (mstyle
))->go_color
;
424 /* ---------------------------------------- */
427 * Excel actually does something rather weird. Just like
428 * everywhere else we just see displayed formulas as
432 (VALUE_IS_STRING (cell
->value
) || displayed_formula
) &&
433 gnm_style_get_effective_wrap_text (mstyle
);
435 res
->effective_valign
= gnm_style_get_align_v (mstyle
);
436 res
->effective_halign
= gnm_style_default_halign (mstyle
, cell
);
437 res
->indent_left
= res
->indent_right
= 0;
439 if (res
->effective_halign
== GNM_HALIGN_FILL
) {
440 pango_layout_set_single_paragraph_mode (layout
, TRUE
);
441 res
->variable_width
= TRUE
;
444 /* ---------------------------------------- */
446 res
->numeric_overflow
= FALSE
;
448 if (displayed_formula
) {
450 GnmConventionsOut out
;
451 gboolean is_array
= gnm_expr_top_is_array (cell
->base
.texpr
);
453 out
.accum
= g_string_new (is_array
? "{=" : "=");
454 out
.convs
= sheet
->convs
;
457 parse_pos_init_cell (&pp
, cell
),
458 gnm_expr_top_as_gstring (cell
->base
.texpr
, &out
);
460 g_string_append_c (out
.accum
, '}');
461 pango_layout_set_text (layout
, out
.accum
->str
, out
.accum
->len
);
462 g_string_free (out
.accum
, TRUE
);
463 res
->might_overflow
= FALSE
;
464 } else if (sheet
->hide_zero
&& gnm_cell_is_zero (cell
)) {
465 pango_layout_set_text (layout
, "", 0);
466 res
->might_overflow
= FALSE
;
469 GOFormat
const *format
= gnm_style_get_format (mstyle
);
470 GODateConventions
const *date_conv
= sheet
->workbook
471 ? sheet_date_conv (sheet
)
473 GnmFont
*font
= gnm_style_get_font (mstyle
, context
);
474 gboolean is_rotated
= (rotation
!= 0);
476 GOFormatNumberError err
;
478 if (go_format_is_general (format
) && VALUE_FMT (cell
->value
))
479 format
= VALUE_FMT (cell
->value
);
481 res
->might_overflow
= !is_rotated
&&
482 VALUE_IS_FLOAT (cell
->value
);
484 if (go_format_is_general (format
))
485 variable
= !is_rotated
&& VALUE_IS_FLOAT (cell
->value
);
487 variable
= !is_rotated
&& go_format_is_var_width (format
);
490 res
->variable_width
= TRUE
;
492 if (variable
&& allow_variable_width
) {
493 int col_width_pixels
;
495 if (gnm_cell_is_merged (cell
)) {
496 GnmRange
const *merged
=
497 gnm_sheet_merge_is_corner (sheet
, &cell
->pos
);
499 col_width_pixels
= sheet_col_get_distance_pixels
501 merged
->start
.col
, merged
->end
.col
+ 1);
503 ColRowInfo
const *ci
= sheet_col_get_info (sheet
, cell
->pos
.col
);
504 col_width_pixels
= ci
->size_pixels
;
506 col_width_pixels
-= (GNM_COL_MARGIN
+ GNM_COL_MARGIN
+ 1);
507 if (col_width_pixels
< 0)
508 col_width_pixels
= 0;
509 col_width
= col_width_pixels
* PANGO_SCALE
;
512 err
= gnm_format_layout (layout
, font
->go
.metrics
, format
,
514 col_width
, date_conv
, TRUE
);
516 // If we are formatting a number as General without a limit
517 // on size [i.e., we autofitting a column] then avoid excess
518 // precision. This is somewhat hacky.
519 if (col_width
== -1 &&
520 go_format_is_general (format
) &&
521 VALUE_IS_FLOAT (cell
->value
) &&
522 font
->go
.metrics
->min_digit_width
> 0 &&
523 too_many_digits (pango_layout_get_text (layout
))) {
525 int delta
= (font
->go
.metrics
->min_digit_width
+ 1) / 2;
526 pango_layout_get_size (layout
, &width
, NULL
);
527 col_width
= width
- delta
;
528 err
= gnm_format_layout (layout
,
529 font
->go
.metrics
, format
,
531 col_width
, date_conv
, TRUE
);
535 case GO_FORMAT_NUMBER_DATE_ERROR
:
536 pango_layout_set_text (layout
, "", -1);
537 pango_layout_set_alignment (layout
, PANGO_ALIGN_LEFT
);
538 res
->numeric_overflow
= TRUE
;
539 res
->effective_halign
= GNM_HALIGN_LEFT
;
546 /* ---------------------------------------- */
548 text
= pango_layout_get_text (layout
);
549 dir
= (text
&& *text
)? pango_find_base_dir (text
, -1): PANGO_DIRECTION_LTR
;
550 if (gnm_style_get_align_h (mstyle
) == GNM_HALIGN_GENERAL
&& dir
== PANGO_DIRECTION_RTL
) {
551 switch (res
->effective_halign
) {
552 case GNM_HALIGN_LEFT
:
553 res
->effective_halign
= GNM_HALIGN_RIGHT
;
555 case GNM_HALIGN_RIGHT
:
556 res
->effective_halign
= GNM_HALIGN_LEFT
;
560 switch (res
->effective_halign
) {
561 case GNM_HALIGN_LEFT
:
562 res
->indent_left
= calc_indent (context
, mstyle
);
563 pango_layout_set_alignment (layout
, (dir
== PANGO_DIRECTION_RTL
)? PANGO_ALIGN_RIGHT
: PANGO_ALIGN_LEFT
);
566 case GNM_HALIGN_JUSTIFY
:
567 pango_layout_set_justify (layout
, TRUE
);
568 pango_layout_set_alignment (layout
, PANGO_ALIGN_LEFT
);
571 case GNM_HALIGN_FILL
:
574 case GNM_HALIGN_RIGHT
:
575 res
->indent_right
= calc_indent (context
, mstyle
);
576 pango_layout_set_alignment (layout
, (dir
== PANGO_DIRECTION_RTL
)? PANGO_ALIGN_LEFT
: PANGO_ALIGN_RIGHT
);
579 case GNM_HALIGN_DISTRIBUTED
:
580 pango_layout_set_justify (layout
, TRUE
);
581 pango_layout_set_alignment (layout
, PANGO_ALIGN_CENTER
);
584 case GNM_HALIGN_CENTER
:
585 case GNM_HALIGN_CENTER_ACROSS_SELECTION
:
586 pango_layout_set_alignment (layout
, PANGO_ALIGN_CENTER
);
590 g_warning ("Line justification style not supported.");
592 /* ---------------------------------------- */
594 go_pango_translate_layout (layout
);
595 gnm_rendered_value_remeasure (res
);
601 gnm_rendered_value_destroy (GnmRenderedValue
*rv
)
604 g_object_unref (rv
->layout
);
609 GnmRenderedRotatedValue
*rrv
= (GnmRenderedRotatedValue
*)rv
;
611 CHUNK_FREE (rendered_rotated_value_pool
, rrv
);
613 CHUNK_FREE (rendered_value_pool
, rv
);
616 /* Return the value as a single string without format infomation.
619 gnm_rendered_value_get_text (GnmRenderedValue
const *rv
)
621 g_return_val_if_fail (rv
!= NULL
, "ERROR");
622 return pango_layout_get_text (rv
->layout
);
626 gnm_rendered_value_get_color (GnmRenderedValue
const * rv
)
628 return rv
->go_fore_color
;
631 /* ------------------------------------------------------------------------- */
638 res
= gnm_debug_flag ("rvc");
644 * gnm_rvc_new: (skip)
645 * @context: The context
648 * Returns: a new GnmRenderedValueCollection
650 GnmRenderedValueCollection
*
651 gnm_rvc_new (PangoContext
*context
, gsize size
)
653 GnmRenderedValueCollection
*res
= g_new0 (GnmRenderedValueCollection
, 1);
655 res
->context
= g_object_ref (context
);
658 res
->values
= g_hash_table_new_full
659 (g_direct_hash
, g_direct_equal
,
661 (GDestroyNotify
)gnm_rendered_value_destroy
);
664 g_printerr ("Created rendered value cache %p of size %u\n",
665 res
, (unsigned)size
);
671 gnm_rvc_free (GnmRenderedValueCollection
*rvc
)
673 g_return_if_fail (rvc
!= NULL
);
676 g_printerr ("Destroying rendered value cache %p\n", rvc
);
678 g_object_unref (rvc
->context
);
679 g_hash_table_destroy (rvc
->values
);
684 * gnm_rvc_query: (skip)
685 * @rvc: The rendered value collection
688 * Returns: the rendered value for @cell.
691 gnm_rvc_query (GnmRenderedValueCollection
*rvc
, GnmCell
const *cell
)
693 g_return_val_if_fail (rvc
!= NULL
, NULL
);
695 return g_hash_table_lookup (rvc
->values
, cell
);
699 gnm_rvc_store (GnmRenderedValueCollection
*rvc
,
701 GnmRenderedValue
*rv
)
703 g_return_if_fail (rvc
!= NULL
);
705 /* Crude cache management: */
706 if (g_hash_table_size (rvc
->values
) >= rvc
->size
) {
708 g_printerr ("Clearing rendered value cache %p\n", rvc
);
709 g_hash_table_remove_all (rvc
->values
);
712 g_hash_table_insert (rvc
->values
, (gpointer
)cell
, rv
);
716 gnm_rvc_remove (GnmRenderedValueCollection
*rvc
, GnmCell
const *cell
)
718 g_return_if_fail (rvc
!= NULL
);
719 g_hash_table_remove (rvc
->values
, (gpointer
)cell
);
722 /* ------------------------------------------------------------------------- */
725 * gnm_rendered_value_init: (skip)
728 gnm_rendered_value_init (void)
730 sane_digits
= (int)gnm_floor (GNM_MANT_DIG
* gnm_log10 (FLT_RADIX
));
733 rendered_value_pool
=
734 go_mem_chunk_new ("rendered value pool",
735 sizeof (GnmRenderedValue
),
737 rendered_rotated_value_pool
=
738 go_mem_chunk_new ("rendered rotated value pool",
739 sizeof (GnmRenderedRotatedValue
),
746 cb_rendered_value_pool_leak (gpointer data
, G_GNUC_UNUSED gpointer user
)
748 GnmRenderedValue
*rendered_value
= data
;
749 g_printerr ("Leaking rendered value at %p [%s].\n",
750 rendered_value
, pango_layout_get_text (rendered_value
->layout
));
755 * gnm_rendered_value_shutdown: (skip)
758 gnm_rendered_value_shutdown (void)
761 go_mem_chunk_foreach_leak (rendered_value_pool
, cb_rendered_value_pool_leak
, NULL
);
762 go_mem_chunk_destroy (rendered_value_pool
, FALSE
);
763 rendered_value_pool
= NULL
;
765 go_mem_chunk_foreach_leak (rendered_rotated_value_pool
, cb_rendered_value_pool_leak
, NULL
);
766 go_mem_chunk_destroy (rendered_rotated_value_pool
, FALSE
);
767 rendered_rotated_value_pool
= NULL
;
770 g_printerr ("Leaking %d rendered values.\n", rv_allocations
);