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

Generated by: LCOV version 2.0-1