fortplot_pdf_core.f90 Source File


Source Code

module fortplot_pdf_core
    !! Core PDF types and basic operations
    !! Provides fundamental PDF context and stream management
    
    use iso_fortran_env, only: wp => real64
    implicit none
    private

    ! Public types
    public :: pdf_context_core
    public :: pdf_font_t
    
    ! Public procedures
    public :: create_pdf_canvas_core
    public :: initialize_pdf_stream
    public :: finalize_pdf_stream

    ! PDF-specific constants (margins and sizes)
    real(wp), parameter, public :: PDF_MARGIN = 50.0_wp   ! Margin in points
    real(wp), parameter, public :: PDF_TICK_SIZE = 5.0_wp
    real(wp), parameter, public :: PDF_TITLE_SIZE = 14.0_wp
    real(wp), parameter, public :: PDF_LABEL_SIZE = 12.0_wp
    real(wp), parameter, public :: PDF_TICK_LABEL_SIZE = 10.0_wp
    real(wp), parameter, public :: PDF_FONT_SIZE = 10.0_wp
    
    type :: pdf_font_t
        integer :: helvetica_obj = 5
        integer :: symbol_obj = 6
    contains
        procedure :: get_helvetica_obj
        procedure :: get_symbol_obj
    end type pdf_font_t

    type :: pdf_context_core
        character(len=:), allocatable :: stream_data
        real(wp) :: width
        real(wp) :: height
        real(wp) :: current_line_width = 1.0_wp
        type(pdf_font_t) :: fonts
    contains
        procedure :: set_color => set_pdf_color
        procedure :: set_line_width => set_pdf_line_width
    end type pdf_context_core

contains

    function create_pdf_canvas_core(width, height) result(ctx)
        real(wp), intent(in) :: width, height
        type(pdf_context_core) :: ctx
        
        ctx%width = width
        ctx%height = height
        ctx%stream_data = ""
        ctx%current_line_width = 1.0_wp
        
        call initialize_pdf_stream(ctx)
    end function create_pdf_canvas_core

    subroutine initialize_pdf_stream(ctx)
        type(pdf_context_core), intent(inout) :: ctx
        
        ! Initialize stream with save state
        ctx%stream_data = ctx%stream_data // "q" // new_line('a')
        ! Set initial line width
        ctx%stream_data = ctx%stream_data // "1 w" // new_line('a')
        ! Set line join and cap styles
        ctx%stream_data = ctx%stream_data // "1 J 1 j" // new_line('a')
    end subroutine initialize_pdf_stream

    subroutine set_pdf_color(this, r, g, b)
        use, intrinsic :: ieee_arithmetic, only: ieee_is_finite
        class(pdf_context_core), intent(inout) :: this
        real(wp), intent(in) :: r, g, b
        character(len=64) :: color_cmd
        real(wp) :: safe_r, safe_g, safe_b
        
        ! Validate and clamp RGB values to prevent PDF format errors
        safe_r = r
        safe_g = g  
        safe_b = b
        
        ! Handle NaN and infinity
        if (.not. ieee_is_finite(safe_r)) safe_r = 0.0_wp
        if (.not. ieee_is_finite(safe_g)) safe_g = 0.0_wp
        if (.not. ieee_is_finite(safe_b)) safe_b = 0.0_wp
        
        ! Clamp to [0,1] range
        safe_r = max(0.0_wp, min(1.0_wp, safe_r))
        safe_g = max(0.0_wp, min(1.0_wp, safe_g))
        safe_b = max(0.0_wp, min(1.0_wp, safe_b))
        
        write(color_cmd, '(F0.3, 1X, F0.3, 1X, F0.3, " RG")') safe_r, safe_g, safe_b
        this%stream_data = this%stream_data // trim(adjustl(color_cmd)) // new_line('a')
    end subroutine set_pdf_color

    subroutine set_pdf_line_width(this, width)
        class(pdf_context_core), intent(inout) :: this
        real(wp), intent(in) :: width
        character(len=32) :: width_cmd
        
        this%current_line_width = width
        write(width_cmd, '(F0.3, " w")') width
        this%stream_data = this%stream_data // trim(adjustl(width_cmd)) // new_line('a')
    end subroutine set_pdf_line_width


    subroutine finalize_pdf_stream(ctx)
        type(pdf_context_core), intent(inout) :: ctx
        
        ! Restore graphics state
        ctx%stream_data = ctx%stream_data // "Q" // new_line('a')
    end subroutine finalize_pdf_stream

    integer function get_helvetica_obj(this) result(obj)
        class(pdf_font_t), intent(in) :: this
        obj = this%helvetica_obj
    end function get_helvetica_obj

    integer function get_symbol_obj(this) result(obj)
        class(pdf_font_t), intent(in) :: this
        obj = this%symbol_obj
    end function get_symbol_obj

end module fortplot_pdf_core