Fortran-native plotting inspired by Python's matplotlib.pyplot
and https://github.com/jacobwilliams/pyplot-fortran. This library is under active development, and the API is still subject to change. There are no external dependencies for building the library itself. For verifying PDF/PNG outputs, optional tooling is recommended (see Artifact Verification).
use fortplot
use fortplot ! Provides wp => real64 for precision
real(wp), dimension(50) :: x, y
! Generate sample data
x = [(real(i-1, wp) * 0.2_wp, i=1, 50)]
y = sin(x)
call figure()
call plot(x, y)
call title("Function Plot")
call xlabel("x")
call ylabel("y")
call xlim(0.0_wp, 10.0_wp) ! Set x-axis limits
call ylim(-1.0_wp, 1.0_wp) ! Set y-axis limits
call savefig("plot.png")
! Multiple subplots are not yet implemented.
! Calling subplot(...) currently emits a warning ("subplot: Multiple
! subplots not yet implemented") and only the first panel is
! rendered/saved. This feature is planned for a future release.
call figure(figsize=[8.0_wp, 6.0_wp])
call subplot(2, 2, 1) ! Will show warning
call plot(x, sin(x))
call title("Sine Wave")
call savefig("single_plot.png") ! Only first subplot will be saved
use fortplot ! Imports wp => real64 for precision
type(figure_t) :: fig
real(wp), dimension(50) :: x, yf
integer :: i
! Generate test data
x = [(real(i-1, wp) * 0.1_wp, i=1, 50)]
yf = sin(x)
call fig%initialize()
call fig%set_title("Function Plot")
call fig%set_xlabel("x")
call fig%set_ylabel("y")
call fig%add_plot(x, yf)
call fig%savefig("plot_oo.png")
call figure(figsize=[8.0_wp, 6.0_wp])
call add_3d_plot(x, y, z, label="3D curve")
call title("3D Line Plot")
call savefig("3d_plot.png")
call figure(figsize=[8.0_wp, 6.0_wp])
call plot(x, sin(x), label="sin(x)", linestyle="b-")
call plot(x, cos(x), label="cos(x)", linestyle="r--")
call plot(x, sin(2*x), label="sin(2x)", linestyle="g:")
call legend()
call savefig("trig_functions.pdf")
call figure(figsize=[8.0_wp, 6.0_wp])
call title("Wave Functions: \psi(\omega t) = A e^{-\lambda t} sin(\omega t)")
call xlabel("Time \tau (normalized)")
call ylabel("Amplitude \Psi (V)")
call plot(t, damped_sine, label="\alpha decay")
call plot(t, damped_cosine, label="\beta oscillation")
call legend()
call savefig("unicode_demo.png") ! Works in PNG, PDF, and ASCII
! Basic scatter plot
call figure()
call scatter(x, y, label="Data Points")
call savefig("basic_scatter.png")
! Object-oriented scatter plot
type(figure_t) :: fig
real(wp), dimension(20) :: x, y, sizes
call fig%initialize()
call fig%scatter(x, y, label='Bubble Chart')
call fig%savefig("bubble_chart.pdf")
use fortplot
implicit none
real(wp), dimension(50) :: x, y, z
integer :: i
! Generate 3D curve data
x = [(real(i-1, wp) * 0.1_wp, i=1, 50)]
y = sin(x)
z = cos(x)
! 3D surface visualization
call figure(figsize=[8.0_wp, 6.0_wp])
call add_3d_plot(x, y, z, label="3D Surface Curve")
call title("3D Surface Plot")
call savefig("surface_3d.png")
! pcolormesh for 2D heatmaps is available and working
! Array dimensions: z(ny,nx) with x(nx+1), y(ny+1) is the
! standard scientific format and does NOT emit warnings.
! C-style z(nx,ny) is also accepted; it is transposed internally.
real(wp), dimension(10, 10) :: z_data ! ny=10, nx=10
call pcolormesh(x_grid, y_grid, z_data, colormap="viridis")
call figure()
call contour_filled(x_grid, y_grid, z_data, colormap="viridis", show_colorbar=.true.)
call title("Temperature Distribution")
call xlabel("X Position")
call ylabel("Y Position")
call savefig("temperature.png")
! Basic streamplot with default arrows
call figure()
call streamplot(x_grid, y_grid, u_field, v_field)
call savefig("flow_field.png")
! Streamplot with custom arrow size and style
call streamplot(x_grid, y_grid, u_field, v_field, arrowsize=1.5_real64, arrowstyle='<->')
! Streamlines without arrows
call streamplot(x_grid, y_grid, u_field, v_field, arrowsize=0.0_real64)
use fortplot ! Imports wp => real64 automatically
implicit none
! Use functional API for errorbar plots (currently supported)
real(wp), dimension(20) :: x, y, yerr, y_theory
call figure(figsize=[8.0_wp, 6.0_wp])
call errorbar(x, y, yerr=yerr, marker='o', label='Experimental data')
call plot(x, y_theory, label='Theory', linestyle='-')
call legend()
call savefig("scientific_plot.png")
call figure()
call plot(x, y)
call set_xscale("log")
call set_yscale("symlog", threshold=0.01_wp)
call xlim(1.0e-3_wp, 1.0e3_wp)
call ylim(-100.0_wp, 100.0_wp)
call savefig("log_plot.pdf")
- Build:
make build
orfpm build
- Tests:
make test
(full) ormake test-ci
(fast subset) - Examples:
make example
(Fortran),make example_python
,make example_matplotlib
Use make verify-artifacts
to run key examples and validate generated PDF/PNG/txt artifacts with strict checks.
Dependencies for verification tools:
- Arch Linux:
sudo pacman -S poppler ghostscript
- Ubuntu/Debian:
sudo apt-get install -y poppler-utils ghostscript
- macOS (Homebrew):
brew install poppler ghostscript
In PRs and issues for output-affecting fixes, include the exact commands run, artifact paths, and short excerpts from tools (e.g., pdftotext
) as evidence.
! Text annotation system is FULLY IMPLEMENTED
use fortplot
type(figure_t) :: fig
real(wp), dimension(50) :: x, y
integer :: i
! Generate sample data
x = [(real(i-1, wp) * 0.1_wp, i=1, 50)]
y = sin(x)
call figure(figsize=[8.0_wp, 6.0_wp])
call add_plot(x, y, label="Scientific Data", linestyle="b-")
call set_title("Annotated Scientific Plot")
call set_xlabel("X Variable")
call set_ylabel("Y Variable")
! Add text annotations with different coordinate systems
call add_text_annotation("Maximum", 1.57_wp, 1.0_wp, coord_type=COORD_DATA)
call add_arrow_annotation("Peak→", 1.2_wp, 0.8_wp, 1.57_wp, 1.0_wp, coord_type=COORD_DATA)
call add_text_annotation("Title Area", 0.5_wp, 0.95_wp, coord_type=COORD_FIGURE)
call savefig("annotated_plot.png")
use fortplot_animation
type(figure_t) :: fig
type(animation_t) :: anim
integer :: status
real(wp), dimension(100) :: x_data, y_data
! Setup figure and initial plot
call figure(figsize=[8.0_wp, 6.0_wp])
call add_plot(x_data, y_data, label='animated data')
call title('Animation Demo')
! Create animation with update function
anim = FuncAnimation(update_frame, frames=100, interval=50, fig=fig)
call save_animation(anim, "animation.mp4", 24, status)
if (status /= 0) then
print *, "ERROR: Animation save failed. Check ffmpeg installation."
print *, "Windows: choco install ffmpeg"
print *, "Linux: sudo apt install ffmpeg"
end if
contains
subroutine update_frame(frame)
integer, intent(in) :: frame
! Update plot data based on frame number
call set_ydata(sin(x_data + real(frame, wp) * 0.1_wp))
end subroutine update_frame
Windows Support (Issue #189 Fixed): Binary pipe handling and path escaping now work correctly on Windows.
Cross-Platform FFmpeg Setup:
- Windows:
choco install ffmpeg
or download from ffmpeg.org - Linux:
sudo apt install ffmpeg
(Ubuntu/Debian) or equivalent - macOS:
brew install ffmpeg
File Validation: Use ffprobe -v error -show_format filename.mp4
to verify video integrity.
For more examples, see the example directory and run
make example
to build and run them.
Required:
- Modern Fortran compiler (gfortran-11 or newer)
- FPM (Fortran Package Manager) or CMake
Optional:
ffmpeg
- Required for saving animations in compressed video formats (MP4, AVI, MKV)- Windows Support: Issue #189 fixed - binary pipes and path escaping work correctly
- Cross-platform: Install via
choco install ffmpeg
(Windows),brew install ffmpeg
(macOS), or package manager (Linux) - Setup Guide: See Windows FFmpeg Setup for Windows-specific installation
- Validation: FFprobe integration for format verification
Add to your fpm.toml
:
[[dependencies]]
fortplot = { git = "https://github.com/lazy-fortran/fortplot" }
Add to your CMakeLists.txt
:
include(FetchContent)
FetchContent_Declare(
fortplot
GIT_REPOSITORY https://github.com/lazy-fortran/fortplot
GIT_TAG main
)
FetchContent_MakeAvailable(fortplot)
target_link_libraries(your_target fortplot::fortplot)
Executable Stack Protection: fortplot prevents creation of executable stack segments which could be exploited for code injection attacks.
Trampoline Detection: The build system automatically detects and prevents nested functions that generate trampolines. All library code is trampoline-free for security compliance.
# Build with trampoline detection enabled
# Requires Ninja generator (install package: ninja or ninja-build)
cmake -S . -B build -G Ninja && cmake --build build -j
# Library builds successfully = trampoline-free core code
# Verify no executable stack segments
readelf -W -l build/libfortplot.a | grep -i stack
# Should return empty (no executable stack)
# Test trampoline detection (should fail on example with nested function)
fpm build --flag "-Werror=trampolines" 2>/dev/null || echo "Trampoline detection working"
# Error confirms security validation is active
# FPM: Manual security flags (as documented in fpm.toml)
fpm build --flag "-Wtrampolines -Werror=trampolines"
# CMake: Automatic security flags (see CMakeLists.txt lines 36-47)
cmake -S . -B build -G Ninja && cmake --build build -j
# Standard development (FPM default)
make build # Uses fpm.toml configuration
Install the Python package with pip:
pip install git+https://github.com/lazy-fortran/fortplot.git
- Line plots (
plot
) with customizable line styles and markers - Error bars (
errorbar
) with symmetric/asymmetric X/Y errors and customization - 3D line plots (
add_3d_plot
) with automatic projection - 3D surface plots (
add_surface
) with automatic dimension validation - Contour plots (
contour
,contourf
) with custom levels and colormaps - Pseudocolor mesh (
pcolormesh
) with color limits and edge colors - Streamplots (
streamplot
) for vector field visualization with arrows - Enhanced scatter plots (
scatter
) with size/color mapping and multiple marker shapes - Bar charts (
bar
,barh
) - Histograms (
hist
,histogram
) - Images (
imshow
)
- PNG (raster graphics)
- PDF (vector graphics)
- ASCII (terminal display)
- Interactive display via system viewer (
show()
)
- Line styles: solid (
-
), dashed (--
), dotted (:
), dashdot (-.
) - Markers: circle (
o
), cross (x
), square (s
), diamond (D
), plus (+
), star (*
), triangle, pentagon, hexagon - Format strings (
'r-o'
,'b--'
,'g:'
) for matplotlib compatibility - Colormaps: viridis, plasma, inferno, crest, coolwarm, jet, rocket, mako, flare
- Colorbars for contour and pcolormesh plots
- Legends with automatic positioning
- Scales: linear, log, symlog (with configurable threshold)
- Axis limits (
xlim
,ylim
) - Subplot grids (
subplot
) - Not yet implemented (shows warning) - Interactive display with
show()
(GUI detection for X11, Wayland, macOS, Windows) - Animation support with
FuncAnimation
(requiresffmpeg
for video formats)- 5-Layer Validation: Comprehensive framework with size, header, semantic, and external tool checks
- False Positive Prevention: Multi-criteria validation framework
- Unicode and LaTeX-style Greek letters (
\alpha
,\beta
,\gamma
, etc.) in all backends - Security features: Executable stack protection, trampoline detection, path validation
- Text annotations (
add_text_annotation
,add_arrow_annotation
) with multi-coordinate systems and typography
Fortplot uses a modular architecture with focused modules under 1,000 lines each. The facade pattern maintains 100% backward compatibility:
! Your existing code works unchanged
use fortplot_pdf, only: pdf_context, create_pdf_canvas
type(pdf_context) :: ctx
call create_pdf_canvas(ctx, "plot.pdf", 800, 600)
See Module Architecture Guide for developer guidelines and refactoring details.
make test
# Verify heuristic procedure count budgets
make verify-complexity
# Optional: override budgets
MAX_TOTAL_PROCS=1800 MAX_PROCS_PER_FILE=50 make verify-complexity
Requires ripgrep
(rg
) to be installed and available on PATH.
# Suppress warnings for clean test output
FORTPLOT_SUPPRESS_WARNINGS=1 make test
# Force warnings even in CI environments
FORTPLOT_FORCE_WARNINGS=1 make test
See Testing Guide for complete testing documentation.
Generate HTML documentation using FORD:
make doc
The documentation is generated in build/doc/index.html
and includes:
- API reference with full procedure documentation
- Source code cross-references
- Example gallery with working code
Browse documentation: Open file:///path/to/fortplot/build/doc/index.html
- Naming: use
fix/<topic>
,feat/<topic>
,docs/<topic>
,refactor/<topic>
. - Scope: keep branches focused; one unit of change per branch.
- Lifecycle: delete branches after merge; avoid long-lived topic branches.
- Hygiene: regularly prune remote-tracking branches and local branches fully merged into
main
.
Pruning helper:
# Dry-run: see what would be pruned
make git-prune
# Apply pruning (removes merged local branches older than 30 days)
make git-prune FORCE=1
This keeps the repository navigable and avoids metadata bloat.
Mostly for the lulz and to help make Fortran great again. In addition,
there is a need for high-quality high-performance plotting directly from Fortran
with the side-effect of a higher-performance limited-feature version of matplotlib.pyplot
.
Timing comparison
time make example_matplotlib
5.13s user 0.52s system 91% cpu 6.159 total
time make example_python
1.35s user 0.17s system 97% cpu 1.562 total