1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
4 * rendered-value.c: Management & utility routines for formated
7 * Copyright (C) 2000, 2001 Jody Goldberg (jody@gnome.org)
8 * Copyright (C) 2001-2009 Morten Welinder (terra@gnome.org)
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License as
12 * published by the Free Software Foundation; either version 2 of the
13 * License, or (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
25 #include <gnumeric-config.h>
27 #include "rendered-value.h"
32 #include "style-color.h"
33 #include "style-font.h"
34 #include "style-border.h"
35 #include "style-conditions.h"
37 #include "sheet-merge.h"
38 #include "gnm-format.h"
40 #include "parse-util.h"
45 #include <goffice/goffice.h>
47 #undef DEBUG_BOUNDING_BOX
50 #define USE_RV_POOLS 0
54 /* Memory pool for GnmRenderedValue. */
55 static GOMemChunk
*rendered_value_pool
;
56 static GOMemChunk
*rendered_rotated_value_pool
;
57 #define CHUNK_ALLOC(T,p) ((T*)go_mem_chunk_alloc (p))
58 #define CHUNK_FREE(p,v) go_mem_chunk_free ((p), (v))
60 static int rv_allocations
;
61 #define CHUNK_ALLOC(T,c) (rv_allocations++, g_slice_new (T))
62 #define CHUNK_FREE(p,v) (rv_allocations--, g_slice_free1 (sizeof(*v),(v)))
65 // Number of decimal digits in a gnm_float rounded down to an integer.
66 static int sane_digits
;
73 res
= gnm_debug_flag ("rendered-value");
79 * Some valgrind versions have a hard time with signed bitfields,
80 * such as GnmRenderedValue::rotation.
83 valgrind_bitfield_workarounds (void)
87 res
= gnm_debug_flag ("valgrind-bitfield-workarounds");
94 calc_indent (PangoContext
*context
, const GnmStyle
*mstyle
)
97 if (gnm_style_is_element_set (mstyle
, MSTYLE_INDENT
)) {
98 int n
= gnm_style_get_indent (mstyle
);
100 GnmFont
*style_font
= gnm_style_get_font (mstyle
, context
);
101 indent
= PANGO_PIXELS (n
* style_font
->go
.metrics
->avg_digit_width
);
104 return MIN (indent
, 65535);
109 gnm_rendered_value_remeasure (GnmRenderedValue
*rv
)
112 GnmRenderedRotatedValue
*rrv
= (GnmRenderedRotatedValue
*)rv
;
113 PangoContext
*context
= pango_layout_get_context (rv
->layout
);
114 double sin_a
, abs_sin_a
, cos_a
;
117 PangoLayoutIter
*iter
;
120 PangoMatrix rotmat
= PANGO_MATRIX_INIT
;
122 pango_matrix_rotate (&rotmat
, rv
->rotation
);
125 rrv
->sin_a_neg
= (sin_a
< 0);
126 abs_sin_a
= fabs (sin_a
);
128 pango_context_set_matrix (context
, &rotmat
);
129 pango_layout_context_changed (rv
->layout
);
131 rrv
->linecount
= pango_layout_get_line_count (rv
->layout
);
132 rrv
->lines
= g_new (struct GnmRenderedRotatedValueInfo
, rrv
->linecount
);
133 pango_layout_get_size (rv
->layout
, &lwidth
, NULL
);
135 rv
->layout_natural_height
= 0;
137 iter
= pango_layout_get_iter (rv
->layout
);
139 PangoRectangle logical
;
140 int x
, dx
, dy
, indent
;
141 int h
, ytop
, ybot
, baseline
;
143 pango_layout_iter_get_line_extents (iter
, NULL
, &logical
);
144 pango_layout_iter_get_line_yrange (iter
, &ytop
, &ybot
);
145 baseline
= pango_layout_iter_get_baseline (iter
);
150 if (l
== 0 && rv
->noborders
)
151 sdx
= (int)(baseline
* sin_a
- ybot
/ sin_a
);
152 dx
= sdx
+ (int)(ybot
/ sin_a
+ indent
* cos_a
);
153 dy
= (int)((baseline
- ybot
) * cos_a
- indent
* sin_a
);
155 rrv
->lines
[l
].dx
= dx
;
156 rrv
->lines
[l
].dy
= dy
;
159 x
= dx
- (int)((baseline
- ytop
) * sin_a
);
163 x
= dx
+ (int)(logical
.width
* cos_a
+ (ybot
- baseline
) * sin_a
);
166 h
= logical
.width
* abs_sin_a
+ logical
.height
* cos_a
;
167 if (h
> rv
->layout_natural_height
)
168 rv
->layout_natural_height
= h
;
171 } while (pango_layout_iter_next_line (iter
));
172 pango_layout_iter_free (iter
);
174 rv
->layout_natural_width
= x1
- x0
;
176 int dx
= rv
->layout_natural_width
;
177 for (l
= 0; l
< rrv
->linecount
; l
++)
178 rrv
->lines
[l
].dx
+= dx
;
180 for (l
= 0; l
< rrv
->linecount
; l
++)
181 rrv
->lines
[l
].dy
+= rv
->layout_natural_height
;
184 g_print ("Natural size: %d x %d\n", rv
->layout_natural_width
, rv
->layout_natural_height
);
187 pango_context_set_matrix (context
, NULL
);
188 pango_layout_context_changed (rv
->layout
);
190 pango_layout_get_size (rv
->layout
,
191 &rv
->layout_natural_width
,
192 &rv
->layout_natural_height
);
199 } rv_adjust_attributes_t
;
202 rv_adjust_filter (PangoAttribute
*attribute
, rv_adjust_attributes_t
*raat
)
204 if (attribute
->klass
->type
== PANGO_ATTR_RISE
) {
205 PangoAttrInt
*pa_rise
= (PangoAttrInt
*)attribute
;
206 pa_rise
->value
= raat
->scale
* pa_rise
->value
+ raat
->rise
;
208 if (attribute
->klass
->type
== PANGO_ATTR_SCALE
&& raat
->scale
!= 1.) {
209 PangoAttrFloat
*pa_scale
= (PangoAttrFloat
*)attribute
;
210 pa_scale
->value
= pa_scale
->value
* raat
->zoom
;
216 rv_adjust_attributes (PangoAttrList
*markup
, double zoom
, double scale
, int rise
)
218 rv_adjust_attributes_t raat
= {zoom
, scale
, rise
};
220 pango_attr_list_filter (markup
, (PangoAttrFilterFunc
) rv_adjust_filter
,
225 too_many_digits (const char *s
)
230 // Count significant digits
231 for (p
= s
; *p
; p
= g_utf8_next_char (p
)) {
232 gunichar uc
= g_utf8_get_char (p
);
233 if (uc
== '0' && count
== 0)
235 if (g_unichar_isdigit (uc
))
237 if (uc
== 'e' || uc
== 'E')
241 return count
> sane_digits
;
245 * gnm_rendered_value_new: (skip)
247 * @variable_width: Allow format to depend on column width.
249 * Formats the value of the cell according to the format style given in @mstyle
251 * Returns: a new GnmRenderedValue
254 gnm_rendered_value_new (GnmCell
const *cell
,
255 PangoContext
*context
,
256 gboolean allow_variable_width
,
259 GnmRenderedValue
*res
;
261 PangoAttrList
*attrs
;
264 gboolean displayed_formula
;
265 GnmStyle
const *mstyle
;
268 gboolean debug
= debug_rv ();
269 GnmStyleConditions
*conds
;
271 g_return_val_if_fail (cell
!= NULL
, NULL
);
273 /* sheet->workbook can be NULL when called from preview-grid.c */
274 sheet
= cell
->base
.sheet
;
277 g_printerr ("Rendering %s value [%s]\n",
279 value_peek_string (cell
->value
));
282 gnm_cell_has_expr (cell
) && sheet
->display_formulas
;
284 /* Special handling for manual recalc.
285 * If a cell has a new expression and something tries to display it we
286 * need to recalc the value */
287 if (cell
->base
.flags
& GNM_CELL_HAS_NEW_EXPR
) {
288 gnm_cell_eval ((GnmCell
*)cell
);
291 /* Must come after above gnm_cell_eval. */
292 g_return_val_if_fail (cell
->value
!= NULL
, NULL
);
294 mstyle
= gnm_cell_get_style (cell
);
296 conds
= gnm_style_get_conditions (mstyle
);
300 eval_pos_init_cell (&ep
, cell
);
302 res
= gnm_style_conditions_eval (conds
, &ep
);
304 mstyle
= gnm_style_get_cond_style (mstyle
, res
);
307 rotation
= gnm_style_get_rotation (mstyle
);
309 GnmRenderedRotatedValue
*rrv
;
312 rrv
= CHUNK_ALLOC (GnmRenderedRotatedValue
, rendered_rotated_value_pool
);
314 if (valgrind_bitfield_workarounds ()) {
315 memset (&res
->go_fore_color
+ 1,
317 (char *)(res
+ 1) - (char *)(&res
->go_fore_color
+ 1));
323 res
->noborders
= TRUE
;
324 /* Deliberately exclude diagonals. */
325 for (e
= MSTYLE_BORDER_TOP
; e
<= MSTYLE_BORDER_RIGHT
; e
++) {
326 GnmBorder
*b
= gnm_style_get_border (mstyle
, e
);
327 if (!gnm_style_border_is_blank (b
)) {
328 res
->noborders
= FALSE
;
333 res
= CHUNK_ALLOC (GnmRenderedValue
, rendered_value_pool
);
334 res
->noborders
= FALSE
;
336 res
->rotation
= rotation
;
338 res
->layout
= layout
= pango_layout_new (context
);
339 res
->hfilled
= FALSE
;
340 res
->vfilled
= FALSE
;
341 res
->variable_width
= FALSE
;
344 /* ---------------------------------------- */
346 attrs
= gnm_style_get_pango_attrs (mstyle
, context
, zoom
);
347 #ifdef DEBUG_BOUNDING_BOX
348 /* Make the whole layout end up with a red background. */
350 PangoAttrList
*new_attrs
= pango_attr_list_copy (attrs
);
351 PangoAttribute
*attr
;
353 pango_attr_list_unref (attrs
);
355 attr
= pango_attr_background_new (0xffff, 0, 0);
356 attr
->start_index
= 0;
357 attr
->end_index
= -1;
358 pango_attr_list_insert (attrs
, attr
);
364 GOFormat
const *fmt
= VALUE_FMT (cell
->value
);
365 if (fmt
!= NULL
&& go_format_is_markup (fmt
)) {
366 PangoAttrList
*orig
= attrs
;
367 const PangoAttrList
*markup
= go_format_get_markup (fmt
);
368 PangoAttrList
*c_markup
= NULL
;
369 PangoAttrIterator
*iter
;
370 GSList
*extra_attrs
= NULL
, *l
;
371 PangoFontDescription
*desc
= pango_font_description_new ();
372 double font_size
, scale
= 1., tscale
;
376 g_printerr (" Markup on value: %s\n", go_format_as_XL (fmt
));
378 attrs
= pango_attr_list_copy (attrs
);
380 iter
= pango_attr_list_get_iterator (attrs
);
381 pango_attr_iterator_get_font (iter
,
385 font_size
= pango_font_description_get_size (desc
)/
388 for (l
= extra_attrs
; l
!= NULL
; l
= l
->next
) {
389 PangoAttribute
*pa
= l
->data
;
390 if (pa
->klass
->type
== PANGO_ATTR_RISE
) {
391 PangoAttrInt
*pa_rise
= l
->data
;
392 rise
= pa_rise
->value
;
395 if (pa
->klass
->type
== PANGO_ATTR_SCALE
) {
396 PangoAttrFloat
*pa_scale
= l
->data
;
397 scale
= pa_scale
->value
;
400 g_slist_free_full (extra_attrs
,
401 (GFreeFunc
) pango_attribute_destroy
);
402 pango_font_description_free (desc
);
403 pango_attr_iterator_destroy (iter
);
405 tscale
= font_size
/10. * scale
;
406 if (tscale
!= 1|| rise
!= 0) {
407 markup
= c_markup
= pango_attr_list_copy
408 ((PangoAttrList
*)markup
);
409 rv_adjust_attributes (c_markup
, zoom
, tscale
, rise
);
412 pango_attr_list_splice (attrs
, (PangoAttrList
*)markup
, 0, 0);
413 pango_attr_list_unref (orig
);
414 pango_attr_list_unref (c_markup
);
418 pango_layout_set_attributes (res
->layout
, attrs
);
419 pango_attr_list_unref (attrs
);
421 /* Store foreground color. */
422 /* Wrapping this colour around the attribute list drops performance! */
423 res
->go_fore_color
= (gnm_style_get_font_color (mstyle
))->go_color
;
425 /* ---------------------------------------- */
428 * Excel actually does something rather weird. Just like
429 * everywhere else we just see displayed formulas as
433 (VALUE_IS_STRING (cell
->value
) || displayed_formula
) &&
434 gnm_style_get_effective_wrap_text (mstyle
);
436 res
->effective_valign
= gnm_style_get_align_v (mstyle
);
437 res
->effective_halign
= gnm_style_default_halign (mstyle
, cell
);
438 res
->indent_left
= res
->indent_right
= 0;
440 if (res
->effective_halign
== GNM_HALIGN_FILL
) {
441 pango_layout_set_single_paragraph_mode (layout
, TRUE
);
442 res
->variable_width
= TRUE
;
445 /* ---------------------------------------- */
447 res
->numeric_overflow
= FALSE
;
449 if (displayed_formula
) {
451 GnmConventionsOut out
;
452 gboolean is_array
= gnm_expr_top_is_array (cell
->base
.texpr
);
454 out
.accum
= g_string_new (is_array
? "{=" : "=");
455 out
.convs
= sheet
->convs
;
458 parse_pos_init_cell (&pp
, cell
),
459 gnm_expr_top_as_gstring (cell
->base
.texpr
, &out
);
461 g_string_append_c (out
.accum
, '}');
462 pango_layout_set_text (layout
, out
.accum
->str
, out
.accum
->len
);
463 g_string_free (out
.accum
, TRUE
);
464 res
->might_overflow
= FALSE
;
465 } else if (sheet
->hide_zero
&& gnm_cell_is_zero (cell
)) {
466 pango_layout_set_text (layout
, "", 0);
467 res
->might_overflow
= FALSE
;
470 GOFormat
const *format
= gnm_style_get_format (mstyle
);
471 GODateConventions
const *date_conv
= sheet
->workbook
472 ? sheet_date_conv (sheet
)
474 GnmFont
*font
= gnm_style_get_font (mstyle
, context
);
475 gboolean is_rotated
= (rotation
!= 0);
477 GOFormatNumberError err
;
479 if (go_format_is_general (format
) && VALUE_FMT (cell
->value
))
480 format
= VALUE_FMT (cell
->value
);
482 res
->might_overflow
= !is_rotated
&&
483 VALUE_IS_FLOAT (cell
->value
);
485 if (go_format_is_general (format
))
486 variable
= !is_rotated
&& VALUE_IS_FLOAT (cell
->value
);
488 variable
= !is_rotated
&& go_format_is_var_width (format
);
491 res
->variable_width
= TRUE
;
493 if (variable
&& allow_variable_width
) {
494 int col_width_pixels
;
496 if (gnm_cell_is_merged (cell
)) {
497 GnmRange
const *merged
=
498 gnm_sheet_merge_is_corner (sheet
, &cell
->pos
);
500 col_width_pixels
= sheet_col_get_distance_pixels
502 merged
->start
.col
, merged
->end
.col
+ 1);
504 ColRowInfo
const *ci
= sheet_col_get_info (sheet
, cell
->pos
.col
);
505 col_width_pixels
= ci
->size_pixels
;
507 col_width_pixels
-= (GNM_COL_MARGIN
+ GNM_COL_MARGIN
+ 1);
508 if (col_width_pixels
< 0)
509 col_width_pixels
= 0;
510 col_width
= col_width_pixels
* PANGO_SCALE
;
513 err
= gnm_format_layout (layout
, font
->go
.metrics
, format
,
515 col_width
, date_conv
, TRUE
);
517 // If we are formatting a number as General without a limit
518 // on size [i.e., we autofitting a column] then avoid excess
519 // precision. This is somewhat hacky.
520 if (col_width
== -1 &&
521 go_format_is_general (format
) &&
522 VALUE_IS_FLOAT (cell
->value
) &&
523 font
->go
.metrics
->min_digit_width
> 0 &&
524 too_many_digits (pango_layout_get_text (layout
))) {
526 int delta
= (font
->go
.metrics
->min_digit_width
+ 1) / 2;
527 pango_layout_get_size (layout
, &width
, NULL
);
528 col_width
= width
- delta
;
529 err
= gnm_format_layout (layout
,
530 font
->go
.metrics
, format
,
532 col_width
, date_conv
, TRUE
);
536 case GO_FORMAT_NUMBER_DATE_ERROR
:
537 pango_layout_set_text (layout
, "", -1);
538 pango_layout_set_alignment (layout
, PANGO_ALIGN_LEFT
);
539 res
->numeric_overflow
= TRUE
;
540 res
->effective_halign
= GNM_HALIGN_LEFT
;
547 /* ---------------------------------------- */
549 text
= pango_layout_get_text (layout
);
550 dir
= (text
&& *text
)? pango_find_base_dir (text
, -1): PANGO_DIRECTION_LTR
;
551 if (gnm_style_get_align_h (mstyle
) == GNM_HALIGN_GENERAL
&& dir
== PANGO_DIRECTION_RTL
) {
552 switch (res
->effective_halign
) {
553 case GNM_HALIGN_LEFT
:
554 res
->effective_halign
= GNM_HALIGN_RIGHT
;
556 case GNM_HALIGN_RIGHT
:
557 res
->effective_halign
= GNM_HALIGN_LEFT
;
561 switch (res
->effective_halign
) {
562 case GNM_HALIGN_LEFT
:
563 res
->indent_left
= calc_indent (context
, mstyle
);
564 pango_layout_set_alignment (layout
, (dir
== PANGO_DIRECTION_RTL
)? PANGO_ALIGN_RIGHT
: PANGO_ALIGN_LEFT
);
567 case GNM_HALIGN_JUSTIFY
:
568 pango_layout_set_justify (layout
, TRUE
);
569 pango_layout_set_alignment (layout
, PANGO_ALIGN_LEFT
);
572 case GNM_HALIGN_FILL
:
575 case GNM_HALIGN_RIGHT
:
576 res
->indent_right
= calc_indent (context
, mstyle
);
577 pango_layout_set_alignment (layout
, (dir
== PANGO_DIRECTION_RTL
)? PANGO_ALIGN_LEFT
: PANGO_ALIGN_RIGHT
);
580 case GNM_HALIGN_DISTRIBUTED
:
581 pango_layout_set_justify (layout
, TRUE
);
582 pango_layout_set_alignment (layout
, PANGO_ALIGN_CENTER
);
585 case GNM_HALIGN_CENTER
:
586 case GNM_HALIGN_CENTER_ACROSS_SELECTION
:
587 pango_layout_set_alignment (layout
, PANGO_ALIGN_CENTER
);
591 g_warning ("Line justification style not supported.");
593 /* ---------------------------------------- */
595 go_pango_translate_layout (layout
);
596 gnm_rendered_value_remeasure (res
);
602 gnm_rendered_value_destroy (GnmRenderedValue
*rv
)
605 g_object_unref (rv
->layout
);
610 GnmRenderedRotatedValue
*rrv
= (GnmRenderedRotatedValue
*)rv
;
612 CHUNK_FREE (rendered_rotated_value_pool
, rrv
);
614 CHUNK_FREE (rendered_value_pool
, rv
);
617 /* Return the value as a single string without format infomation.
620 gnm_rendered_value_get_text (GnmRenderedValue
const *rv
)
622 g_return_val_if_fail (rv
!= NULL
, "ERROR");
623 return pango_layout_get_text (rv
->layout
);
627 gnm_rendered_value_get_color (GnmRenderedValue
const * rv
)
629 return rv
->go_fore_color
;
632 /* ------------------------------------------------------------------------- */
639 res
= gnm_debug_flag ("rvc");
645 * gnm_rvc_new: (skip)
646 * @context: The context
649 * Returns: a new GnmRenderedValueCollection
651 GnmRenderedValueCollection
*
652 gnm_rvc_new (PangoContext
*context
, gsize size
)
654 GnmRenderedValueCollection
*res
= g_new0 (GnmRenderedValueCollection
, 1);
656 res
->context
= g_object_ref (context
);
659 res
->values
= g_hash_table_new_full
660 (g_direct_hash
, g_direct_equal
,
662 (GDestroyNotify
)gnm_rendered_value_destroy
);
665 g_printerr ("Created rendered value cache %p of size %u\n",
666 res
, (unsigned)size
);
672 gnm_rvc_free (GnmRenderedValueCollection
*rvc
)
674 g_return_if_fail (rvc
!= NULL
);
677 g_printerr ("Destroying rendered value cache %p\n", rvc
);
679 g_object_unref (rvc
->context
);
680 g_hash_table_destroy (rvc
->values
);
685 * gnm_rvc_query: (skip)
686 * @rvc: The rendered value collection
689 * Returns: the rendered value for @cell.
692 gnm_rvc_query (GnmRenderedValueCollection
*rvc
, GnmCell
const *cell
)
694 g_return_val_if_fail (rvc
!= NULL
, NULL
);
696 return g_hash_table_lookup (rvc
->values
, cell
);
700 gnm_rvc_store (GnmRenderedValueCollection
*rvc
,
702 GnmRenderedValue
*rv
)
704 g_return_if_fail (rvc
!= NULL
);
706 /* Crude cache management: */
707 if (g_hash_table_size (rvc
->values
) >= rvc
->size
) {
709 g_printerr ("Clearing rendered value cache %p\n", rvc
);
710 g_hash_table_remove_all (rvc
->values
);
713 g_hash_table_insert (rvc
->values
, (gpointer
)cell
, rv
);
717 gnm_rvc_remove (GnmRenderedValueCollection
*rvc
, GnmCell
const *cell
)
719 g_return_if_fail (rvc
!= NULL
);
720 g_hash_table_remove (rvc
->values
, (gpointer
)cell
);
723 /* ------------------------------------------------------------------------- */
726 * gnm_rendered_value_init: (skip)
729 gnm_rendered_value_init (void)
731 sane_digits
= (int)gnm_floor (GNM_MANT_DIG
* gnm_log10 (FLT_RADIX
));
734 rendered_value_pool
=
735 go_mem_chunk_new ("rendered value pool",
736 sizeof (GnmRenderedValue
),
738 rendered_rotated_value_pool
=
739 go_mem_chunk_new ("rendered rotated value pool",
740 sizeof (GnmRenderedRotatedValue
),
747 cb_rendered_value_pool_leak (gpointer data
, G_GNUC_UNUSED gpointer user
)
749 GnmRenderedValue
*rendered_value
= data
;
750 g_printerr ("Leaking rendered value at %p [%s].\n",
751 rendered_value
, pango_layout_get_text (rendered_value
->layout
));
756 * gnm_rendered_value_shutdown: (skip)
759 gnm_rendered_value_shutdown (void)
762 go_mem_chunk_foreach_leak (rendered_value_pool
, cb_rendered_value_pool_leak
, NULL
);
763 go_mem_chunk_destroy (rendered_value_pool
, FALSE
);
764 rendered_value_pool
= NULL
;
766 go_mem_chunk_foreach_leak (rendered_rotated_value_pool
, cb_rendered_value_pool_leak
, NULL
);
767 go_mem_chunk_destroy (rendered_rotated_value_pool
, FALSE
);
768 rendered_rotated_value_pool
= NULL
;
771 g_printerr ("Leaking %d rendered values.\n", rv_allocations
);