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 ! Optional single image XObject support logical :: has_image = .false. integer :: image_width = 0 integer :: image_height = 0 character(len=:), allocatable :: image_data contains procedure :: set_color => set_pdf_color procedure :: set_line_width => set_pdf_line_width procedure :: set_image => set_pdf_image 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)) ! Set both stroking (RG) and non-stroking (rg) colors. 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') 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 subroutine set_pdf_image(this, width_px, height_px, data) class(pdf_context_core), intent(inout) :: this integer, intent(in) :: width_px, height_px character(len=*), intent(in) :: data this%has_image = .true. this%image_width = width_px this%image_height = height_px this%image_data = data end subroutine set_pdf_image 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