LCOV - code coverage report
Current view: top level - pencil/source/draw - draw_classifier_label.c (source / functions) Coverage Total Hit
Test: crystal-facet-uml_v1.71.2_covts Lines: 48.8 % 160 78
Test Date: 2026-08-22 19:47:36 Functions: 75.0 % 4 3

            Line data    Source code
       1              : /* File: draw_classifier_label.c; Copyright and License: see below */
       2              : 
       3              : #include "draw/draw_classifier_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 "utf8stream/utf8stream_writer.h"
       9              : #include "utf8stringbuf/utf8stringbuf.h"
      10              : #include "utf8stringbuf/utf8string.h"
      11              : #include "u8stream/universal_memory_output_stream.h"
      12              : #include "u8stream/universal_output_stream.h"
      13              : #include <pango/pangocairo.h>
      14              : #include <stdio.h>
      15              : #include <stdlib.h>
      16              : #include <assert.h>
      17              : 
      18              : static const int DRAW_CLASSIFIER_PANGO_UNLIMITED_WIDTH = -1;
      19              : static const int DRAW_CLASSIFIER_PANGO_AUTO_DETECT_LENGTH = -1;
      20              : #define DRAW_CLASSIFIER_LEFT_GUILLEMETS "\xc2\xab"
      21              : #define DRAW_CLASSIFIER_RIGHT_GUILLEMETS "\xc2\xbb"
      22              : #define DRAW_CLASSIFIER_COLON ':'
      23              : 
      24            3 : void draw_classifier_label_init( draw_classifier_label_t *this_ )
      25              : {
      26              :     assert( DATA_CLASSIFIER_MAX_NAME_SIZE == DATA_CLASSIFIER_MAX_STEREOTYPE_SIZE );
      27            3 :     utf8stream_writemem_init( &((*this_).text_builder), &((*this_).text_buffer), sizeof( (*this_).text_buffer) );
      28            3 :     draw_line_breaker_init( &((*this_).linebr) );
      29            3 : }
      30              : 
      31            3 : void draw_classifier_label_destroy( draw_classifier_label_t *this_ )
      32              : {
      33            3 :     draw_line_breaker_destroy( &((*this_).linebr) );
      34            3 :     const u8_error_t text_err = utf8stream_writemem_destroy( &((*this_).text_builder) );
      35            3 :     if ( text_err != U8_ERROR_NONE )
      36              :     {
      37            0 :         U8_LOG_WARNING_HEX( "error at draw/draw_classifier_label: buffer too small", text_err );
      38              :     }
      39            3 : }
      40              : 
      41          256 : void draw_classifier_label_get_stereotype_and_name_dimensions( draw_classifier_label_t *this_,
      42              :                                                                const data_visible_classifier_t *visible_classifier,
      43              :                                                                bool with_stereotype,
      44              :                                                                const geometry_dimensions_t *proposed_bounds,
      45              :                                                                unsigned int max_recursions,
      46              :                                                                const pencil_size_t *pencil_size,
      47              :                                                                PangoLayout *font_layout,
      48              :                                                                geometry_dimensions_t *out_label_dim )
      49              : {
      50          256 :     U8_TRACE_BEGIN();
      51          256 :     assert( NULL != visible_classifier );
      52          256 :     assert( NULL != proposed_bounds );
      53          256 :     assert( NULL != pencil_size );
      54          256 :     assert( NULL != font_layout );
      55          256 :     assert( NULL != out_label_dim );
      56              : 
      57          256 :     if ( data_visible_classifier_is_valid( visible_classifier ) )
      58              :     {
      59              :         /* get data objects */
      60              :         const data_classifier_t *const classifier
      61          256 :             = data_visible_classifier_get_classifier_const( visible_classifier );
      62              :         const data_diagramelement_t *const diagramelement
      63          256 :             = data_visible_classifier_get_diagramelement_const( visible_classifier );
      64              :         const data_diagramelement_flag_t display_flags
      65          256 :             = data_diagramelement_get_display_flags( diagramelement );
      66              : 
      67              :         /* get layout sizes */
      68          256 :         const int proposed_pango_width = (int) geometry_dimensions_get_width( proposed_bounds );
      69              : 
      70              :         /* stereotype text */
      71          256 :         int text1_height = 0;
      72          256 :         int text1_width = 0;
      73              :         {
      74          256 :             if ( with_stereotype && data_classifier_has_stereotype( classifier ) )
      75              :             {
      76              :                 /* prepare text */
      77          256 :                 u8_error_t name_err = U8_ERROR_NONE;
      78          256 :                 utf8stream_writer_t *to_name = utf8stream_writemem_get_writer( &((*this_).text_builder) );
      79          256 :                 name_err |= utf8stream_writer_write_str( to_name, DRAW_CLASSIFIER_LEFT_GUILLEMETS );
      80          256 :                 utf8stringview_t stereo_name = UTF8STRINGVIEW_STR( data_classifier_get_stereotype_const( classifier ) );
      81              :                 /* insert linebreaks */
      82          256 :                 name_err |= draw_line_breaker_append( &((*this_).linebr), &stereo_name, to_name );
      83          256 :                 name_err |= utf8stream_writer_write_str( to_name, DRAW_CLASSIFIER_RIGHT_GUILLEMETS );
      84          256 :                 const utf8stringview_t stereo = utf8stream_writemem_get_view( &((*this_).text_builder) );
      85              : 
      86              :                 /* determine text width and height */
      87          256 :                 pango_layout_set_font_description( font_layout, pencil_size_get_standard_font_description(pencil_size) );
      88          512 :                 pango_layout_set_text( font_layout,
      89              :                                        utf8stringview_get_start( &stereo ),
      90          256 :                                        utf8stringview_get_length( &stereo )
      91              :                                      );
      92          256 :                 pango_layout_set_width( font_layout, proposed_pango_width * PANGO_SCALE );
      93          256 :                 pango_layout_get_pixel_size( font_layout, &text1_width, &text1_height) ;
      94          256 :                 text1_height += PENCIL_SIZE_FONT_ALIGN_MARGIN;  /* allow to align font with pixel border */
      95          256 :                 text1_width += PENCIL_SIZE_FONT_ALIGN_MARGIN;
      96              : 
      97              :                 /* restore pango context */
      98          256 :                 pango_layout_set_width(font_layout, DRAW_CLASSIFIER_PANGO_UNLIMITED_WIDTH );
      99              : 
     100              :                 /* cleanup the text_builder */
     101          256 :                 name_err |= utf8stream_writemem_reset( &((*this_).text_builder) );
     102          256 :                 if ( name_err != U8_ERROR_NONE )
     103              :                 {
     104            0 :                     U8_LOG_WARNING_HEX( "error at get_dim/draw_line_breaker_append", name_err );
     105              :                 }
     106              :             }
     107              :         }
     108              : 
     109              :         /* draw name text */
     110          256 :         int text2_width = 0;
     111          256 :         int text2_height = 0;
     112              :         double space_for_line;
     113              :         {
     114              :             /* prepare text */
     115          256 :             u8_error_t name_err = U8_ERROR_NONE;
     116          256 :             utf8stream_writer_t *to_name = utf8stream_writemem_get_writer( &((*this_).text_builder) );
     117              : 
     118          256 :             if ( 0 != ( display_flags & DATA_DIAGRAMELEMENT_FLAG_ANONYMOUS_INSTANCE ) )
     119              :             {
     120            0 :                 name_err |= utf8stream_writer_write_char( to_name, DRAW_CLASSIFIER_COLON );
     121              :             }
     122          256 :             utf8stringview_t class_name = UTF8STRINGVIEW_STR( data_classifier_get_name_const( classifier ) );
     123              :             /* insert linebreaks */
     124          256 :             name_err |= draw_line_breaker_append( &((*this_).linebr), &class_name, to_name );
     125          256 :             const utf8stringview_t name = utf8stream_writemem_get_view( &((*this_).text_builder) );
     126              : 
     127              :             /* determine text width and height */
     128          256 :             pango_layout_set_font_description( font_layout, pencil_size_get_title_font_description(pencil_size) );
     129          512 :             pango_layout_set_text( font_layout,
     130              :                                    utf8stringview_get_start( &name ),
     131          256 :                                    utf8stringview_get_length( &name )
     132              :                                  );
     133          256 :             pango_layout_set_width( font_layout, proposed_pango_width * PANGO_SCALE );
     134              :             /* option for nicer layout but harder to read: */
     135              :             /* pango_layout_set_wrap( font_layout, PANGO_WRAP_WORD_CHAR ); */
     136          256 :             pango_layout_get_pixel_size( font_layout, &text2_width, &text2_height );
     137          256 :             text2_height += PENCIL_SIZE_FONT_ALIGN_MARGIN;  /* allow to align font with pixel border */
     138          256 :             text2_width += PENCIL_SIZE_FONT_ALIGN_MARGIN;
     139              : 
     140              :             /* restore pango context */
     141          256 :             pango_layout_set_width(font_layout, DRAW_CLASSIFIER_PANGO_UNLIMITED_WIDTH );
     142              : 
     143              :             /* for space between stereotype and name */
     144          256 :             text2_height += pencil_size_get_font_line_gap( pencil_size );
     145              : 
     146              :             /* for underscores under object instance names, add 2 * gap: */
     147          256 :             space_for_line = 2.0 * pencil_size_get_standard_object_border( pencil_size );
     148              : 
     149              :             /* cleanup the text_builder */
     150          256 :             name_err |= utf8stream_writemem_reset( &((*this_).text_builder) );
     151          256 :             if ( name_err != U8_ERROR_NONE )
     152              :             {
     153            0 :                 U8_LOG_WARNING_HEX( "error at get_dim/draw_line_breaker_append", name_err );
     154              :             }
     155              :         }
     156              : 
     157              :         /* draw description text */
     158          256 :         int text3_width = 0;
     159          256 :         int text3_height = 0;
     160          256 :         if ( DATA_CLASSIFIER_TYPE_COMMENT == data_classifier_get_main_type ( classifier ) )
     161              :         {
     162            8 :             pango_layout_set_font_description (font_layout, pencil_size_get_standard_font_description(pencil_size) );
     163            8 :             pango_layout_set_text ( font_layout,
     164              :                                     data_classifier_get_description_const( classifier ),
     165              :                                     DRAW_CLASSIFIER_PANGO_AUTO_DETECT_LENGTH
     166              :                                   );
     167            8 :             pango_layout_get_pixel_size (font_layout, &text3_width, &text3_height);
     168            8 :             text3_height += PENCIL_SIZE_FONT_ALIGN_MARGIN;  /* allow to align font with pixel border */
     169            8 :             text3_width += PENCIL_SIZE_FONT_ALIGN_MARGIN;
     170              :         }
     171              : 
     172          256 :         *out_label_dim = (geometry_dimensions_t) {
     173          256 :             .width = u8_i32_max3( text1_width, text2_width, text3_width ),
     174          256 :             .height = text1_height + text2_height + space_for_line + text3_height
     175              :         };
     176              : 
     177              :         /* maybe try a recursnio to improce/compactify the result dimensions? */
     178          256 :         const double proposed_width = geometry_dimensions_get_width( proposed_bounds );
     179          256 :         if (( max_recursions > 0 )&&( geometry_dimensions_get_width( out_label_dim ) > proposed_width ))
     180              :         {
     181           64 :             const geometry_dimensions_t new_proposed_bounds = *out_label_dim;
     182           64 :             draw_classifier_label_get_stereotype_and_name_dimensions( this_,
     183              :                                                                       visible_classifier,
     184              :                                                                       with_stereotype,
     185              :                                                                       &new_proposed_bounds,
     186              :                                                                       max_recursions-1,
     187              :                                                                       pencil_size,
     188              :                                                                       font_layout,
     189              :                                                                       out_label_dim
     190              :                                                                     );
     191              :         }
     192              :     }
     193              :     else
     194              :     {
     195            0 :         U8_LOG_ERROR("invalid visible classifier in draw_classifier_label_get_stereotype_and_name_dimensions()");
     196            0 :         *out_label_dim = (geometry_dimensions_t) { .width = 0.0, .height = 0.0 };
     197              :     }
     198          256 :     U8_TRACE_END();
     199          256 : }
     200              : 
     201            0 : void draw_classifier_label_draw_stereotype_and_name( draw_classifier_label_t *this_,
     202              :                                                      const data_visible_classifier_t *visible_classifier,
     203              :                                                      bool with_stereotype,
     204              :                                                      const GdkRGBA *color,
     205              :                                                      const geometry_rectangle_t *label_box,
     206              :                                                      const pencil_size_t *pencil_size,
     207              :                                                      PangoLayout *font_layout,
     208              :                                                      cairo_t *cr )
     209              : {
     210            0 :     U8_TRACE_BEGIN();
     211            0 :     assert( NULL != visible_classifier );
     212            0 :     assert( NULL != color );
     213            0 :     assert( NULL != label_box );
     214            0 :     assert( NULL != pencil_size );
     215            0 :     assert( NULL != font_layout );
     216            0 :     assert( NULL != cr );
     217              : 
     218              :     /* define names for input data: */
     219              :     const data_classifier_t *const classifier
     220            0 :         = data_visible_classifier_get_classifier_const( visible_classifier );
     221              :     const data_diagramelement_t *const diagramelement
     222            0 :         = data_visible_classifier_get_diagramelement_const( visible_classifier );
     223            0 :     const data_classifier_type_t classifier_type = data_classifier_get_main_type( classifier );
     224            0 :     const data_diagramelement_flag_t display_flags = data_diagramelement_get_display_flags( diagramelement );
     225              : 
     226              :     /* get layout sizes */
     227            0 :     const double left = geometry_rectangle_get_left( label_box );
     228            0 :     const double top = geometry_rectangle_get_top( label_box );
     229            0 :     const double width = geometry_rectangle_get_width( label_box );
     230            0 :     const double f_line_gap = pencil_size_get_font_line_gap( pencil_size );
     231            0 :     const double f_size = pencil_size_get_standard_font_size( pencil_size );
     232              : 
     233              :     /* draw stereotype text */
     234            0 :     int text1_height = 0;
     235              :     {
     236            0 :         if ( with_stereotype && data_classifier_has_stereotype( classifier ) )
     237              :         {
     238              :             /* prepare text */
     239            0 :             u8_error_t name_err = U8_ERROR_NONE;
     240            0 :             utf8stream_writer_t *to_name = utf8stream_writemem_get_writer( &((*this_).text_builder) );
     241            0 :             name_err |= utf8stream_writer_write_str( to_name, DRAW_CLASSIFIER_LEFT_GUILLEMETS );
     242            0 :             utf8stringview_t stereo_name = UTF8STRINGVIEW_STR( data_classifier_get_stereotype_const( classifier ) );
     243              :             /* insert linebreaks */
     244            0 :             name_err |= draw_line_breaker_append( &((*this_).linebr), &stereo_name, to_name );
     245            0 :             name_err |= utf8stream_writer_write_str( to_name, DRAW_CLASSIFIER_RIGHT_GUILLEMETS );
     246            0 :             const utf8stringview_t stereo = utf8stream_writemem_get_view( &((*this_).text_builder) );
     247              : 
     248              :             int text1_width;
     249            0 :             cairo_set_source_rgba( cr, color->red, color->green, color->blue, color->alpha );
     250            0 :             pango_layout_set_font_description( font_layout, pencil_size_get_standard_font_description(pencil_size) );
     251            0 :             pango_layout_set_text( font_layout,
     252              :                                    utf8stringview_get_start( &stereo ),
     253            0 :                                    utf8stringview_get_length( &stereo )
     254              :                                  );
     255            0 :             pango_layout_set_width( font_layout, (width+f_size) * PANGO_SCALE );  /* add gap to avoid line breaks by rounding  errors and whitespace character widths */
     256            0 :             pango_layout_get_pixel_size (font_layout, &text1_width, &text1_height);
     257            0 :             cairo_move_to( cr,
     258            0 :                            ceil( left + 0.5*( width - text1_width ) ),
     259              :                            ceil( top )
     260              :                          );  /* align font with pixel border */
     261            0 :             pango_cairo_show_layout( cr, font_layout );
     262              : 
     263              :             /* restore pango context */
     264            0 :             pango_layout_set_width(font_layout, DRAW_CLASSIFIER_PANGO_UNLIMITED_WIDTH );
     265              : 
     266              :             /* cleanup the text_builder */
     267            0 :             name_err |= utf8stream_writemem_reset( &((*this_).text_builder) );
     268            0 :             if ( name_err != U8_ERROR_NONE )
     269              :             {
     270            0 :                 U8_LOG_WARNING_HEX( "error at draw/draw_line_breaker_append", name_err );
     271              :             }
     272              :         }
     273              :     }
     274              : 
     275              :     /* draw name text */
     276            0 :     int text2_height = 0;
     277              :     {
     278              :         /* prepare text */
     279            0 :         u8_error_t name_err = U8_ERROR_NONE;
     280            0 :         utf8stream_writer_t *to_name = utf8stream_writemem_get_writer( &((*this_).text_builder) );
     281              : 
     282            0 :         if ( 0 != ( display_flags & DATA_DIAGRAMELEMENT_FLAG_ANONYMOUS_INSTANCE ) )
     283              :         {
     284            0 :             name_err |= utf8stream_writer_write_char( to_name, DRAW_CLASSIFIER_COLON );
     285              :         }
     286            0 :         utf8stringview_t class_name = UTF8STRINGVIEW_STR( data_classifier_get_name_const( classifier ) );
     287              :         /* insert linebreaks */
     288            0 :         name_err |= draw_line_breaker_append( &((*this_).linebr), &class_name, to_name );
     289            0 :         const utf8stringview_t name = utf8stream_writemem_get_view( &((*this_).text_builder) );
     290              : 
     291              :         int text2_width;
     292            0 :         cairo_set_source_rgba( cr, color->red, color->green, color->blue, color->alpha );
     293            0 :         pango_layout_set_font_description( font_layout, pencil_size_get_title_font_description(pencil_size) );
     294            0 :         pango_layout_set_text( font_layout,
     295              :                                utf8stringview_get_start( &name ),
     296            0 :                                utf8stringview_get_length( &name )
     297              :                              );
     298            0 :         pango_layout_set_width( font_layout, (width+f_size) * PANGO_SCALE );  /* add gap to avoid line breaks by rounding errors and whitespace character widths */
     299              :         /* option for nicer layout but harder to read: */
     300              :         /* pango_layout_set_wrap( font_layout, PANGO_WRAP_WORD_CHAR ); */
     301            0 :         pango_layout_get_pixel_size( font_layout, &text2_width, &text2_height );
     302              : 
     303              :         /* draw text */
     304            0 :         cairo_move_to( cr,
     305            0 :                        ceil( left + 0.5*( width - text2_width ) ),
     306            0 :                        ceil( top + text1_height + f_line_gap )
     307              :                      );  /* align font with pixel border */
     308            0 :         pango_cairo_show_layout( cr, font_layout );
     309              : 
     310              :         /* restore pango context */
     311            0 :         pango_layout_set_width( font_layout, DRAW_CLASSIFIER_PANGO_UNLIMITED_WIDTH );
     312              : 
     313              :         /* underline instances */
     314            0 :         if ( 0 != ( display_flags & ( DATA_DIAGRAMELEMENT_FLAG_ANONYMOUS_INSTANCE | DATA_DIAGRAMELEMENT_FLAG_NAMED_INSTANCE ) ) )
     315              :         {
     316            0 :             cairo_move_to( cr, left + 0.5*( width - text2_width ), top+text1_height+f_line_gap+text2_height );
     317            0 :             cairo_line_to( cr, left + 0.5*( width + text2_width ), top+text1_height+f_line_gap+text2_height );
     318            0 :             cairo_stroke(cr);
     319              :         }
     320              : 
     321              :         /* cleanup the text_builder */
     322            0 :         name_err |= utf8stream_writemem_reset( &((*this_).text_builder) );
     323            0 :         if ( name_err != U8_ERROR_NONE )
     324              :         {
     325            0 :             U8_LOG_WARNING_HEX( "error at draw/draw_line_breaker_append", name_err );
     326              :         }
     327              :     }
     328              : 
     329              :     /* draw description text */
     330            0 :     if ( DATA_CLASSIFIER_TYPE_COMMENT == classifier_type )
     331              :     {
     332              :         int text3_width;
     333              :         int text3_height;
     334            0 :         cairo_set_source_rgba( cr, color->red, color->green, color->blue, color->alpha );
     335            0 :         pango_layout_set_font_description (font_layout, pencil_size_get_standard_font_description(pencil_size) );
     336            0 :         pango_layout_set_text ( font_layout,
     337              :                                 data_classifier_get_description_const( classifier ),
     338              :                                 DRAW_CLASSIFIER_PANGO_AUTO_DETECT_LENGTH
     339              :                               );
     340            0 :         pango_layout_get_pixel_size (font_layout, &text3_width, &text3_height);
     341              : 
     342              :         /* draw text */
     343            0 :         cairo_move_to( cr,
     344            0 :                        ceil( left + 0.5*( width - text3_width ) ),
     345            0 :                        ceil( top + text1_height + f_line_gap + text2_height + f_line_gap )
     346              :                      );  /* align font with pixel border */
     347            0 :         pango_cairo_show_layout( cr, font_layout );
     348              :     }
     349              : 
     350            0 :     U8_TRACE_END();
     351            0 : }
     352              : 
     353              : 
     354              : /*
     355              : Copyright 2016-2026 Andreas Warnke
     356              :     http://www.apache.org/licenses/LICENSE-2.0
     357              : 
     358              : Licensed under the Apache License, Version 2.0 (the "License");
     359              : you may not use this file except in compliance with the License.
     360              : You may obtain a copy of the License at
     361              : 
     362              : 
     363              : Unless required by applicable law or agreed to in writing, software
     364              : distributed under the License is distributed on an "AS IS" BASIS,
     365              : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     366              : See the License for the specific language governing permissions and
     367              : limitations under the License.
     368              : */
        

Generated by: LCOV version 2.0-1