module render implicit none type, abstract :: renderer integer::y integer::max_width contains procedure::render_proportional procedure(initialize), deferred::initialize procedure(calculate_width), deferred::text_width procedure(calculate_height), deferred::text_height procedure(calculate_visibility), deferred::is_text_visible procedure(draw_text), deferred::draw_porportional procedure(calculate_width), deferred::preformatted_width procedure(calculate_height), deferred::preformatted_height procedure(calculate_visibility), deferred::is_preformatted_visible procedure(draw_text), deferred::draw_preformatted end type renderer abstract interface subroutine initialize(self) import::renderer class(renderer)::self end subroutine initialize end interface abstract interface function calculate_width(self, text) import::renderer class(renderer)::self character(*), intent(in)::text integer::calculate_width end function calculate_width end interface abstract interface function calculate_height(self, text) import::renderer class(renderer)::self character(*), intent(in)::text integer::calculate_height end function calculate_height end interface abstract interface subroutine draw_text(self, text) import::renderer class(renderer)::self character(*), intent(in)::text end subroutine draw_text end interface abstract interface function calculate_visibility(self, text) import::renderer class(renderer)::self character(*), intent(in)::text logical::calculate_visibility end function calculate_visibility end interface contains function width_of_line(r, text, startpos, endpos) implicit none class(renderer)::r character(*), intent(in)::text integer, intent(in)::startpos, endpos integer::width_of_line integer::my_start, my_end my_end = endpos if(endpos <= 0) then my_end = len_trim(text) end if my_start = startpos if(startpos <= 0) then my_start = 1 end if if(my_end <= my_start) then width_of_line = 0 else width_of_line = r%text_width(text(my_start:my_end)) end if end function width_of_line function wrap_line(r, text, startpos) result(endpos) implicit none class(renderer)::r character(*), intent(in)::text integer, intent(in)::startpos integer::endpos integer::my_start integer::w my_start = startpos if(startpos == 0) then my_start = 1 end if endpos = len_trim(text) w = width_of_line(r, text, my_start, endpos) do while(w > r%max_width) endpos = endpos - 1 do while(text(endpos:endpos) /= ' ' .and. text(endpos:endpos) /= '-') endpos = endpos - 1 end do w = width_of_line(r, text, my_start, endpos) end do end function wrap_line subroutine render_proportional(r, text) implicit none class(renderer)::r character(*)::text integer::startpos, endpos if(len_trim(text) == 0) then if(r%is_text_visible(" ")) then call r%draw_porportional("") end if r%y = r%y + r%text_height(" ") else startpos = 1 endpos = wrap_line(r, text, startpos) do while(endpos > startpos) if(r%is_text_visible(text(startpos:endpos))) then call r%draw_porportional(text(startpos:endpos)) end if r%y = r%y + r%text_height(text(startpos:endpos)) ! Advance string positions startpos = endpos+1 do while(text(startpos:startpos) == ' ') startpos = startpos + 1 end do endpos = wrap_line(r, text, startpos) end do end if end subroutine render_proportional subroutine render_preformatted(r, text) implicit none class(renderer)::r character(*)::text if(r%is_preformatted_visible(text)) then call r%draw_preformatted(text) end if r%y = r%y + r%preformatted_height(text) end subroutine render_preformatted end module render