Line data Source code
1 : /* File: draw_feature_label.c; Copyright and License: see below */
2 :
3 : #include "draw/draw_feature_label.h"
4 : #include "u8/u8_trace.h"
5 : #include "entity/data_classifier.h"
6 : #include "entity/data_diagramelement.h"
7 : #include "u8/u8_f64.h"
8 : #include "utf8stringbuf/utf8stringbuf.h"
9 : #include "utf8stringbuf/utf8string.h"
10 : #include <pango/pangocairo.h>
11 : #include <stdio.h>
12 : #include <stdlib.h>
13 : #include <assert.h>
14 :
15 : static const int DRAW_FEATURE_PANGO_UNLIMITED_WIDTH = -1;
16 :
17 0 : void draw_feature_label_init( draw_feature_label_t *this_ )
18 : {
19 0 : utf8stream_writemem_init( &((*this_).text_builder), &((*this_).text_buffer), sizeof( (*this_).text_buffer) );
20 0 : draw_line_breaker_init( &((*this_).linebr) );
21 0 : draw_stereotype_image_init( &((*this_).image_renderer) );
22 0 : }
23 :
24 0 : void draw_feature_label_destroy( draw_feature_label_t *this_ )
25 : {
26 0 : draw_line_breaker_destroy( &((*this_).linebr) );
27 0 : const u8_error_t text_err = utf8stream_writemem_destroy( &((*this_).text_builder) );
28 0 : if ( text_err != U8_ERROR_NONE )
29 : {
30 0 : U8_LOG_WARNING_HEX( "error at draw/draw_classifier_label: buffer too small", text_err );
31 : }
32 0 : draw_stereotype_image_destroy( &((*this_).image_renderer) );
33 0 : }
34 :
35 0 : void draw_feature_label_get_key_and_value_dimensions ( draw_feature_label_t *this_,
36 : const data_feature_t *feature,
37 : const data_profile_part_t *profile,
38 : const geometry_dimensions_t *proposed_bounds,
39 : const pencil_size_t *pencil_size,
40 : PangoLayout *font_layout,
41 : geometry_dimensions_t *out_label_dim )
42 : {
43 0 : U8_TRACE_BEGIN();
44 0 : assert( NULL != feature );
45 0 : assert( NULL != profile );
46 0 : assert( NULL != proposed_bounds );
47 0 : assert( NULL != pencil_size );
48 0 : assert( NULL != font_layout );
49 0 : assert( NULL != out_label_dim );
50 :
51 0 : if ( data_feature_is_valid( feature ) )
52 : {
53 : /* calc stereotype image bounds */
54 0 : const char *const feature_stereotype = data_feature_get_value_const( feature );
55 : const bool has_stereotype_image
56 0 : = draw_stereotype_image_exists( &((*this_).image_renderer), feature_stereotype, profile );
57 0 : const geometry_dimensions_t icon_dim
58 : = has_stereotype_image
59 0 : ? draw_stereotype_image_get_dimensions( &((*this_).image_renderer), pencil_size )
60 0 : : (geometry_dimensions_t){ .width = 0.0, .height = 0.0 };
61 0 : const double icon_gap = has_stereotype_image ? pencil_size_get_standard_object_border( pencil_size ) : 0.0;
62 :
63 : /* draw text - except for lifelines */
64 0 : int proposed_pango_width = geometry_dimensions_get_width( proposed_bounds );
65 0 : int text_width = 0;
66 0 : int text_height = 0;
67 0 : if ( DATA_FEATURE_TYPE_LIFELINE != data_feature_get_main_type (feature) )
68 : {
69 : /* prepare text */
70 0 : u8_error_t name_err = U8_ERROR_NONE;
71 0 : utf8stream_writer_t *to_name = utf8stream_writemem_get_writer( &((*this_).text_builder) );
72 :
73 : /* append parts to name and insert linebreaks */
74 0 : utf8stringview_t feat_key = UTF8STRINGVIEW_STR( data_feature_get_key_const( feature ) );
75 0 : name_err |= draw_line_breaker_append( &((*this_).linebr), &feat_key, to_name );
76 0 : if ( data_feature_has_value( feature ) && ( ! has_stereotype_image ) )
77 : {
78 0 : name_err |= utf8stream_writer_write_str( to_name, ": " );
79 0 : utf8stringview_t feat_value = UTF8STRINGVIEW_STR( data_feature_get_value_const( feature ) );
80 0 : name_err |= draw_line_breaker_append( &((*this_).linebr), &feat_value, to_name );
81 : }
82 0 : const utf8stringview_t name = utf8stream_writemem_get_view( &((*this_).text_builder) );
83 :
84 : /* determine text width and height */
85 0 : pango_layout_set_font_description( font_layout, pencil_size_get_standard_font_description( pencil_size ) );
86 0 : pango_layout_set_text( font_layout,
87 : utf8stringview_get_start( &name ),
88 0 : utf8stringview_get_length( &name )
89 : );
90 0 : pango_layout_set_width( font_layout, proposed_pango_width * PANGO_SCALE );
91 0 : pango_layout_get_pixel_size( font_layout, &text_width, &text_height );
92 0 : text_height += PENCIL_SIZE_FONT_ALIGN_MARGIN; /* allow to align font with pixel border */
93 0 : text_width += PENCIL_SIZE_FONT_ALIGN_MARGIN;
94 : /* restore pango context */
95 0 : pango_layout_set_width( font_layout, DRAW_FEATURE_PANGO_UNLIMITED_WIDTH );
96 :
97 : /* cleanup the text_builder */
98 0 : name_err |= utf8stream_writemem_reset( &((*this_).text_builder) );
99 0 : if ( name_err != U8_ERROR_NONE )
100 : {
101 0 : U8_LOG_WARNING_HEX( "error at get_dim/draw_line_breaker_append", name_err );
102 : }
103 : }
104 :
105 0 : *out_label_dim = (geometry_dimensions_t) {
106 0 : .width = geometry_dimensions_get_width( &icon_dim ) + icon_gap + text_width,
107 0 : .height = u8_f64_max2( geometry_dimensions_get_height( &icon_dim ), text_height )
108 : };
109 : }
110 : else
111 : {
112 0 : U8_LOG_ERROR("invalid feature in draw_feature_label_get_key_and_value_dimensions()");
113 0 : *out_label_dim = (geometry_dimensions_t) { .width = 0.0, .height = 0.0 };
114 : }
115 0 : U8_TRACE_END();
116 0 : }
117 :
118 0 : void draw_feature_label_draw_key_and_value ( draw_feature_label_t *this_,
119 : const data_feature_t *feature,
120 : const data_profile_part_t *profile,
121 : const GdkRGBA *color,
122 : const geometry_rectangle_t *label_box,
123 : const pencil_size_t *pencil_size,
124 : PangoLayout *font_layout,
125 : cairo_t *cr )
126 : {
127 0 : U8_TRACE_BEGIN();
128 0 : assert( NULL != feature );
129 0 : assert( NULL != profile );
130 0 : assert( NULL != color );
131 0 : assert( NULL != label_box );
132 0 : assert( NULL != pencil_size );
133 0 : assert( NULL != font_layout );
134 0 : assert( NULL != cr );
135 :
136 : /* calc bounds of stereotype icon */
137 0 : const char *const feature_stereotype = data_feature_get_value_const( feature );
138 : const bool has_stereotype_image
139 0 : = draw_stereotype_image_exists( &((*this_).image_renderer), feature_stereotype, profile );
140 0 : const geometry_rectangle_t stereotype_box
141 : = has_stereotype_image
142 0 : ? draw_stereotype_image_get_bounds( &((*this_).image_renderer),
143 : geometry_rectangle_get_left( label_box ),
144 : geometry_rectangle_get_top( label_box ),
145 : GEOMETRY_H_ALIGN_LEFT,
146 : GEOMETRY_V_ALIGN_TOP,
147 : pencil_size
148 : )
149 0 : : (geometry_rectangle_t){ .left = 0.0, .top = 0.0, .width = 0.0, .height = 0.0 };
150 0 : const double icon_gap = has_stereotype_image ? pencil_size_get_standard_object_border( pencil_size ) : 0.0;
151 :
152 : /* draw stereotype icon */
153 0 : if ( has_stereotype_image )
154 : {
155 : u8_error_info_t err_info;
156 : const u8_error_t stereotype_err
157 0 : = draw_stereotype_image_draw( &((*this_).image_renderer),
158 : feature_stereotype,
159 : profile,
160 : color,
161 : &err_info,
162 : &stereotype_box,
163 : cr
164 : );
165 0 : if ( u8_error_info_is_error( &err_info ) )
166 : {
167 0 : U8_LOG_WARNING_INT( "stereotype image: unxpected token in svg path in line",
168 : u8_error_info_get_line( &err_info )
169 : );
170 : }
171 0 : else if ( stereotype_err != U8_ERROR_NONE )
172 : {
173 0 : U8_LOG_WARNING_HEX( "error at drawing stereotype image", stereotype_err );
174 : }
175 : }
176 :
177 : /* define names for input data: */
178 0 : const double text_left
179 0 : = geometry_rectangle_get_left( label_box ) + geometry_rectangle_get_width( &stereotype_box ) + icon_gap;
180 0 : const double text_top = geometry_rectangle_get_top( label_box );
181 0 : const double text_width
182 0 : = geometry_rectangle_get_width( label_box ) - geometry_rectangle_get_width( &stereotype_box ) - icon_gap;
183 :
184 : /* draw text - except for lifelines */
185 0 : if ( DATA_FEATURE_TYPE_LIFELINE != data_feature_get_main_type (feature) )
186 : {
187 : /* prepare text */
188 0 : u8_error_t name_err = U8_ERROR_NONE;
189 0 : utf8stream_writer_t *to_name = utf8stream_writemem_get_writer( &((*this_).text_builder) );
190 :
191 : /* append parts to name and insert linebreaks */
192 0 : utf8stringview_t feat_key = UTF8STRINGVIEW_STR( data_feature_get_key_const( feature ) );
193 0 : name_err |= draw_line_breaker_append( &((*this_).linebr), &feat_key, to_name );
194 0 : if ( data_feature_has_value( feature ) && ( ! has_stereotype_image ) )
195 : {
196 0 : name_err |= utf8stream_writer_write_str( to_name, ": " );
197 0 : utf8stringview_t feat_value = UTF8STRINGVIEW_STR( data_feature_get_value_const( feature ) );
198 0 : name_err |= draw_line_breaker_append( &((*this_).linebr), &feat_value, to_name );
199 : }
200 0 : const utf8stringview_t name = utf8stream_writemem_get_view( &((*this_).text_builder) );
201 :
202 0 : const double f_size = pencil_size_get_standard_font_size( pencil_size );
203 0 : cairo_set_source_rgba( cr, color->red, color->green, color->blue, color->alpha );
204 0 : pango_layout_set_font_description (font_layout, pencil_size_get_standard_font_description(pencil_size) );
205 0 : pango_layout_set_text (font_layout,
206 : utf8stringview_get_start( &name ),
207 0 : utf8stringview_get_length( &name )
208 : );
209 0 : pango_layout_set_width(font_layout, (text_width+f_size) * PANGO_SCALE ); /* add gap to avoid line breaks by rounding errors and whitespace character widths */
210 :
211 : /* draw text */
212 0 : cairo_move_to ( cr, ceil( text_left ), ceil( text_top ) ); /* align font with pixel border */
213 0 : pango_cairo_show_layout (cr, font_layout);
214 :
215 : /* restore pango context */
216 0 : pango_layout_set_width(font_layout, DRAW_FEATURE_PANGO_UNLIMITED_WIDTH);
217 :
218 : /* cleanup the text_builder */
219 0 : name_err |= utf8stream_writemem_reset( &((*this_).text_builder) );
220 0 : if ( name_err != U8_ERROR_NONE )
221 : {
222 0 : U8_LOG_WARNING_HEX( "error at get_dim/draw_line_breaker_append", name_err );
223 : }
224 : }
225 :
226 0 : U8_TRACE_END();
227 0 : }
228 :
229 :
230 : /*
231 : Copyright 2017-2024 Andreas Warnke
232 : http://www.apache.org/licenses/LICENSE-2.0
233 :
234 : Licensed under the Apache License, Version 2.0 (the "License");
235 : you may not use this file except in compliance with the License.
236 : You may obtain a copy of the License at
237 :
238 :
239 : Unless required by applicable law or agreed to in writing, software
240 : distributed under the License is distributed on an "AS IS" BASIS,
241 : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
242 : See the License for the specific language governing permissions and
243 : limitations under the License.
244 : */
|