LCOV - code coverage report
Current view: top level - pencil/source/draw - draw_relationship_label.c (source / functions) Coverage Total Hit
Test: crystal-facet-uml_v1.63.2_covts Lines: 0.0 % 143 0
Test Date: 2025-05-01 10:10:14 Functions: 0.0 % 4 0

            Line data    Source code
       1              :   /* File: draw_relationship_label.c; Copyright and License: see below */
       2              : 
       3              : #include "draw/draw_relationship_label.h"
       4              : #include "u8/u8_trace.h"
       5              : #include "entity/data_classifier.h"
       6              : #include "entity/data_diagramelement.h"
       7              : #include "u8/u8_i32.h"
       8              : #include "u8/u8_f64.h"
       9              : #include "utf8stringbuf/utf8stringbuf.h"
      10              : #include "utf8stringbuf/utf8string.h"
      11              : #include <pango/pangocairo.h>
      12              : #include <stdio.h>
      13              : #include <stdlib.h>
      14              : #include <assert.h>
      15              : 
      16              : static const int DRAW_RELATIONSHIP_PANGO_UNLIMITED_WIDTH = -1;
      17              : static const int DRAW_RELATIONSHIP_PANGO_AUTO_DETECT_LENGTH = -1;
      18              : #define DRAW_RELATIONSHIP_LEFT_GUILLEMENTS "\xc2\xab"
      19              : #define DRAW_RELATIONSHIP_RIGHT_GUILLEMENTS "\xc2\xbb"
      20              : 
      21            0 : void draw_relationship_label_init( draw_relationship_label_t *this_ )
      22              : {
      23            0 :     utf8stream_writemem_init( &((*this_).text_builder), &((*this_).text_buffer), sizeof( (*this_).text_buffer) );
      24            0 :     draw_line_breaker_init( &((*this_).linebr) );
      25            0 :     draw_stereotype_icon_init( &((*this_).image_renderer) );
      26            0 : }
      27              : 
      28            0 : void draw_relationship_label_destroy( draw_relationship_label_t *this_ )
      29              : {
      30            0 :     draw_line_breaker_destroy( &((*this_).linebr) );
      31            0 :     const u8_error_t text_err = utf8stream_writemem_destroy( &((*this_).text_builder) );
      32            0 :     if ( text_err != U8_ERROR_NONE )
      33              :     {
      34            0 :         U8_LOG_WARNING_HEX( "error at draw/draw_classifier_label: buffer too small", text_err );
      35              :     }
      36            0 :     draw_stereotype_icon_destroy( &((*this_).image_renderer) );
      37            0 : }
      38              : 
      39            0 : void draw_relationship_label_get_type_and_name_dimensions ( draw_relationship_label_t *this_,
      40              :                                                             const data_relationship_t *relationship,
      41              :                                                             const data_profile_part_t *profile,
      42              :                                                             const geometry_dimensions_t *proposed_bounds,
      43              :                                                             const pencil_size_t *pencil_size,
      44              :                                                             PangoLayout *font_layout,
      45              :                                                             geometry_dimensions_t *out_label_dim )
      46              : {
      47            0 :     U8_TRACE_BEGIN();
      48            0 :     assert( NULL != relationship );
      49            0 :     assert( NULL != profile );
      50            0 :     assert( NULL != proposed_bounds );
      51            0 :     assert( NULL != pencil_size );
      52            0 :     assert( NULL != font_layout );
      53            0 :     assert( NULL != out_label_dim );
      54              : 
      55            0 :     if ( data_relationship_is_valid( relationship ) )
      56              :     {
      57              :         /* calc stereotype image bounds */
      58            0 :         const char *const relationship_stereotype = data_relationship_get_stereotype_const( relationship );
      59              :         const bool has_stereotype_image
      60            0 :             = draw_stereotype_icon_exists( &((*this_).image_renderer), relationship_stereotype, profile );
      61            0 :         const geometry_dimensions_t icon_dim
      62              :             = has_stereotype_image
      63            0 :             ? draw_stereotype_icon_get_dimensions( &((*this_).image_renderer), pencil_size )
      64            0 :             : (geometry_dimensions_t){ .width = 0.0, .height = 0.0 };
      65            0 :         const double icon_gap = has_stereotype_image ? pencil_size_get_standard_object_border( pencil_size ) : 0.0;
      66              : 
      67              :         /* define names for input data */
      68            0 :         int proposed_pango_width = geometry_dimensions_get_width( proposed_bounds );
      69            0 :         const double f_line_gap = pencil_size_get_font_line_gap( pencil_size );
      70              : 
      71              :         /* calc dimensions of stereotype as text */
      72            0 :         int text1_height = 0;
      73            0 :         int text1_width = 0;
      74              :         {
      75            0 :             const data_relationship_type_t rel_type = data_relationship_get_main_type( relationship );
      76              :             const char *const pseudo_stereotype
      77            0 :                 = draw_relationship_label_private_stereotype_from_type( this_, rel_type );
      78            0 :             const bool has_pseudo_stereotype = ( ! utf8string_equals_str( pseudo_stereotype, "" ) );
      79            0 :             const bool has_stereotype = ( ! utf8string_equals_str( relationship_stereotype, "" ) );
      80              : 
      81            0 :             if ( ( ! has_stereotype_image ) && ( has_pseudo_stereotype || has_stereotype ) )
      82              :             {
      83              :                 /* prepare text */
      84              :                 char stereotype_text[DATA_CLASSIFIER_MAX_STEREOTYPE_SIZE+4];
      85            0 :                 utf8stringbuf_t stereotype_buf = UTF8STRINGBUF(stereotype_text);
      86            0 :                 utf8stringbuf_copy_str( &stereotype_buf, DRAW_RELATIONSHIP_LEFT_GUILLEMENTS );
      87            0 :                 if ( has_stereotype )
      88              :                 {
      89            0 :                     utf8stringbuf_append_str( &stereotype_buf, relationship_stereotype );
      90              :                 }
      91              :                 else
      92              :                 {
      93            0 :                     utf8stringbuf_append_str( &stereotype_buf, pseudo_stereotype );
      94              :                 }
      95            0 :                 utf8stringbuf_append_str( &stereotype_buf, DRAW_RELATIONSHIP_RIGHT_GUILLEMENTS );
      96              : 
      97              :                 /* determine text width and height */
      98            0 :                 pango_layout_set_font_description( font_layout, pencil_size_get_footnote_font_description( pencil_size ) );
      99            0 :                 pango_layout_set_text( font_layout, utf8stringbuf_get_string( &stereotype_buf ), DRAW_RELATIONSHIP_PANGO_AUTO_DETECT_LENGTH );
     100            0 :                 pango_layout_get_pixel_size( font_layout, &text1_width, &text1_height );
     101            0 :                 text1_height += PENCIL_SIZE_FONT_ALIGN_MARGIN;  /* allow to align font with pixel border */
     102            0 :                 text1_width += PENCIL_SIZE_FONT_ALIGN_MARGIN;
     103              :             }
     104              :         }
     105              : 
     106              :         /* calc name text dimensions */
     107            0 :         int text2_height = 0;
     108            0 :         int text2_width = 0;
     109            0 :         if ( 0 != utf8string_get_length( data_relationship_get_name_const( relationship ) ))
     110              :         {
     111            0 :             u8_error_t name_err = U8_ERROR_NONE;
     112            0 :             utf8stream_writer_t *to_name = utf8stream_writemem_get_writer( &((*this_).text_builder) );
     113              : 
     114              :             /* append parts to name and insert linebreaks */
     115            0 :             utf8stringview_t rel_name = UTF8STRINGVIEW_STR( data_relationship_get_name_const( relationship ) );
     116            0 :             name_err |= draw_line_breaker_append( &((*this_).linebr), &rel_name, to_name );
     117            0 :             const utf8stringview_t name = utf8stream_writemem_get_view( &((*this_).text_builder) );
     118              : 
     119            0 :             pango_layout_set_font_description( font_layout, pencil_size_get_standard_font_description(pencil_size) );
     120            0 :             pango_layout_set_text( font_layout,
     121              :                                    utf8stringview_get_start( &name ),
     122            0 :                                    utf8stringview_get_length( &name )
     123              :                                  );
     124            0 :             pango_layout_set_width( font_layout, proposed_pango_width * PANGO_SCALE );
     125            0 :             pango_layout_get_pixel_size( font_layout, &text2_width, &text2_height );
     126            0 :             text2_height += PENCIL_SIZE_FONT_ALIGN_MARGIN;  /* allow to align font with pixel border */
     127            0 :             text2_width += PENCIL_SIZE_FONT_ALIGN_MARGIN;
     128              : 
     129              :             /* restore pango context */
     130            0 :             pango_layout_set_width(font_layout, DRAW_RELATIONSHIP_PANGO_UNLIMITED_WIDTH);
     131              : 
     132              :             /* cleanup the text_builder */
     133            0 :             name_err |= utf8stream_writemem_reset( &((*this_).text_builder) );
     134            0 :             if ( name_err != U8_ERROR_NONE )
     135              :             {
     136            0 :                 U8_LOG_WARNING_HEX( "error at get_dim/draw_line_breaker_append", name_err );
     137              :             }
     138              :         }
     139              : 
     140            0 :         *out_label_dim = (geometry_dimensions_t){
     141            0 :             .width = geometry_dimensions_get_width( &icon_dim ) + icon_gap + u8_i32_max2( text2_width, text1_width ),
     142            0 :             .height = u8_f64_max2( geometry_dimensions_get_height( &icon_dim ), text1_height + f_line_gap + text2_height )
     143              :         };
     144              :     }
     145              :     else
     146              :     {
     147            0 :         U8_LOG_ERROR("invalid relationship in draw_relationship_label_get_type_and_name_dimensions()");
     148            0 :         *out_label_dim = (geometry_dimensions_t) { .width = 0.0, .height = 0.0 };
     149              :     }
     150            0 :     U8_TRACE_END();
     151            0 : }
     152              : 
     153            0 : void draw_relationship_label_draw_type_and_name ( draw_relationship_label_t *this_,
     154              :                                                   const data_relationship_t *relationship,
     155              :                                                   const data_profile_part_t *profile,
     156              :                                                   const GdkRGBA *color,
     157              :                                                   const geometry_rectangle_t *label_box,
     158              :                                                   const pencil_size_t *pencil_size,
     159              :                                                   PangoLayout *font_layout,
     160              :                                                   cairo_t *cr )
     161              : {
     162            0 :     U8_TRACE_BEGIN();
     163            0 :     assert( NULL != relationship );
     164            0 :     assert( NULL != profile );
     165            0 :     assert( NULL != color );
     166            0 :     assert( NULL != label_box );
     167            0 :     assert( NULL != pencil_size );
     168            0 :     assert( NULL != font_layout );
     169            0 :     assert( NULL != cr );
     170              : 
     171              :     /* calc bounds of stereotype icon */
     172            0 :     const char *const relationship_stereotype = data_relationship_get_stereotype_const( relationship );
     173              :     const bool has_stereotype_image
     174            0 :         = draw_stereotype_icon_exists( &((*this_).image_renderer), relationship_stereotype, profile );
     175            0 :     const geometry_rectangle_t stereotype_box
     176              :         = has_stereotype_image
     177            0 :         ? draw_stereotype_icon_get_bounds( &((*this_).image_renderer),
     178              :                                             geometry_rectangle_get_left( label_box ),
     179              :                                             geometry_rectangle_get_top( label_box ),
     180              :                                             GEOMETRY_H_ALIGN_LEFT,
     181              :                                             GEOMETRY_V_ALIGN_TOP,
     182              :                                             pencil_size
     183              :                                           )
     184            0 :         : (geometry_rectangle_t){ .left = 0.0, .top = 0.0, .width = 0.0, .height = 0.0 };
     185            0 :     const double icon_gap = has_stereotype_image ? pencil_size_get_standard_object_border( pencil_size ) : 0.0;
     186              : 
     187              :     /* draw stereotype icon */
     188            0 :     if ( has_stereotype_image )
     189              :     {
     190              :         u8_error_info_t err_info;
     191              :         const u8_error_t stereotype_err
     192            0 :             = draw_stereotype_icon_draw( &((*this_).image_renderer),
     193              :                                           relationship_stereotype,
     194              :                                           profile,
     195              :                                           color,
     196              :                                           &err_info,
     197              :                                           &stereotype_box,
     198              :                                           cr
     199              :                                         );
     200            0 :         if ( u8_error_info_is_error( &err_info ) )
     201              :         {
     202            0 :             U8_LOG_WARNING_INT( "stereotype image: unxpected token in svg path in line",
     203              :                                 u8_error_info_get_line( &err_info )
     204              :                               );
     205              :         }
     206            0 :         else if ( stereotype_err != U8_ERROR_NONE )
     207              :         {
     208            0 :             U8_LOG_WARNING_HEX( "error at drawing stereotype image", stereotype_err );
     209              :         }
     210              :     }
     211              : 
     212              :     /* define names for input data */
     213            0 :     const double text_width
     214            0 :         = geometry_rectangle_get_width( label_box ) - geometry_rectangle_get_width( &stereotype_box ) - icon_gap;
     215            0 :     const double center_x
     216            0 :         = geometry_rectangle_get_left( label_box ) + geometry_rectangle_get_width( &stereotype_box ) + icon_gap
     217            0 :         + 0.5 * text_width;
     218            0 :     const double top = geometry_rectangle_get_top( label_box );
     219            0 :     const double f_line_gap = pencil_size_get_font_line_gap( pencil_size );
     220              : 
     221              :     /* draw stereotype as text */
     222            0 :     int text1_height = 0;
     223              :     {
     224            0 :         const data_relationship_type_t rel_type = data_relationship_get_main_type( relationship );
     225              :         const char *const pseudo_stereotype
     226            0 :             = draw_relationship_label_private_stereotype_from_type( this_, rel_type );
     227            0 :         const bool has_pseudo_stereotype = ( ! utf8string_equals_str( pseudo_stereotype, "" ) );
     228            0 :         const bool has_stereotype = ( ! utf8string_equals_str( relationship_stereotype, "" ) );
     229              : 
     230            0 :         if ( ( ! has_stereotype_image ) && ( has_pseudo_stereotype || has_stereotype ) )
     231              :         {
     232              :             /* prepare text */
     233              :             char stereotype_text[DATA_CLASSIFIER_MAX_STEREOTYPE_SIZE+4];
     234            0 :             utf8stringbuf_t stereotype_buf = UTF8STRINGBUF(stereotype_text);
     235            0 :             utf8stringbuf_copy_str( &stereotype_buf, DRAW_RELATIONSHIP_LEFT_GUILLEMENTS );
     236            0 :             if ( has_stereotype )
     237              :             {
     238            0 :                 utf8stringbuf_append_str( &stereotype_buf, relationship_stereotype );
     239              :             }
     240              :             else
     241              :             {
     242            0 :                 utf8stringbuf_append_str( &stereotype_buf, pseudo_stereotype );
     243              :             }
     244            0 :             utf8stringbuf_append_str( &stereotype_buf, DRAW_RELATIONSHIP_RIGHT_GUILLEMENTS );
     245              : 
     246              :             int text1_width;
     247            0 :             cairo_set_source_rgba( cr, color->red, color->green, color->blue, color->alpha );
     248            0 :             pango_layout_set_font_description( font_layout, pencil_size_get_footnote_font_description( pencil_size ) );
     249            0 :             pango_layout_set_text( font_layout,
     250            0 :                                    utf8stringbuf_get_string( &stereotype_buf ),
     251              :                                    DRAW_RELATIONSHIP_PANGO_AUTO_DETECT_LENGTH
     252              :                                  );
     253            0 :             pango_layout_get_pixel_size( font_layout, &text1_width, &text1_height );
     254              : 
     255              :             /* draw text */
     256            0 :             cairo_move_to( cr, ceil( center_x - 0.5*text1_width ), ceil( top ) );  /* align font with pixel border */
     257            0 :             pango_cairo_show_layout( cr, font_layout );
     258              :         }
     259              :     }
     260              : 
     261              :     /* draw name text */
     262            0 :     if ( 0 != utf8string_get_length( data_relationship_get_name_const( relationship ) ))
     263              :     {
     264            0 :         u8_error_t name_err = U8_ERROR_NONE;
     265            0 :         utf8stream_writer_t *to_name = utf8stream_writemem_get_writer( &((*this_).text_builder) );
     266              : 
     267              :         /* append parts to name and insert linebreaks */
     268            0 :         utf8stringview_t rel_name = UTF8STRINGVIEW_STR( data_relationship_get_name_const( relationship ) );
     269            0 :         name_err |= draw_line_breaker_append( &((*this_).linebr), &rel_name, to_name );
     270            0 :         const utf8stringview_t name = utf8stream_writemem_get_view( &((*this_).text_builder) );
     271              : 
     272              :         int text2_height;
     273              :         int text2_width;
     274            0 :         const double f_size = pencil_size_get_standard_font_size( pencil_size );
     275            0 :         cairo_set_source_rgba( cr, color->red, color->green, color->blue, color->alpha );
     276            0 :         pango_layout_set_font_description (font_layout, pencil_size_get_standard_font_description(pencil_size) );
     277            0 :         pango_layout_set_text( font_layout,
     278              :                                utf8stringview_get_start( &name ),
     279            0 :                                utf8stringview_get_length( &name )
     280              :                              );
     281            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 */
     282            0 :         pango_layout_get_pixel_size (font_layout, &text2_width, &text2_height);
     283              : 
     284              :         /* draw text */
     285            0 :         cairo_move_to( cr,
     286            0 :                        ceil( center_x - 0.5*text2_width ),
     287            0 :                        ceil( top + text1_height + f_line_gap )
     288              :                      );  /* align font with pixel border */
     289            0 :         pango_cairo_show_layout( cr, font_layout );
     290              : 
     291              :         /* restore pango context */
     292            0 :         pango_layout_set_width(font_layout, DRAW_RELATIONSHIP_PANGO_UNLIMITED_WIDTH);
     293              : 
     294              :         /* cleanup the text_builder */
     295            0 :         name_err |= utf8stream_writemem_reset( &((*this_).text_builder) );
     296            0 :         if ( name_err != U8_ERROR_NONE )
     297              :         {
     298            0 :             U8_LOG_WARNING_HEX( "error at get_dim/draw_line_breaker_append", name_err );
     299              :         }
     300              :     }
     301              : 
     302            0 :     U8_TRACE_END();
     303            0 : }
     304              : 
     305              : 
     306              : /*
     307              : Copyright 2017-2025 Andreas Warnke
     308              :     http://www.apache.org/licenses/LICENSE-2.0
     309              : 
     310              : Licensed under the Apache License, Version 2.0 (the "License");
     311              : you may not use this file except in compliance with the License.
     312              : You may obtain a copy of the License at
     313              : 
     314              : 
     315              : Unless required by applicable law or agreed to in writing, software
     316              : distributed under the License is distributed on an "AS IS" BASIS,
     317              : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     318              : See the License for the specific language governing permissions and
     319              : limitations under the License.
     320              : */
        

Generated by: LCOV version 2.0-1