fortplot_pdf.f90 Source File


Source Code

module fortplot_pdf
    !! PDF backend main interface (unified coordinates using plot area)

    use fortplot_pdf_core
    use fortplot_pdf_text
    use fortplot_pdf_drawing
    use fortplot_zlib_core, only: zlib_compress_into
    use fortplot_pdf_axes, only: draw_pdf_axes_and_labels, render_mixed_text
    use fortplot_pdf_secondary_axes, only: draw_pdf_secondary_y_axis, &
                                           draw_pdf_secondary_x_axis_top
    use fortplot_pdf_io
    use fortplot_pdf_coordinate
    use fortplot_pdf_markers

    use fortplot_context, only: plot_context, setup_canvas
    use fortplot_plot_data, only: plot_data_t
    use fortplot_latex_parser, only: process_latex_in_text
    use fortplot_margins, only: plot_margins_t, plot_area_t, calculate_plot_area
    use fortplot_constants, only: EPSILON_COMPARE, REFERENCE_DPI
    use, intrinsic :: iso_fortran_env, only: wp => real64
    use fortplot_colormap, only: colormap_value_to_color
    use fortplot_logging, only: log_error, log_info
    implicit none

    private

    public :: pdf_context, create_pdf_canvas
    public :: draw_pdf_axes_and_labels, draw_mixed_font_text
    public :: pdf_stream_writer

    type, extends(plot_context) :: pdf_context
        type(pdf_stream_writer) :: stream_writer
        type(plot_margins_t) :: margins
        type(plot_area_t) :: plot_area
        type(pdf_context_core), private :: core_ctx
        type(pdf_context_handle), private :: coord_ctx
        integer :: x_tick_count = 0
        integer :: y_tick_count = 0
        logical, private :: axes_rendered = .false.
        ! Custom tick support (set_xticks / set_yticks)
        real(wp), allocatable :: custom_xtick_positions(:)
        real(wp), allocatable :: custom_ytick_positions(:)
        character(len=50), allocatable :: custom_xtick_labels(:)
        character(len=50), allocatable :: custom_ytick_labels(:)
    contains
        procedure :: line => draw_pdf_line
        procedure :: color => set_pdf_color
        procedure :: text => draw_pdf_text_wrapper
        procedure :: draw_text_styled => draw_pdf_text_styled
        procedure :: save => write_pdf_file_facade
        procedure :: set_line_width => set_pdf_line_width
        procedure :: set_line_style => set_pdf_line_style
        procedure :: draw_marker => draw_pdf_marker_wrapper
        procedure :: set_marker_colors => set_marker_colors_wrapper
        procedure :: set_marker_colors_with_alpha => &
            set_marker_colors_with_alpha_wrapper
        procedure :: draw_arrow => draw_pdf_arrow_wrapper
        procedure :: get_ascii_output => pdf_get_ascii_output

        procedure :: get_width_scale => get_width_scale_wrapper
        procedure :: get_height_scale => get_height_scale_wrapper
        procedure :: fill_quad => fill_quad_wrapper
        procedure :: fill_heatmap => fill_heatmap_wrapper
        procedure :: extract_rgb_data => extract_rgb_data_wrapper
        procedure :: get_png_data_backend => get_png_data_wrapper
        procedure :: prepare_3d_data => prepare_3d_data_wrapper
        procedure :: render_ylabel => render_ylabel_wrapper
        procedure :: draw_axes_and_labels_backend => &
            draw_axes_and_labels_backend_wrapper
        procedure :: save_coordinates => pdf_save_coordinates
        procedure :: set_coordinates => pdf_set_coordinates
        procedure :: render_axes => render_pdf_axes_wrapper
        procedure :: draw_secondary_y_axis => pdf_draw_secondary_y_axis_wrapper
        procedure :: draw_secondary_x_axis_top => pdf_draw_secondary_x_axis_top_wrapper

        procedure, private :: update_coord_context
        procedure, private :: make_coord_context
    end type pdf_context

    interface
        module function create_pdf_canvas(width, height) result(ctx)
            integer, intent(in) :: width, height
            type(pdf_context) :: ctx
        end function create_pdf_canvas

        module subroutine draw_pdf_line(this, x1, y1, x2, y2)
            class(pdf_context), intent(inout) :: this
            real(wp), intent(in) :: x1, y1, x2, y2
        end subroutine draw_pdf_line

        module subroutine set_pdf_color(this, r, g, b)
            class(pdf_context), intent(inout) :: this
            real(wp), intent(in) :: r, g, b
        end subroutine set_pdf_color

        module subroutine set_pdf_line_width(this, width)
            class(pdf_context), intent(inout) :: this
            real(wp), intent(in) :: width
        end subroutine set_pdf_line_width

        module subroutine set_pdf_line_style(this, style)
            class(pdf_context), intent(inout) :: this
            character(len=*), intent(in) :: style
        end subroutine set_pdf_line_style

        module subroutine draw_pdf_text_wrapper(this, x, y, text)
            class(pdf_context), intent(inout) :: this
            real(wp), intent(in) :: x, y
            character(len=*), intent(in) :: text
        end subroutine draw_pdf_text_wrapper

        module subroutine draw_pdf_text_styled(this, x_pt, y_pt, text, font_size, rotation, &
                                        ha, va, bbox, color)
            class(pdf_context), intent(inout) :: this
            real(wp), intent(in) :: x_pt, y_pt
            character(len=*), intent(in) :: text
            real(wp), intent(in) :: font_size
            real(wp), intent(in) :: rotation
            character(len=*), intent(in) :: ha, va
            logical, intent(in) :: bbox
            real(wp), intent(in) :: color(3)
        end subroutine draw_pdf_text_styled

        module subroutine write_pdf_file_facade(this, filename)
            class(pdf_context), intent(inout) :: this
            character(len=*), intent(in) :: filename
        end subroutine write_pdf_file_facade

        module subroutine update_coord_context(this)
            class(pdf_context), intent(inout) :: this
        end subroutine update_coord_context

        module function make_coord_context(this) result(ctx)
            class(pdf_context), intent(in) :: this
            type(pdf_context_handle) :: ctx
        end function make_coord_context

        module subroutine draw_pdf_marker_wrapper(this, x, y, style)
            class(pdf_context), intent(inout) :: this
            real(wp), intent(in) :: x, y
            character(len=*), intent(in) :: style
        end subroutine draw_pdf_marker_wrapper

        module subroutine set_marker_colors_wrapper(this, edge_r, edge_g, edge_b, face_r, &
                                             face_g, face_b)
            class(pdf_context), intent(inout) :: this
            real(wp), intent(in) :: edge_r, edge_g, edge_b, face_r, face_g, face_b
        end subroutine set_marker_colors_wrapper

        module subroutine set_marker_colors_with_alpha_wrapper(this, edge_r, edge_g, edge_b, &
                                                         edge_alpha, &
                                                         face_r, face_g, face_b, &
                                                         face_alpha)
            class(pdf_context), intent(inout) :: this
            real(wp), intent(in) :: edge_r, edge_g, edge_b, edge_alpha
            real(wp), intent(in) :: face_r, face_g, face_b, face_alpha
        end subroutine set_marker_colors_with_alpha_wrapper

        module subroutine draw_pdf_arrow_wrapper(this, x, y, dx, dy, size, style)
            class(pdf_context), intent(inout) :: this
            real(wp), intent(in) :: x, y, dx, dy, size
            character(len=*), intent(in) :: style
        end subroutine draw_pdf_arrow_wrapper

        module function pdf_get_ascii_output(this) result(output)
            class(pdf_context), intent(in) :: this
            character(len=:), allocatable :: output
        end function pdf_get_ascii_output

        module function get_width_scale_wrapper(this) result(scale)
            class(pdf_context), intent(in) :: this
            real(wp) :: scale
        end function get_width_scale_wrapper

        module function get_height_scale_wrapper(this) result(scale)
            class(pdf_context), intent(in) :: this
            real(wp) :: scale
        end function get_height_scale_wrapper

        module subroutine fill_quad_wrapper(this, x_quad, y_quad)
            class(pdf_context), intent(inout) :: this
            real(wp), intent(in) :: x_quad(4), y_quad(4)
        end subroutine fill_quad_wrapper

        module subroutine fill_heatmap_wrapper(this, x_grid, y_grid, z_grid, z_min, z_max, colormap_name)
            class(pdf_context), intent(inout) :: this
            real(wp), contiguous, intent(in) :: x_grid(:), y_grid(:), z_grid(:, :)
            real(wp), intent(in) :: z_min, z_max
            character(len=*), intent(in), optional :: colormap_name
        end subroutine fill_heatmap_wrapper

        module subroutine extract_rgb_data_wrapper(this, width, height, rgb_data)
            class(pdf_context), intent(in) :: this
            integer, intent(in) :: width, height
            real(wp), intent(out) :: rgb_data(width, height, 3)
        end subroutine extract_rgb_data_wrapper

        module subroutine get_png_data_wrapper(this, width, height, png_data, status)
            class(pdf_context), intent(in) :: this
            integer, intent(in) :: width, height
            integer(1), allocatable, intent(out) :: png_data(:)
            integer, intent(out) :: status
        end subroutine get_png_data_wrapper

        module subroutine prepare_3d_data_wrapper(this, plots)
            class(pdf_context), intent(inout) :: this
            type(plot_data_t), intent(in) :: plots(:)
        end subroutine prepare_3d_data_wrapper

        module subroutine render_ylabel_wrapper(this, ylabel)
            class(pdf_context), intent(inout) :: this
            character(len=*), intent(in) :: ylabel
        end subroutine render_ylabel_wrapper

        module subroutine draw_axes_and_labels_backend_wrapper(this, xscale, yscale, &
                                                        symlog_threshold, &
                                                        x_min, x_max, y_min, y_max, &
                                                        title, xlabel, ylabel, &
                                                        x_date_format, y_date_format, &
                                                        z_min, z_max, has_3d_plots)
            class(pdf_context), intent(inout) :: this
            character(len=*), intent(in) :: xscale, yscale
            real(wp), intent(in) :: symlog_threshold
            real(wp), intent(in) :: x_min, x_max, y_min, y_max
            character(len=:), allocatable, intent(in), optional :: title, xlabel, ylabel
            character(len=*), intent(in), optional :: x_date_format, y_date_format
            real(wp), intent(in), optional :: z_min, z_max
            logical, intent(in) :: has_3d_plots
        end subroutine draw_axes_and_labels_backend_wrapper

        module subroutine pdf_save_coordinates(this, x_min, x_max, y_min, y_max)
            class(pdf_context), intent(in) :: this
            real(wp), intent(out) :: x_min, x_max, y_min, y_max
        end subroutine pdf_save_coordinates

        module subroutine pdf_set_coordinates(this, x_min, x_max, y_min, y_max)
            class(pdf_context), intent(inout) :: this
            real(wp), intent(in) :: x_min, x_max, y_min, y_max
        end subroutine pdf_set_coordinates

        module subroutine render_pdf_axes_wrapper(this, title_text, xlabel_text, ylabel_text)
            class(pdf_context), intent(inout) :: this
            character(len=*), intent(in), optional :: title_text, xlabel_text, ylabel_text
        end subroutine render_pdf_axes_wrapper

        module subroutine pdf_draw_secondary_y_axis_wrapper(this, yscale, symlog_threshold, &
                                                     y_min, y_max, ylabel, date_format)
            class(pdf_context), intent(inout) :: this
            character(len=*), intent(in) :: yscale
            real(wp), intent(in) :: symlog_threshold
            real(wp), intent(in) :: y_min, y_max
            character(len=:), allocatable, intent(in), optional :: ylabel
            character(len=*), intent(in), optional :: date_format
        end subroutine pdf_draw_secondary_y_axis_wrapper

        module subroutine pdf_draw_secondary_x_axis_top_wrapper(this, xscale, symlog_threshold, &
                                                         x_min, x_max, xlabel, date_format)
            class(pdf_context), intent(inout) :: this
            character(len=*), intent(in) :: xscale
            real(wp), intent(in) :: symlog_threshold
            real(wp), intent(in) :: x_min, x_max
            character(len=:), allocatable, intent(in), optional :: xlabel
            character(len=*), intent(in), optional :: date_format
        end subroutine pdf_draw_secondary_x_axis_top_wrapper
    end interface

end module fortplot_pdf