fortplot_svg_legend.f90 Source File


Source Code

module fortplot_svg_legend
    !! Standalone SVG legend rendering and layout

   use, intrinsic :: iso_fortran_env, only: wp => real64
   use fortplot_legend, only: legend_entry_t
   implicit none

   private
   public :: svg_render_legend_impl, svg_calc_legend_dims_impl, &
             svg_set_legend_border_impl, svg_calc_legend_pos_impl

contains

   subroutine svg_render_legend_impl(svg_content, entries, x, y, width, height)
      character(len=:), allocatable, intent(inout) :: svg_content
      type(legend_entry_t), dimension(:), intent(in) :: entries
      real(wp), intent(in) :: x, y, width, height
      integer :: i, n
      real(wp) :: entry_h, lx, ly
      character(len=1024) :: elem

      n = size(entries)
      if (n == 0) return
      entry_h = height/real(n, wp)

      write (elem, '(A,F0.3,A,F0.3,A,F0.3,A,F0.3,A)') &
         '<rect x="', x, '" y="', y, '" width="', width, &
         '" height="', height, '" fill="white" stroke="black"/>'
      svg_content = svg_content//elem//new_line('a')

      do i = 1, n
         ly = y + real(i - 1, wp)*entry_h + entry_h*0.5_wp
         lx = x + 5.0_wp
    write (elem, '(A,F0.3,A,F0.3,A,F0.3,A,F0.3,A,F0.1,A,F0.1,A,F0.1,A)') &
             '<line x1="', lx, '" y1="', ly, '" x2="', lx + 20.0_wp, &
             '" y2="', ly, '" stroke="rgb(', &
             entries(i)%color(1)*255.0_wp, ',', &
             entries(i)%color(2)*255.0_wp, ',', &
             entries(i)%color(3)*255.0_wp, ')" stroke-width="2"/>'
         svg_content = svg_content//elem//new_line('a')

         if (allocated(entries(i)%label)) then
            write (elem, '(A,F0.3,A,F0.3,A,A,A)') &
               '<text x="', lx + 25.0_wp, '" y="', ly + 4.0_wp, &
               '" font-family="sans-serif" font-size="10">', &
               trim(entries(i)%label), '</text>'
            svg_content = svg_content//elem//new_line('a')
         end if
      end do
   end subroutine svg_render_legend_impl

   subroutine svg_calc_legend_dims_impl(entries, width, height)
      type(legend_entry_t), dimension(:), intent(in) :: entries
      real(wp), intent(out) :: width, height
      integer :: n, i, max_len

      n = size(entries)
      max_len = 0
      do i = 1, n
         if (allocated(entries(i)%label)) then
            max_len = max(max_len, len_trim(entries(i)%label))
         end if
      end do
      width = 30.0_wp + real(max_len, wp)*7.0_wp
      height = real(n, wp)*18.0_wp + 10.0_wp
   end subroutine svg_calc_legend_dims_impl

   subroutine svg_set_legend_border_impl(width)
      real(wp), intent(in) :: width
      associate (w => width); end associate
   end subroutine svg_set_legend_border_impl

 subroutine svg_calc_legend_pos_impl(pa_left, pa_bottom, pa_width, pa_height, loc, x, y)
      real(wp), intent(in) :: pa_left, pa_bottom, pa_width, pa_height
      character(len=*), intent(in) :: loc
      real(wp), intent(out) :: x, y

      select case (trim(loc))
      case ('upper left')
         x = pa_left + 10.0_wp
         y = pa_bottom + 10.0_wp
      case ('upper right')
         x = pa_left + pa_width - 110.0_wp
         y = pa_bottom + 10.0_wp
      case ('lower left')
         x = pa_left + 10.0_wp
         y = pa_bottom + pa_height - 60.0_wp
      case ('lower right')
         x = pa_left + pa_width - 110.0_wp
         y = pa_bottom + pa_height - 60.0_wp
      case ('east')
         x = pa_left + pa_width + 10.0_wp
         y = pa_bottom + pa_height*0.5_wp - 30.0_wp
      case default
         x = pa_left + pa_width - 110.0_wp
         y = pa_bottom + 10.0_wp
      end select
   end subroutine svg_calc_legend_pos_impl

end module fortplot_svg_legend